@@ -3472,20 +3472,7 @@ namespace ts {
3472
3472
}
3473
3473
3474
3474
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);
3489
3476
}
3490
3477
links.type = type;
3491
3478
}
@@ -3619,11 +3606,33 @@ namespace ts {
3619
3606
function getTypeOfInstantiatedSymbol(symbol: Symbol): Type {
3620
3607
const links = getSymbolLinks(symbol);
3621
3608
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;
3623
3617
}
3624
3618
return links.type;
3625
3619
}
3626
3620
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
+
3627
3636
function getTypeOfSymbol(symbol: Symbol): Type {
3628
3637
if (symbol.flags & SymbolFlags.Instantiated) {
3629
3638
return getTypeOfInstantiatedSymbol(symbol);
@@ -4667,33 +4676,24 @@ namespace ts {
4667
4676
* The apparent type of a type parameter is the base constraint instantiated with the type parameter
4668
4677
* as the type argument for the 'this' type.
4669
4678
*/
4670
- function getApparentTypeOfTypeParameter (type: TypeParameter ) {
4679
+ function getApparentTypeOfTypeVariable (type: TypeVariable ) {
4671
4680
if (!type.resolvedApparentType) {
4672
- let constraintType = getConstraintOfTypeParameter (type);
4681
+ let constraintType = getConstraintOfTypeVariable (type);
4673
4682
while (constraintType && constraintType.flags & TypeFlags.TypeParameter) {
4674
- constraintType = getConstraintOfTypeParameter(<TypeParameter >constraintType);
4683
+ constraintType = getConstraintOfTypeVariable(<TypeVariable >constraintType);
4675
4684
}
4676
4685
type.resolvedApparentType = getTypeWithThisArgument(constraintType || emptyObjectType, type);
4677
4686
}
4678
4687
return type.resolvedApparentType;
4679
4688
}
4680
4689
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
-
4688
4690
/**
4689
4691
* For a type parameter, return the base constraint of the type parameter. For the string, number,
4690
4692
* boolean, and symbol primitive types, return the corresponding object types. Otherwise return the
4691
4693
* type itself. Note that the apparent type of a union type is the union type itself.
4692
4694
*/
4693
4695
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;
4697
4697
return t.flags & TypeFlags.StringLike ? globalStringType :
4698
4698
t.flags & TypeFlags.NumberLike ? globalNumberType :
4699
4699
t.flags & TypeFlags.BooleanLike ? globalBooleanType :
@@ -5279,6 +5279,12 @@ namespace ts {
5279
5279
return typeParameter.constraint === noConstraintType ? undefined : typeParameter.constraint;
5280
5280
}
5281
5281
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
+
5282
5288
function getParentSymbolOfTypeParameter(typeParameter: TypeParameter): Symbol {
5283
5289
return getSymbolOfNode(getDeclarationOfKind(typeParameter.symbol, SyntaxKind.TypeParameter).parent);
5284
5290
}
@@ -5954,6 +5960,24 @@ namespace ts {
5954
5960
const type = <IndexedAccessType>createType(TypeFlags.IndexedAccess);
5955
5961
type.objectType = objectType;
5956
5962
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
+ }
5957
5981
return type;
5958
5982
}
5959
5983
@@ -6032,14 +6056,19 @@ namespace ts {
6032
6056
}
6033
6057
6034
6058
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)) {
6036
6068
if (objectType.flags & TypeFlags.Any) {
6037
6069
return objectType;
6038
6070
}
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.
6043
6072
if (accessNode) {
6044
6073
if (!isTypeAssignableTo(indexType, getIndexType(objectType))) {
6045
6074
error(accessNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(objectType));
@@ -6056,6 +6085,7 @@ namespace ts {
6056
6085
const id = objectType.id + "," + indexType.id;
6057
6086
return indexedAccessTypes[id] || (indexedAccessTypes[id] = createIndexedAccessType(objectType, indexType));
6058
6087
}
6088
+ // In the following we resolve T[K] to the type of the property in T selected by K.
6059
6089
const apparentObjectType = getApparentType(objectType);
6060
6090
if (indexType.flags & TypeFlags.Union && !(indexType.flags & TypeFlags.Primitive)) {
6061
6091
const propTypes: Type[] = [];
@@ -7243,8 +7273,7 @@ namespace ts {
7243
7273
return result;
7244
7274
}
7245
7275
}
7246
-
7247
- if (target.flags & TypeFlags.TypeParameter) {
7276
+ else if (target.flags & TypeFlags.TypeParameter) {
7248
7277
// A source type { [P in keyof T]: X } is related to a target type T if X is related to T[P].
7249
7278
if (getObjectFlags(source) & ObjectFlags.Mapped && getConstraintTypeFromMappedType(<MappedType>source) === getIndexType(target)) {
7250
7279
if (!(<MappedType>source).declaration.questionToken) {
@@ -7273,10 +7302,10 @@ namespace ts {
7273
7302
return result;
7274
7303
}
7275
7304
}
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
7277
7306
// 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);
7280
7309
if (constraint) {
7281
7310
if (result = isRelatedTo(source, getIndexType(constraint), reportErrors)) {
7282
7311
return result;
@@ -7292,6 +7321,14 @@ namespace ts {
7292
7321
return result;
7293
7322
}
7294
7323
}
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
+ }
7295
7332
}
7296
7333
7297
7334
if (source.flags & TypeFlags.TypeParameter) {
@@ -7300,6 +7337,7 @@ namespace ts {
7300
7337
const indexedAccessType = getIndexedAccessType(source, getTypeParameterFromMappedType(<MappedType>target));
7301
7338
const templateType = getTemplateTypeFromMappedType(<MappedType>target);
7302
7339
if (result = isRelatedTo(indexedAccessType, templateType, reportErrors)) {
7340
+ errorInfo = saveErrorInfo;
7303
7341
return result;
7304
7342
}
7305
7343
}
@@ -7321,6 +7359,16 @@ namespace ts {
7321
7359
}
7322
7360
}
7323
7361
}
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
+ }
7324
7372
else {
7325
7373
if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target) {
7326
7374
// We have type references to same target type, see if relationship holds for all type arguments
@@ -14990,8 +15038,8 @@ namespace ts {
14990
15038
14991
15039
function isLiteralContextualType(contextualType: Type) {
14992
15040
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);
14995
15043
// If the type parameter is constrained to the base primitive type we're checking for,
14996
15044
// consider this a literal context. For example, given a type parameter 'T extends string',
14997
15045
// this causes us to infer string literal types for T.
@@ -15826,7 +15874,7 @@ namespace ts {
15826
15874
checkSourceElement(node.type);
15827
15875
const type = <MappedType>getTypeFromMappedTypeNode(node);
15828
15876
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;
15830
15878
checkTypeAssignableTo(keyType, stringType, node.typeParameter.constraint);
15831
15879
}
15832
15880
0 commit comments