diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 10761123a6b..b752f8fa5ff 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9077,10 +9077,10 @@ namespace ts { return type; } - function createDeferredTypeReference(target: GenericType, typeArgumentNodes: ReadonlyArray, mapper?: TypeMapper, aliasSymbol?: Symbol, aliasTypeArguments?: ReadonlyArray): TypeReference { - const type = createObjectType(ObjectFlags.Reference, target.symbol); + function createDeferredTypeReference(target: GenericType, node: ArrayTypeNode | TupleTypeNode, mapper?: TypeMapper, aliasSymbol?: Symbol, aliasTypeArguments?: ReadonlyArray): DeferredTypeReference { + const type = createObjectType(ObjectFlags.Reference, target.symbol); type.target = target; - type.typeArgumentNodes = typeArgumentNodes; + type.node = node; type.mapper = mapper; type.aliasSymbol = aliasSymbol; type.aliasTypeArguments = aliasTypeArguments; @@ -9089,7 +9089,7 @@ namespace ts { function getTypeArguments(type: TypeReference): ReadonlyArray { if (!type.resolvedTypeArguments) { - const typeArguments = type.typeArgumentNodes ? map(type.typeArgumentNodes, getTypeFromTypeNode) : emptyArray; + const typeArguments = type.node ? map(type.node.kind === SyntaxKind.ArrayType ? [type.node.elementType] : type.node.elementTypes, getTypeFromTypeNode) : emptyArray; type.resolvedTypeArguments = type.mapper ? instantiateTypes(typeArguments, type.mapper) : typeArguments; } return type.resolvedTypeArguments; @@ -9598,8 +9598,8 @@ namespace ts { const target = getArrayOrTupleTargetType(node); const aliasSymbol = getAliasSymbolForTypeNode(node); const aliasTypeArguments = getTypeArgumentsForAliasSymbol(aliasSymbol); - const elementTypes = node.kind === SyntaxKind.ArrayType ? [node.elementType] : node.elementTypes; - links.resolvedType = elementTypes.length ? createDeferredTypeReference(target, elementTypes, /*mapper*/ undefined, aliasSymbol, aliasTypeArguments) : target; + links.resolvedType = node.kind === SyntaxKind.TupleType && node.elementTypes.length === 0 ? target : + createDeferredTypeReference(target, node, /*mapper*/ undefined, aliasSymbol, aliasTypeArguments); } return links.resolvedType; } @@ -11393,17 +11393,17 @@ namespace ts { return result; } - function getAnonymousTypeInstantiation(type: AnonymousType, mapper: TypeMapper) { + function getObjectTypeInstantiation(type: AnonymousType | DeferredTypeReference, mapper: TypeMapper) { const target = type.objectFlags & ObjectFlags.Instantiated ? type.target! : type; - const { symbol } = target; - const links = getSymbolLinks(symbol); + const node = type.objectFlags & ObjectFlags.Reference ? (type).node! : type.symbol.declarations[0]; + const links = getNodeLinks(node); let typeParameters = links.outerTypeParameters; if (!typeParameters) { // The first time an anonymous type is instantiated we compute and store a list of the type // parameters that are in scope (and therefore potentially referenced). For type literals that // aren't the right hand side of a generic type alias declaration we optimize by reducing the // set of type parameters to those that are possibly referenced in the literal. - let declaration = symbol.declarations[0]; + let declaration = node; if (isInJSFile(declaration)) { const paramTag = findAncestor(declaration, isJSDocParameterTag); if (paramTag) { @@ -11419,7 +11419,7 @@ namespace ts { outerTypeParameters = addRange(outerTypeParameters, templateTagParameters); } typeParameters = outerTypeParameters || emptyArray; - typeParameters = symbol.flags & SymbolFlags.TypeLiteral && !target.aliasTypeArguments ? + typeParameters = (target.objectFlags & ObjectFlags.Reference || target.symbol.flags & SymbolFlags.TypeLiteral) && !target.aliasTypeArguments ? filter(typeParameters, tp => isTypeParameterPossiblyReferenced(tp, declaration)) : typeParameters; links.outerTypeParameters = typeParameters; @@ -11432,13 +11432,14 @@ namespace ts { // We are instantiating an anonymous type that has one or more type parameters in scope. Apply the // mapper to the type parameters to produce the effective list of type arguments, and compute the // instantiation cache key from the type IDs of the type arguments. - const combinedMapper = type.objectFlags & ObjectFlags.Instantiated ? combineTypeMappers(type.mapper!, mapper) : mapper; - const typeArguments: Type[] = map(typeParameters, combinedMapper); + const typeArguments: Type[] = map(typeParameters, combineTypeMappers(type.mapper, mapper)); const id = getTypeListId(typeArguments); let result = links.instantiations!.get(id); if (!result) { const newMapper = createTypeMapper(typeParameters, typeArguments); - result = target.objectFlags & ObjectFlags.Mapped ? instantiateMappedType(target, newMapper) : instantiateAnonymousType(target, newMapper); + result = target.objectFlags & ObjectFlags.Reference ? instantiateDeferredTypeReference(type, newMapper) : + target.objectFlags & ObjectFlags.Mapped ? instantiateMappedType(target, newMapper) : + instantiateAnonymousType(target, newMapper); links.instantiations!.set(id, result); } return result; @@ -11570,6 +11571,10 @@ namespace ts { return result; } + function instantiateDeferredTypeReference(type: DeferredTypeReference, mapper: TypeMapper): TypeReference { + return createDeferredTypeReference(type.target, type.node, mapper, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper)); + } + function getConditionalTypeInstantiation(type: ConditionalType, mapper: TypeMapper): Type { const root = type.root; if (root.outerTypeParameters) { @@ -11635,17 +11640,14 @@ namespace ts { // interface, in an object type literal, or in an object literal expression, we may need // to instantiate the type because it might reference a type parameter. return couldContainTypeVariables(type) ? - getAnonymousTypeInstantiation(type, mapper) : type; + getObjectTypeInstantiation(type, mapper) : type; } if (objectFlags & ObjectFlags.Mapped) { - return getAnonymousTypeInstantiation(type, mapper); + return getObjectTypeInstantiation(type, mapper); } if (objectFlags & ObjectFlags.Reference) { - const typeArgumentNodes = (type).typeArgumentNodes; - if (typeArgumentNodes) { - const combinedMapper = combineTypeMappers((type).mapper, mapper); - return createDeferredTypeReference((type).target, typeArgumentNodes, combinedMapper, - (type).aliasSymbol, instantiateTypes((type).aliasTypeArguments, combinedMapper)); + if ((type).node) { + return getObjectTypeInstantiation(type, mapper); } const resolvedTypeArguments = (type).resolvedTypeArguments; const newTypeArguments = instantiateTypes(resolvedTypeArguments, mapper); @@ -14440,7 +14442,7 @@ namespace ts { } function isNonDeferredTypeReference(type: Type): type is TypeReference { - return !!(getObjectFlags(type) & ObjectFlags.Reference) && !(type).typeArgumentNodes; + return !!(getObjectFlags(type) & ObjectFlags.Reference) && !(type).node; } function isTypeReferenceWithGenericArguments(type: Type): boolean { @@ -22849,7 +22851,7 @@ namespace ts { * Indicates whether a declaration can be treated as a constructor in a JavaScript * file. */ - function isJSConstructor(node: Declaration | undefined): boolean { + function isJSConstructor(node: Node | undefined): boolean { if (!node || !isInJSFile(node)) { return false; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f61aecbc9c3..1b8c8df94e2 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3954,6 +3954,8 @@ namespace ts { contextFreeType?: Type; // Cached context-free type used by the first pass of inference; used when a function's return is partially contextually sensitive deferredNodes?: Map; // 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?: Map; // Instantiations of generic type alias (undefined if non-generic) } export const enum TypeFlags { @@ -4200,13 +4202,18 @@ namespace ts { */ export interface TypeReference extends ObjectType { target: GenericType; // Type reference target - resolvedTypeArguments?: ReadonlyArray; // Type reference type arguments (undefined if none) - typeArgumentNodes?: ReadonlyArray; + node?: ArrayTypeNode | TupleTypeNode; mapper?: TypeMapper; + resolvedTypeArguments?: ReadonlyArray; // Resolved ype reference type arguments /* @internal */ literalType?: TypeReference; // Clone of type with ObjectFlags.ArrayLiteral set } + export interface DeferredTypeReference extends TypeReference { + node: ArrayTypeNode | TupleTypeNode; + mapper?: TypeMapper; + } + /* @internal */ export const enum VarianceFlags { Invariant = 0, // Neither covariant nor contravariant