@@ -4667,33 +4667,24 @@ namespace ts {
4667
4667
* The apparent type of a type parameter is the base constraint instantiated with the type parameter
4668
4668
* as the type argument for the 'this' type.
4669
4669
*/
4670
- function getApparentTypeOfTypeParameter (type: TypeParameter ) {
4670
+ function getApparentTypeOfTypeVariable (type: TypeVariable ) {
4671
4671
if (!type.resolvedApparentType) {
4672
- let constraintType = getConstraintOfTypeParameter (type);
4672
+ let constraintType = getConstraintOfTypeVariable (type);
4673
4673
while (constraintType && constraintType.flags & TypeFlags.TypeParameter) {
4674
- constraintType = getConstraintOfTypeParameter(<TypeParameter >constraintType);
4674
+ constraintType = getConstraintOfTypeVariable(<TypeVariable >constraintType);
4675
4675
}
4676
4676
type.resolvedApparentType = getTypeWithThisArgument(constraintType || emptyObjectType, type);
4677
4677
}
4678
4678
return type.resolvedApparentType;
4679
4679
}
4680
4680
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
4681
/**
4689
4682
* For a type parameter, return the base constraint of the type parameter. For the string, number,
4690
4683
* boolean, and symbol primitive types, return the corresponding object types. Otherwise return the
4691
4684
* type itself. Note that the apparent type of a union type is the union type itself.
4692
4685
*/
4693
4686
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;
4687
+ const t = type.flags & TypeFlags.TypeVariable ? getApparentTypeOfTypeVariable(<TypeVariable>type) : type;
4697
4688
return t.flags & TypeFlags.StringLike ? globalStringType :
4698
4689
t.flags & TypeFlags.NumberLike ? globalNumberType :
4699
4690
t.flags & TypeFlags.BooleanLike ? globalBooleanType :
@@ -5279,6 +5270,31 @@ namespace ts {
5279
5270
return typeParameter.constraint === noConstraintType ? undefined : typeParameter.constraint;
5280
5271
}
5281
5272
5273
+ function getConstraintOfIndexedAccess(type: IndexedAccessType): Type {
5274
+ // The constraint of T[K], where T is an object, union, or intersection type,
5275
+ // is the type of the string index signature of T, if any.
5276
+ if (type.objectType.flags & TypeFlags.StructuredType) {
5277
+ return getIndexTypeOfType(type.objectType, IndexKind.String);
5278
+ }
5279
+ // The constraint of T[K], where T is a type variable, is A[K], where A is the
5280
+ // apparent type of T.
5281
+ if (type.objectType.flags & TypeFlags.TypeVariable) {
5282
+ const apparentType = getApparentTypeOfTypeVariable(<TypeVariable>type.objectType);
5283
+ if (apparentType !== emptyObjectType) {
5284
+ return isTypeOfKind((<IndexedAccessType>type).indexType, TypeFlags.StringLike) ?
5285
+ getIndexedAccessType(apparentType, (<IndexedAccessType>type).indexType) :
5286
+ getIndexTypeOfType(apparentType, IndexKind.String);
5287
+ }
5288
+ }
5289
+ return undefined;
5290
+ }
5291
+
5292
+ function getConstraintOfTypeVariable(type: TypeVariable): Type {
5293
+ return type.flags & TypeFlags.TypeParameter ? getConstraintOfTypeParameter(<TypeParameter>type) :
5294
+ type.flags & TypeFlags.IndexedAccess ? getConstraintOfIndexedAccess(<IndexedAccessType>type) :
5295
+ undefined;
5296
+ }
5297
+
5282
5298
function getParentSymbolOfTypeParameter(typeParameter: TypeParameter): Symbol {
5283
5299
return getSymbolOfNode(getDeclarationOfKind(typeParameter.symbol, SyntaxKind.TypeParameter).parent);
5284
5300
}
@@ -6032,11 +6048,16 @@ namespace ts {
6032
6048
}
6033
6049
6034
6050
function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode) {
6035
- if (maybeTypeOfKind(indexType, TypeFlags.TypeVariable | TypeFlags.Index) || isGenericMappedType(objectType)) {
6036
- // If the index type is generic or if the object type is a mapped type with a generic constraint,
6037
- // we are performing a higher-order index access where we cannot meaningfully access the properties
6038
- // of the object type. In those cases, we first check that the index type is assignable to 'keyof T'
6039
- // for the object type.
6051
+ // If the index type is generic, if the object type is generic and doesn't originate in an expression,
6052
+ // or if the object type is a mapped type with a generic constraint, we are performing a higher-order
6053
+ // index access where we cannot meaningfully access the properties of the object type. Note that for a
6054
+ // generic T and a non-generic K, we eagerly resolve T[K] if it originates in an expression. This is to
6055
+ // preserve backwards compatibility. For example, an element access 'this["foo"]' has always been resolved
6056
+ // eagerly using the constraint type of 'this' at the given location.
6057
+ if (maybeTypeOfKind(indexType, TypeFlags.TypeVariable | TypeFlags.Index) ||
6058
+ maybeTypeOfKind(objectType, TypeFlags.TypeVariable) && !(accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression) ||
6059
+ isGenericMappedType(objectType)) {
6060
+ // We first check that the index type is assignable to 'keyof T' for the object type.
6040
6061
if (accessNode) {
6041
6062
if (!isTypeAssignableTo(indexType, getIndexType(objectType))) {
6042
6063
error(accessNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(objectType));
@@ -6053,6 +6074,7 @@ namespace ts {
6053
6074
const id = objectType.id + "," + indexType.id;
6054
6075
return indexedAccessTypes[id] || (indexedAccessTypes[id] = createIndexedAccessType(objectType, indexType));
6055
6076
}
6077
+ // In the following we resolve T[K] to the type of the property in T selected by K.
6056
6078
const apparentObjectType = getApparentType(objectType);
6057
6079
if (indexType.flags & TypeFlags.Union && !(indexType.flags & TypeFlags.Primitive)) {
6058
6080
const propTypes: Type[] = [];
@@ -7240,8 +7262,7 @@ namespace ts {
7240
7262
return result;
7241
7263
}
7242
7264
}
7243
-
7244
- if (target.flags & TypeFlags.TypeParameter) {
7265
+ else if (target.flags & TypeFlags.TypeParameter) {
7245
7266
// A source type { [P in keyof T]: X } is related to a target type T if X is related to T[P].
7246
7267
if (getObjectFlags(source) & ObjectFlags.Mapped && getConstraintTypeFromMappedType(<MappedType>source) === getIndexType(target)) {
7247
7268
if (!(<MappedType>source).declaration.questionToken) {
@@ -7270,10 +7291,10 @@ namespace ts {
7270
7291
return result;
7271
7292
}
7272
7293
}
7273
- // Given a type parameter T with a constraint C, a type S is assignable to
7294
+ // Given a type variable T with a constraint C, a type S is assignable to
7274
7295
// keyof T if S is assignable to keyof C.
7275
- if ((<IndexType>target).type.flags & TypeFlags.TypeParameter ) {
7276
- const constraint = getConstraintOfTypeParameter(<TypeParameter >(<IndexType>target).type);
7296
+ if ((<IndexType>target).type.flags & TypeFlags.TypeVariable ) {
7297
+ const constraint = getConstraintOfTypeVariable(<TypeVariable >(<IndexType>target).type);
7277
7298
if (constraint) {
7278
7299
if (result = isRelatedTo(source, getIndexType(constraint), reportErrors)) {
7279
7300
return result;
@@ -7289,6 +7310,15 @@ namespace ts {
7289
7310
return result;
7290
7311
}
7291
7312
}
7313
+ // A type S is related to a type T[K] if S is related to A[K], where K is string-like and
7314
+ // A is the apparent type of S.
7315
+ const constraint = getConstraintOfIndexedAccess(<IndexedAccessType>target);
7316
+ if (constraint) {
7317
+ if (result = isRelatedTo(source, constraint, reportErrors)) {
7318
+ errorInfo = saveErrorInfo;
7319
+ return result;
7320
+ }
7321
+ }
7292
7322
}
7293
7323
7294
7324
if (source.flags & TypeFlags.TypeParameter) {
@@ -7297,6 +7327,7 @@ namespace ts {
7297
7327
const indexedAccessType = getIndexedAccessType(source, getTypeParameterFromMappedType(<MappedType>target));
7298
7328
const templateType = getTemplateTypeFromMappedType(<MappedType>target);
7299
7329
if (result = isRelatedTo(indexedAccessType, templateType, reportErrors)) {
7330
+ errorInfo = saveErrorInfo;
7300
7331
return result;
7301
7332
}
7302
7333
}
@@ -7318,6 +7349,17 @@ namespace ts {
7318
7349
}
7319
7350
}
7320
7351
}
7352
+ else if (source.flags & TypeFlags.IndexedAccess) {
7353
+ // A type S[K] is related to a type T if A[K] is related to T, where K is string-like and
7354
+ // A is the apparent type of S.
7355
+ const constraint = getConstraintOfIndexedAccess(<IndexedAccessType>source);
7356
+ if (constraint) {
7357
+ if (result = isRelatedTo(constraint, target, reportErrors)) {
7358
+ errorInfo = saveErrorInfo;
7359
+ return result;
7360
+ }
7361
+ }
7362
+ }
7321
7363
else {
7322
7364
if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target) {
7323
7365
// We have type references to same target type, see if relationship holds for all type arguments
@@ -14978,8 +15020,8 @@ namespace ts {
14978
15020
14979
15021
function isLiteralContextualType(contextualType: Type) {
14980
15022
if (contextualType) {
14981
- if (contextualType.flags & TypeFlags.TypeParameter ) {
14982
- const apparentType = getApparentTypeOfTypeParameter(<TypeParameter >contextualType);
15023
+ if (contextualType.flags & TypeFlags.TypeVariable ) {
15024
+ const apparentType = getApparentTypeOfTypeVariable(<TypeVariable >contextualType);
14983
15025
// If the type parameter is constrained to the base primitive type we're checking for,
14984
15026
// consider this a literal context. For example, given a type parameter 'T extends string',
14985
15027
// this causes us to infer string literal types for T.
@@ -15814,7 +15856,7 @@ namespace ts {
15814
15856
checkSourceElement(node.type);
15815
15857
const type = <MappedType>getTypeFromMappedTypeNode(node);
15816
15858
const constraintType = getConstraintTypeFromMappedType(type);
15817
- const keyType = constraintType.flags & TypeFlags.TypeParameter ? getApparentTypeOfTypeParameter(<TypeParameter >constraintType) : constraintType;
15859
+ const keyType = constraintType.flags & TypeFlags.TypeVariable ? getApparentTypeOfTypeVariable(<TypeVariable >constraintType) : constraintType;
15818
15860
checkTypeAssignableTo(keyType, stringType, node.typeParameter.constraint);
15819
15861
}
15820
15862
0 commit comments