Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 671f7a8

Browse filesBrowse files
committed
Defer indexed access T[K] where T is generic and K is non-generic
1 parent b7e8a6d commit 671f7a8
Copy full SHA for 671f7a8

2 files changed

+70-28Lines changed: 70 additions & 28 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/compiler/checker.ts‎

Copy file name to clipboardExpand all lines: src/compiler/checker.ts
+68-26Lines changed: 68 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Collapse file

‎src/compiler/types.ts‎

Copy file name to clipboardExpand all lines: src/compiler/types.ts
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2967,6 +2967,8 @@ namespace ts {
29672967
}
29682968

29692969
export interface TypeVariable extends Type {
2970+
/* @internal */
2971+
resolvedApparentType: Type;
29702972
/* @internal */
29712973
resolvedIndexType: IndexType;
29722974
}
@@ -2979,8 +2981,6 @@ namespace ts {
29792981
/* @internal */
29802982
mapper?: TypeMapper; // Instantiation mapper
29812983
/* @internal */
2982-
resolvedApparentType: Type;
2983-
/* @internal */
29842984
isThisType?: boolean;
29852985
}
29862986

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.