@@ -3472,20 +3472,7 @@ namespace ts {
34723472 }
34733473
34743474 if (!popTypeResolution()) {
3475- if ((<VariableLikeDeclaration>symbol.valueDeclaration).type) {
3476- // Variable has type annotation that circularly references the variable itself
3477- type = unknownType;
3478- error(symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation,
3479- symbolToString(symbol));
3480- }
3481- else {
3482- // Variable has initializer that circularly references the variable itself
3483- type = anyType;
3484- if (compilerOptions.noImplicitAny) {
3485- error(symbol.valueDeclaration, Diagnostics._0_implicitly_has_type_any_because_it_does_not_have_a_type_annotation_and_is_referenced_directly_or_indirectly_in_its_own_initializer,
3486- symbolToString(symbol));
3487- }
3488- }
3475+ type = reportCircularityError(symbol);
34893476 }
34903477 links.type = type;
34913478 }
@@ -3619,11 +3606,33 @@ namespace ts {
36193606 function getTypeOfInstantiatedSymbol(symbol: Symbol): Type {
36203607 const links = getSymbolLinks(symbol);
36213608 if (!links.type) {
3622- links.type = instantiateType(getTypeOfSymbol(links.target), links.mapper);
3609+ if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
3610+ return unknownType;
3611+ }
3612+ let type = instantiateType(getTypeOfSymbol(links.target), links.mapper);
3613+ if (!popTypeResolution()) {
3614+ type = reportCircularityError(symbol);
3615+ }
3616+ links.type = type;
36233617 }
36243618 return links.type;
36253619 }
36263620
3621+ function reportCircularityError(symbol: Symbol) {
3622+ // Check if variable has type annotation that circularly references the variable itself
3623+ if ((<VariableLikeDeclaration>symbol.valueDeclaration).type) {
3624+ error(symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation,
3625+ symbolToString(symbol));
3626+ return unknownType;
3627+ }
3628+ // Otherwise variable has initializer that circularly references the variable itself
3629+ if (compilerOptions.noImplicitAny) {
3630+ error(symbol.valueDeclaration, Diagnostics._0_implicitly_has_type_any_because_it_does_not_have_a_type_annotation_and_is_referenced_directly_or_indirectly_in_its_own_initializer,
3631+ symbolToString(symbol));
3632+ }
3633+ return anyType;
3634+ }
3635+
36273636 function getTypeOfSymbol(symbol: Symbol): Type {
36283637 if (symbol.flags & SymbolFlags.Instantiated) {
36293638 return getTypeOfInstantiatedSymbol(symbol);
@@ -4667,33 +4676,24 @@ namespace ts {
46674676 * The apparent type of a type parameter is the base constraint instantiated with the type parameter
46684677 * as the type argument for the 'this' type.
46694678 */
4670- function getApparentTypeOfTypeParameter (type: TypeParameter ) {
4679+ function getApparentTypeOfTypeVariable (type: TypeVariable ) {
46714680 if (!type.resolvedApparentType) {
4672- let constraintType = getConstraintOfTypeParameter (type);
4681+ let constraintType = getConstraintOfTypeVariable (type);
46734682 while (constraintType && constraintType.flags & TypeFlags.TypeParameter) {
4674- constraintType = getConstraintOfTypeParameter(<TypeParameter >constraintType);
4683+ constraintType = getConstraintOfTypeVariable(<TypeVariable >constraintType);
46754684 }
46764685 type.resolvedApparentType = getTypeWithThisArgument(constraintType || emptyObjectType, type);
46774686 }
46784687 return type.resolvedApparentType;
46794688 }
46804689
4681- /**
4682- * The apparent type of an indexed access T[K] is the type of T's string index signature, if any.
4683- */
4684- function getApparentTypeOfIndexedAccess(type: IndexedAccessType) {
4685- return getIndexTypeOfType(getApparentType(type.objectType), IndexKind.String) || type;
4686- }
4687-
46884690 /**
46894691 * For a type parameter, return the base constraint of the type parameter. For the string, number,
46904692 * boolean, and symbol primitive types, return the corresponding object types. Otherwise return the
46914693 * type itself. Note that the apparent type of a union type is the union type itself.
46924694 */
46934695 function getApparentType(type: Type): Type {
4694- const t = type.flags & TypeFlags.TypeParameter ? getApparentTypeOfTypeParameter(<TypeParameter>type) :
4695- type.flags & TypeFlags.IndexedAccess ? getApparentTypeOfIndexedAccess(<IndexedAccessType>type) :
4696- type;
4696+ const t = type.flags & TypeFlags.TypeVariable ? getApparentTypeOfTypeVariable(<TypeVariable>type) : type;
46974697 return t.flags & TypeFlags.StringLike ? globalStringType :
46984698 t.flags & TypeFlags.NumberLike ? globalNumberType :
46994699 t.flags & TypeFlags.BooleanLike ? globalBooleanType :
@@ -5279,6 +5279,12 @@ namespace ts {
52795279 return typeParameter.constraint === noConstraintType ? undefined : typeParameter.constraint;
52805280 }
52815281
5282+ function getConstraintOfTypeVariable(type: TypeVariable): Type {
5283+ return type.flags & TypeFlags.TypeParameter ? getConstraintOfTypeParameter(<TypeParameter>type) :
5284+ type.flags & TypeFlags.IndexedAccess ? (<IndexedAccessType>type).constraint :
5285+ undefined;
5286+ }
5287+
52825288 function getParentSymbolOfTypeParameter(typeParameter: TypeParameter): Symbol {
52835289 return getSymbolOfNode(getDeclarationOfKind(typeParameter.symbol, SyntaxKind.TypeParameter).parent);
52845290 }
@@ -5954,6 +5960,24 @@ namespace ts {
59545960 const type = <IndexedAccessType>createType(TypeFlags.IndexedAccess);
59555961 type.objectType = objectType;
59565962 type.indexType = indexType;
5963+ // We eagerly compute the constraint of the indexed access type such that circularity
5964+ // errors are immediately caught and reported. For example, class C { x: this["x"] }
5965+ // becomes an error only when the constraint is eagerly computed.
5966+ if (type.objectType.flags & TypeFlags.StructuredType) {
5967+ // The constraint of T[K], where T is an object, union, or intersection type,
5968+ // is the type of the string index signature of T, if any.
5969+ type.constraint = getIndexTypeOfType(type.objectType, IndexKind.String);
5970+ }
5971+ else if (type.objectType.flags & TypeFlags.TypeVariable) {
5972+ // The constraint of T[K], where T is a type variable, is A[K], where A is the
5973+ // apparent type of T.
5974+ const apparentType = getApparentTypeOfTypeVariable(<TypeVariable>type.objectType);
5975+ if (apparentType !== emptyObjectType) {
5976+ type.constraint = isTypeOfKind((<IndexedAccessType>type).indexType, TypeFlags.StringLike) ?
5977+ getIndexedAccessType(apparentType, (<IndexedAccessType>type).indexType) :
5978+ getIndexTypeOfType(apparentType, IndexKind.String);
5979+ }
5980+ }
59575981 return type;
59585982 }
59595983
@@ -6032,14 +6056,19 @@ namespace ts {
60326056 }
60336057
60346058 function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode) {
6035- if (maybeTypeOfKind(indexType, TypeFlags.TypeVariable | TypeFlags.Index) || isGenericMappedType(objectType)) {
6059+ // If the index type is generic, if the object type is generic and doesn't originate in an expression,
6060+ // or if the object type is a mapped type with a generic constraint, we are performing a higher-order
6061+ // index access where we cannot meaningfully access the properties of the object type. Note that for a
6062+ // generic T and a non-generic K, we eagerly resolve T[K] if it originates in an expression. This is to
6063+ // preserve backwards compatibility. For example, an element access 'this["foo"]' has always been resolved
6064+ // eagerly using the constraint type of 'this' at the given location.
6065+ if (maybeTypeOfKind(indexType, TypeFlags.TypeVariable | TypeFlags.Index) ||
6066+ maybeTypeOfKind(objectType, TypeFlags.TypeVariable) && !(accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression) ||
6067+ isGenericMappedType(objectType)) {
60366068 if (objectType.flags & TypeFlags.Any) {
60376069 return objectType;
60386070 }
6039- // If the index type is generic or if the object type is a mapped type with a generic constraint,
6040- // we are performing a higher-order index access where we cannot meaningfully access the properties
6041- // of the object type. In those cases, we first check that the index type is assignable to 'keyof T'
6042- // for the object type.
6071+ // We first check that the index type is assignable to 'keyof T' for the object type.
60436072 if (accessNode) {
60446073 if (!isTypeAssignableTo(indexType, getIndexType(objectType))) {
60456074 error(accessNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(objectType));
@@ -6056,6 +6085,7 @@ namespace ts {
60566085 const id = objectType.id + "," + indexType.id;
60576086 return indexedAccessTypes[id] || (indexedAccessTypes[id] = createIndexedAccessType(objectType, indexType));
60586087 }
6088+ // In the following we resolve T[K] to the type of the property in T selected by K.
60596089 const apparentObjectType = getApparentType(objectType);
60606090 if (indexType.flags & TypeFlags.Union && !(indexType.flags & TypeFlags.Primitive)) {
60616091 const propTypes: Type[] = [];
@@ -7243,8 +7273,7 @@ namespace ts {
72437273 return result;
72447274 }
72457275 }
7246-
7247- if (target.flags & TypeFlags.TypeParameter) {
7276+ else if (target.flags & TypeFlags.TypeParameter) {
72487277 // A source type { [P in keyof T]: X } is related to a target type T if X is related to T[P].
72497278 if (getObjectFlags(source) & ObjectFlags.Mapped && getConstraintTypeFromMappedType(<MappedType>source) === getIndexType(target)) {
72507279 if (!(<MappedType>source).declaration.questionToken) {
@@ -7273,10 +7302,10 @@ namespace ts {
72737302 return result;
72747303 }
72757304 }
7276- // Given a type parameter T with a constraint C, a type S is assignable to
7305+ // Given a type variable T with a constraint C, a type S is assignable to
72777306 // keyof T if S is assignable to keyof C.
7278- if ((<IndexType>target).type.flags & TypeFlags.TypeParameter ) {
7279- const constraint = getConstraintOfTypeParameter(<TypeParameter >(<IndexType>target).type);
7307+ if ((<IndexType>target).type.flags & TypeFlags.TypeVariable ) {
7308+ const constraint = getConstraintOfTypeVariable(<TypeVariable >(<IndexType>target).type);
72807309 if (constraint) {
72817310 if (result = isRelatedTo(source, getIndexType(constraint), reportErrors)) {
72827311 return result;
@@ -7292,6 +7321,14 @@ namespace ts {
72927321 return result;
72937322 }
72947323 }
7324+ // A type S is related to a type T[K] if S is related to A[K], where K is string-like and
7325+ // A is the apparent type of S.
7326+ if ((<IndexedAccessType>target).constraint) {
7327+ if (result = isRelatedTo(source, (<IndexedAccessType>target).constraint, reportErrors)) {
7328+ errorInfo = saveErrorInfo;
7329+ return result;
7330+ }
7331+ }
72957332 }
72967333
72977334 if (source.flags & TypeFlags.TypeParameter) {
@@ -7300,6 +7337,7 @@ namespace ts {
73007337 const indexedAccessType = getIndexedAccessType(source, getTypeParameterFromMappedType(<MappedType>target));
73017338 const templateType = getTemplateTypeFromMappedType(<MappedType>target);
73027339 if (result = isRelatedTo(indexedAccessType, templateType, reportErrors)) {
7340+ errorInfo = saveErrorInfo;
73037341 return result;
73047342 }
73057343 }
@@ -7321,6 +7359,16 @@ namespace ts {
73217359 }
73227360 }
73237361 }
7362+ else if (source.flags & TypeFlags.IndexedAccess) {
7363+ // A type S[K] is related to a type T if A[K] is related to T, where K is string-like and
7364+ // A is the apparent type of S.
7365+ if ((<IndexedAccessType>source).constraint) {
7366+ if (result = isRelatedTo((<IndexedAccessType>source).constraint, target, reportErrors)) {
7367+ errorInfo = saveErrorInfo;
7368+ return result;
7369+ }
7370+ }
7371+ }
73247372 else {
73257373 if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target) {
73267374 // We have type references to same target type, see if relationship holds for all type arguments
@@ -14990,8 +15038,8 @@ namespace ts {
1499015038
1499115039 function isLiteralContextualType(contextualType: Type) {
1499215040 if (contextualType) {
14993- if (contextualType.flags & TypeFlags.TypeParameter ) {
14994- const apparentType = getApparentTypeOfTypeParameter(<TypeParameter >contextualType);
15041+ if (contextualType.flags & TypeFlags.TypeVariable ) {
15042+ const apparentType = getApparentTypeOfTypeVariable(<TypeVariable >contextualType);
1499515043 // If the type parameter is constrained to the base primitive type we're checking for,
1499615044 // consider this a literal context. For example, given a type parameter 'T extends string',
1499715045 // this causes us to infer string literal types for T.
@@ -15826,7 +15874,7 @@ namespace ts {
1582615874 checkSourceElement(node.type);
1582715875 const type = <MappedType>getTypeFromMappedTypeNode(node);
1582815876 const constraintType = getConstraintTypeFromMappedType(type);
15829- const keyType = constraintType.flags & TypeFlags.TypeParameter ? getApparentTypeOfTypeParameter(<TypeParameter >constraintType) : constraintType;
15877+ const keyType = constraintType.flags & TypeFlags.TypeVariable ? getApparentTypeOfTypeVariable(<TypeVariable >constraintType) : constraintType;
1583015878 checkTypeAssignableTo(keyType, stringType, node.typeParameter.constraint);
1583115879 }
1583215880
0 commit comments