@@ -4667,33 +4667,24 @@ namespace ts {
46674667 * The apparent type of a type parameter is the base constraint instantiated with the type parameter
46684668 * as the type argument for the 'this' type.
46694669 */
4670- function getApparentTypeOfTypeParameter (type: TypeParameter ) {
4670+ function getApparentTypeOfTypeVariable (type: TypeVariable ) {
46714671 if (!type.resolvedApparentType) {
4672- let constraintType = getConstraintOfTypeParameter (type);
4672+ let constraintType = getConstraintOfTypeVariable (type);
46734673 while (constraintType && constraintType.flags & TypeFlags.TypeParameter) {
4674- constraintType = getConstraintOfTypeParameter(<TypeParameter >constraintType);
4674+ constraintType = getConstraintOfTypeVariable(<TypeVariable >constraintType);
46754675 }
46764676 type.resolvedApparentType = getTypeWithThisArgument(constraintType || emptyObjectType, type);
46774677 }
46784678 return type.resolvedApparentType;
46794679 }
46804680
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-
46884681 /**
46894682 * For a type parameter, return the base constraint of the type parameter. For the string, number,
46904683 * boolean, and symbol primitive types, return the corresponding object types. Otherwise return the
46914684 * type itself. Note that the apparent type of a union type is the union type itself.
46924685 */
46934686 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;
46974688 return t.flags & TypeFlags.StringLike ? globalStringType :
46984689 t.flags & TypeFlags.NumberLike ? globalNumberType :
46994690 t.flags & TypeFlags.BooleanLike ? globalBooleanType :
@@ -5279,6 +5270,31 @@ namespace ts {
52795270 return typeParameter.constraint === noConstraintType ? undefined : typeParameter.constraint;
52805271 }
52815272
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+
52825298 function getParentSymbolOfTypeParameter(typeParameter: TypeParameter): Symbol {
52835299 return getSymbolOfNode(getDeclarationOfKind(typeParameter.symbol, SyntaxKind.TypeParameter).parent);
52845300 }
@@ -6032,11 +6048,16 @@ namespace ts {
60326048 }
60336049
60346050 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.
60406061 if (accessNode) {
60416062 if (!isTypeAssignableTo(indexType, getIndexType(objectType))) {
60426063 error(accessNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(objectType));
@@ -6053,6 +6074,7 @@ namespace ts {
60536074 const id = objectType.id + "," + indexType.id;
60546075 return indexedAccessTypes[id] || (indexedAccessTypes[id] = createIndexedAccessType(objectType, indexType));
60556076 }
6077+ // In the following we resolve T[K] to the type of the property in T selected by K.
60566078 const apparentObjectType = getApparentType(objectType);
60576079 if (indexType.flags & TypeFlags.Union && !(indexType.flags & TypeFlags.Primitive)) {
60586080 const propTypes: Type[] = [];
@@ -7240,8 +7262,7 @@ namespace ts {
72407262 return result;
72417263 }
72427264 }
7243-
7244- if (target.flags & TypeFlags.TypeParameter) {
7265+ else if (target.flags & TypeFlags.TypeParameter) {
72457266 // A source type { [P in keyof T]: X } is related to a target type T if X is related to T[P].
72467267 if (getObjectFlags(source) & ObjectFlags.Mapped && getConstraintTypeFromMappedType(<MappedType>source) === getIndexType(target)) {
72477268 if (!(<MappedType>source).declaration.questionToken) {
@@ -7270,10 +7291,10 @@ namespace ts {
72707291 return result;
72717292 }
72727293 }
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
72747295 // 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);
72777298 if (constraint) {
72787299 if (result = isRelatedTo(source, getIndexType(constraint), reportErrors)) {
72797300 return result;
@@ -7289,6 +7310,15 @@ namespace ts {
72897310 return result;
72907311 }
72917312 }
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+ }
72927322 }
72937323
72947324 if (source.flags & TypeFlags.TypeParameter) {
@@ -7297,6 +7327,7 @@ namespace ts {
72977327 const indexedAccessType = getIndexedAccessType(source, getTypeParameterFromMappedType(<MappedType>target));
72987328 const templateType = getTemplateTypeFromMappedType(<MappedType>target);
72997329 if (result = isRelatedTo(indexedAccessType, templateType, reportErrors)) {
7330+ errorInfo = saveErrorInfo;
73007331 return result;
73017332 }
73027333 }
@@ -7318,6 +7349,17 @@ namespace ts {
73187349 }
73197350 }
73207351 }
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+ }
73217363 else {
73227364 if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target) {
73237365 // We have type references to same target type, see if relationship holds for all type arguments
@@ -14978,8 +15020,8 @@ namespace ts {
1497815020
1497915021 function isLiteralContextualType(contextualType: Type) {
1498015022 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);
1498315025 // If the type parameter is constrained to the base primitive type we're checking for,
1498415026 // consider this a literal context. For example, given a type parameter 'T extends string',
1498515027 // this causes us to infer string literal types for T.
@@ -15814,7 +15856,7 @@ namespace ts {
1581415856 checkSourceElement(node.type);
1581515857 const type = <MappedType>getTypeFromMappedTypeNode(node);
1581615858 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;
1581815860 checkTypeAssignableTo(keyType, stringType, node.typeParameter.constraint);
1581915861 }
1582015862
0 commit comments