diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 65cbfed68a6..6690fa352cf 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -15046,9 +15046,10 @@ namespace ts { } function getObjectTypeInstantiation(type: AnonymousType | DeferredTypeReference, mapper: TypeMapper) { - const target = type.objectFlags & ObjectFlags.Instantiated ? type.target! : type; const declaration = type.objectFlags & ObjectFlags.Reference ? (type).node! : type.symbol.declarations[0]; const links = getNodeLinks(declaration); + const target = type.objectFlags & ObjectFlags.Reference ? links.resolvedType! : + type.objectFlags & ObjectFlags.Instantiated ? type.target! : type; let typeParameters = links.outerTypeParameters; if (!typeParameters) { // The first time an anonymous type is instantiated we compute and store a list of the type @@ -15065,10 +15066,6 @@ namespace ts { filter(typeParameters, tp => isTypeParameterPossiblyReferenced(tp, declaration)) : typeParameters; links.outerTypeParameters = typeParameters; - if (typeParameters.length) { - links.instantiations = new Map(); - links.instantiations.set(getTypeListId(typeParameters), target); - } } if (typeParameters.length) { // We are instantiating an anonymous type that has one or more type parameters in scope. Apply the @@ -15077,13 +15074,17 @@ namespace ts { const combinedMapper = combineTypeMappers(type.mapper, mapper); const typeArguments = map(typeParameters, t => getMappedType(t, combinedMapper)); const id = getTypeListId(typeArguments); - let result = links.instantiations!.get(id); + if (!target.instantiations) { + target.instantiations = new Map(); + target.instantiations.set(getTypeListId(typeParameters), target); + } + let result = target.instantiations.get(id); if (!result) { const newMapper = createTypeMapper(typeParameters, typeArguments); result = target.objectFlags & ObjectFlags.Reference ? createDeferredTypeReference((type).target, (type).node, newMapper) : target.objectFlags & ObjectFlags.Mapped ? instantiateMappedType(target, newMapper) : instantiateAnonymousType(target, newMapper); - links.instantiations!.set(id, result); + target.instantiations.set(id, result); } return result; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 0ce9c2afa75..2bff3ca4ae3 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4847,7 +4847,6 @@ namespace ts { deferredNodes?: ESMap; // Set of nodes whose checking has been deferred capturedBlockScopeBindings?: Symbol[]; // Block-scoped bindings captured beneath this part of an IterationStatement outerTypeParameters?: TypeParameter[]; // Outer type parameters of anonymous object type - instantiations?: ESMap; // Instantiations of generic type alias (undefined if non-generic) isExhaustive?: boolean; // Is node an exhaustive switch statement skipDirectInference?: true; // Flag set by the API `getContextualType` call on a node when `Completions` is passed to force the checker to skip making inferences to a node's type declarationRequiresScopeChange?: boolean; // Set by `useOuterVariableScopeInParameter` in checker when downlevel emit would change the name resolution scope inside of a parameter. @@ -5141,6 +5140,8 @@ namespace ts { node: TypeReferenceNode | ArrayTypeNode | TupleTypeNode; /* @internal */ mapper?: TypeMapper; + /* @internal */ + instantiations?: ESMap; // Instantiations of generic type alias (undefined if non-generic) } /* @internal */ @@ -5221,6 +5222,7 @@ namespace ts { export interface AnonymousType extends ObjectType { target?: AnonymousType; // Instantiation target mapper?: TypeMapper; // Instantiation mapper + instantiations?: ESMap; // Instantiations of generic type alias (undefined if non-generic) } /* @internal */ diff --git a/tests/baselines/reference/objectInstantiationFromUnionSpread.js b/tests/baselines/reference/objectInstantiationFromUnionSpread.js new file mode 100644 index 00000000000..2339e0fe840 --- /dev/null +++ b/tests/baselines/reference/objectInstantiationFromUnionSpread.js @@ -0,0 +1,41 @@ +//// [objectInstantiationFromUnionSpread.ts] +// #40995 + +interface Success { + isSuccess: true; +} + +interface Fail { + isSuccess: false; +} + +type Item = Success | Fail; + +function f1(a: Item[]) { + a.map(item => ({ ...item })).filter(value => {}); +} + +function f2(a: Item[]) { + a.map(item => ({ ...item })).filter(value => {}); +} + + +//// [objectInstantiationFromUnionSpread.js] +// #40995 +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +function f1(a) { + a.map(function (item) { return (__assign({}, item)); }).filter(function (value) { }); +} +function f2(a) { + a.map(function (item) { return (__assign({}, item)); }).filter(function (value) { }); +} diff --git a/tests/baselines/reference/objectInstantiationFromUnionSpread.symbols b/tests/baselines/reference/objectInstantiationFromUnionSpread.symbols new file mode 100644 index 00000000000..53127700e01 --- /dev/null +++ b/tests/baselines/reference/objectInstantiationFromUnionSpread.symbols @@ -0,0 +1,55 @@ +=== tests/cases/compiler/objectInstantiationFromUnionSpread.ts === +// #40995 + +interface Success { +>Success : Symbol(Success, Decl(objectInstantiationFromUnionSpread.ts, 0, 0)) + + isSuccess: true; +>isSuccess : Symbol(Success.isSuccess, Decl(objectInstantiationFromUnionSpread.ts, 2, 19)) +} + +interface Fail { +>Fail : Symbol(Fail, Decl(objectInstantiationFromUnionSpread.ts, 4, 1)) + + isSuccess: false; +>isSuccess : Symbol(Fail.isSuccess, Decl(objectInstantiationFromUnionSpread.ts, 6, 16)) +} + +type Item = Success | Fail; +>Item : Symbol(Item, Decl(objectInstantiationFromUnionSpread.ts, 8, 1)) +>Success : Symbol(Success, Decl(objectInstantiationFromUnionSpread.ts, 0, 0)) +>Fail : Symbol(Fail, Decl(objectInstantiationFromUnionSpread.ts, 4, 1)) + +function f1(a: Item[]) { +>f1 : Symbol(f1, Decl(objectInstantiationFromUnionSpread.ts, 10, 27)) +>a : Symbol(a, Decl(objectInstantiationFromUnionSpread.ts, 12, 12)) +>Item : Symbol(Item, Decl(objectInstantiationFromUnionSpread.ts, 8, 1)) + + a.map(item => ({ ...item })).filter(value => {}); +>a.map(item => ({ ...item })).filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>a.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>a : Symbol(a, Decl(objectInstantiationFromUnionSpread.ts, 12, 12)) +>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>item : Symbol(item, Decl(objectInstantiationFromUnionSpread.ts, 13, 8)) +>item : Symbol(item, Decl(objectInstantiationFromUnionSpread.ts, 13, 8)) +>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>value : Symbol(value, Decl(objectInstantiationFromUnionSpread.ts, 13, 38)) +} + +function f2(a: Item[]) { +>f2 : Symbol(f2, Decl(objectInstantiationFromUnionSpread.ts, 14, 1)) +>T : Symbol(T, Decl(objectInstantiationFromUnionSpread.ts, 16, 12)) +>a : Symbol(a, Decl(objectInstantiationFromUnionSpread.ts, 16, 15)) +>Item : Symbol(Item, Decl(objectInstantiationFromUnionSpread.ts, 8, 1)) + + a.map(item => ({ ...item })).filter(value => {}); +>a.map(item => ({ ...item })).filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>a.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>a : Symbol(a, Decl(objectInstantiationFromUnionSpread.ts, 16, 15)) +>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>item : Symbol(item, Decl(objectInstantiationFromUnionSpread.ts, 17, 8)) +>item : Symbol(item, Decl(objectInstantiationFromUnionSpread.ts, 17, 8)) +>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>value : Symbol(value, Decl(objectInstantiationFromUnionSpread.ts, 17, 38)) +} + diff --git a/tests/baselines/reference/objectInstantiationFromUnionSpread.types b/tests/baselines/reference/objectInstantiationFromUnionSpread.types new file mode 100644 index 00000000000..3061622551c --- /dev/null +++ b/tests/baselines/reference/objectInstantiationFromUnionSpread.types @@ -0,0 +1,60 @@ +=== tests/cases/compiler/objectInstantiationFromUnionSpread.ts === +// #40995 + +interface Success { + isSuccess: true; +>isSuccess : true +>true : true +} + +interface Fail { + isSuccess: false; +>isSuccess : false +>false : false +} + +type Item = Success | Fail; +>Item : Item + +function f1(a: Item[]) { +>f1 : (a: Item[]) => void +>a : Item[] + + a.map(item => ({ ...item })).filter(value => {}); +>a.map(item => ({ ...item })).filter(value => {}) : ({ isSuccess: true; } | { isSuccess: false; })[] +>a.map(item => ({ ...item })).filter : { (predicate: (value: { isSuccess: true; } | { isSuccess: false; }, index: number, array: ({ isSuccess: true; } | { isSuccess: false; })[]) => value is S, thisArg?: any): S[]; (predicate: (value: { isSuccess: true; } | { isSuccess: false; }, index: number, array: ({ isSuccess: true; } | { isSuccess: false; })[]) => unknown, thisArg?: any): ({ isSuccess: true; } | { isSuccess: false; })[]; } +>a.map(item => ({ ...item })) : ({ isSuccess: true; } | { isSuccess: false; })[] +>a.map : (callbackfn: (value: Item, index: number, array: Item[]) => U, thisArg?: any) => U[] +>a : Item[] +>map : (callbackfn: (value: Item, index: number, array: Item[]) => U, thisArg?: any) => U[] +>item => ({ ...item }) : (item: Item) => { isSuccess: true; } | { isSuccess: false; } +>item : Item +>({ ...item }) : { isSuccess: true; } | { isSuccess: false; } +>{ ...item } : { isSuccess: true; } | { isSuccess: false; } +>item : Item +>filter : { (predicate: (value: { isSuccess: true; } | { isSuccess: false; }, index: number, array: ({ isSuccess: true; } | { isSuccess: false; })[]) => value is S, thisArg?: any): S[]; (predicate: (value: { isSuccess: true; } | { isSuccess: false; }, index: number, array: ({ isSuccess: true; } | { isSuccess: false; })[]) => unknown, thisArg?: any): ({ isSuccess: true; } | { isSuccess: false; })[]; } +>value => {} : (value: { isSuccess: true; } | { isSuccess: false; }) => void +>value : { isSuccess: true; } | { isSuccess: false; } +} + +function f2(a: Item[]) { +>f2 : (a: Item[]) => void +>a : Item[] + + a.map(item => ({ ...item })).filter(value => {}); +>a.map(item => ({ ...item })).filter(value => {}) : ({ isSuccess: true; } | { isSuccess: false; })[] +>a.map(item => ({ ...item })).filter : { (predicate: (value: { isSuccess: true; } | { isSuccess: false; }, index: number, array: ({ isSuccess: true; } | { isSuccess: false; })[]) => value is S, thisArg?: any): S[]; (predicate: (value: { isSuccess: true; } | { isSuccess: false; }, index: number, array: ({ isSuccess: true; } | { isSuccess: false; })[]) => unknown, thisArg?: any): ({ isSuccess: true; } | { isSuccess: false; })[]; } +>a.map(item => ({ ...item })) : ({ isSuccess: true; } | { isSuccess: false; })[] +>a.map : (callbackfn: (value: Item, index: number, array: Item[]) => U, thisArg?: any) => U[] +>a : Item[] +>map : (callbackfn: (value: Item, index: number, array: Item[]) => U, thisArg?: any) => U[] +>item => ({ ...item }) : (item: Item) => { isSuccess: true; } | { isSuccess: false; } +>item : Item +>({ ...item }) : { isSuccess: true; } | { isSuccess: false; } +>{ ...item } : { isSuccess: true; } | { isSuccess: false; } +>item : Item +>filter : { (predicate: (value: { isSuccess: true; } | { isSuccess: false; }, index: number, array: ({ isSuccess: true; } | { isSuccess: false; })[]) => value is S, thisArg?: any): S[]; (predicate: (value: { isSuccess: true; } | { isSuccess: false; }, index: number, array: ({ isSuccess: true; } | { isSuccess: false; })[]) => unknown, thisArg?: any): ({ isSuccess: true; } | { isSuccess: false; })[]; } +>value => {} : (value: { isSuccess: true; } | { isSuccess: false; }) => void +>value : { isSuccess: true; } | { isSuccess: false; } +} + diff --git a/tests/cases/compiler/objectInstantiationFromUnionSpread.ts b/tests/cases/compiler/objectInstantiationFromUnionSpread.ts new file mode 100644 index 00000000000..7e5c49bfff3 --- /dev/null +++ b/tests/cases/compiler/objectInstantiationFromUnionSpread.ts @@ -0,0 +1,21 @@ +// @strictFunctionTypes: true + +// #40995 + +interface Success { + isSuccess: true; +} + +interface Fail { + isSuccess: false; +} + +type Item = Success | Fail; + +function f1(a: Item[]) { + a.map(item => ({ ...item })).filter(value => {}); +} + +function f2(a: Item[]) { + a.map(item => ({ ...item })).filter(value => {}); +}