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 b8f6227

Browse filesBrowse files
authored
Merge pull request microsoft#12368 from Microsoft/Port12351-3
Port microsoft#12351 to release-2.1
2 parents 30fde91 + a41746b commit b8f6227
Copy full SHA for b8f6227

7 files changed

+632-33Lines changed: 632 additions & 33 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
+97-29Lines changed: 97 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ namespace ts {
120120
const intersectionTypes = createMap<IntersectionType>();
121121
const stringLiteralTypes = createMap<LiteralType>();
122122
const numericLiteralTypes = createMap<LiteralType>();
123+
const indexedAccessTypes = createMap<IndexedAccessType>();
123124
const evolvingArrayTypes: EvolvingArrayType[] = [];
124125

125126
const unknownSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "unknown");
@@ -4665,13 +4666,22 @@ namespace ts {
46654666
return type.resolvedApparentType;
46664667
}
46674668

4669+
/**
4670+
* The apparent type of an indexed access T[K] is the type of T's string index signature, if any.
4671+
*/
4672+
function getApparentTypeOfIndexedAccess(type: IndexedAccessType) {
4673+
return getIndexTypeOfType(getApparentType(type.objectType), IndexKind.String) || type;
4674+
}
4675+
46684676
/**
46694677
* For a type parameter, return the base constraint of the type parameter. For the string, number,
46704678
* boolean, and symbol primitive types, return the corresponding object types. Otherwise return the
46714679
* type itself. Note that the apparent type of a union type is the union type itself.
46724680
*/
46734681
function getApparentType(type: Type): Type {
4674-
const t = type.flags & TypeFlags.TypeParameter ? getApparentTypeOfTypeParameter(<TypeParameter>type) : type;
4682+
const t = type.flags & TypeFlags.TypeParameter ? getApparentTypeOfTypeParameter(<TypeParameter>type) :
4683+
type.flags & TypeFlags.IndexedAccess ? getApparentTypeOfIndexedAccess(<IndexedAccessType>type) :
4684+
type;
46754685
return t.flags & TypeFlags.StringLike ? globalStringType :
46764686
t.flags & TypeFlags.NumberLike ? globalNumberType :
46774687
t.flags & TypeFlags.BooleanLike ? globalBooleanType :
@@ -5907,6 +5917,7 @@ namespace ts {
59075917

59085918
function getIndexType(type: Type): Type {
59095919
return type.flags & TypeFlags.TypeParameter ? getIndexTypeForTypeParameter(<TypeParameter>type) :
5920+
getObjectFlags(type) & ObjectFlags.Mapped ? getConstraintTypeFromMappedType(<MappedType>type) :
59105921
type.flags & TypeFlags.Any || getIndexInfoOfType(type, IndexKind.String) ? stringOrNumberType :
59115922
getIndexInfoOfType(type, IndexKind.Number) ? getUnionType([numberType, getLiteralTypeFromPropertyNames(type)]) :
59125923
getLiteralTypeFromPropertyNames(type);
@@ -5920,18 +5931,13 @@ namespace ts {
59205931
return links.resolvedType;
59215932
}
59225933

5923-
function createIndexedAccessType(objectType: Type, indexType: TypeParameter) {
5934+
function createIndexedAccessType(objectType: Type, indexType: Type) {
59245935
const type = <IndexedAccessType>createType(TypeFlags.IndexedAccess);
59255936
type.objectType = objectType;
59265937
type.indexType = indexType;
59275938
return type;
59285939
}
59295940

5930-
function getIndexedAccessTypeForTypeParameter(objectType: Type, indexType: TypeParameter) {
5931-
const indexedAccessTypes = indexType.resolvedIndexedAccessTypes || (indexType.resolvedIndexedAccessTypes = []);
5932-
return indexedAccessTypes[objectType.id] || (indexedAccessTypes[objectType.id] = createIndexedAccessType(objectType, indexType));
5933-
}
5934-
59355941
function getPropertyTypeForIndexType(objectType: Type, indexType: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode, cacheSymbol: boolean) {
59365942
const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? <ElementAccessExpression>accessNode : undefined;
59375943
const propName = indexType.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral | TypeFlags.EnumLiteral) ?
@@ -5995,13 +6001,41 @@ namespace ts {
59956001
return unknownType;
59966002
}
59976003

6004+
function getIndexedAccessForMappedType(type: MappedType, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode) {
6005+
const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? <ElementAccessExpression>accessNode : undefined;
6006+
if (accessExpression && isAssignmentTarget(accessExpression) && type.declaration.readonlyToken) {
6007+
error(accessExpression, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(type));
6008+
return unknownType;
6009+
}
6010+
const mapper = createUnaryTypeMapper(getTypeParameterFromMappedType(type), indexType);
6011+
const templateMapper = type.mapper ? combineTypeMappers(type.mapper, mapper) : mapper;
6012+
return addOptionality(instantiateType(getTemplateTypeFromMappedType(type), templateMapper), !!type.declaration.questionToken);
6013+
}
6014+
59986015
function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode) {
5999-
if (indexType.flags & TypeFlags.TypeParameter) {
6000-
if (accessNode && !isTypeAssignableTo(getConstraintOfTypeParameter(<TypeParameter>indexType) || emptyObjectType, getIndexType(objectType))) {
6001-
error(accessNode, Diagnostics.Type_0_is_not_constrained_to_keyof_1, typeToString(indexType), typeToString(objectType));
6002-
return unknownType;
6016+
if (indexType.flags & TypeFlags.TypeParameter ||
6017+
objectType.flags & TypeFlags.TypeParameter && indexType.flags & TypeFlags.Index ||
6018+
isGenericMappedType(objectType)) {
6019+
// If either the object type or the index type are type parameters, or if the object type is a mapped
6020+
// type with a generic constraint, we are performing a higher-order index access where we cannot
6021+
// meaningfully access the properties of the object type. In those cases, we first check that the
6022+
// index type is assignable to 'keyof T' for the object type.
6023+
if (accessNode) {
6024+
const keyType = indexType.flags & TypeFlags.TypeParameter ? getConstraintOfTypeParameter(<TypeParameter>indexType) || emptyObjectType : indexType;
6025+
if (!isTypeAssignableTo(keyType, getIndexType(objectType))) {
6026+
error(accessNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(objectType));
6027+
return unknownType;
6028+
}
6029+
}
6030+
// If the object type is a mapped type { [P in K]: E }, we instantiate E using a mapper that substitutes
6031+
// the index type for P. For example, for an index access { [P in K]: Box<T[P]> }[X], we construct the
6032+
// type Box<T[X]>.
6033+
if (isGenericMappedType(objectType)) {
6034+
return getIndexedAccessForMappedType(<MappedType>objectType, indexType, accessNode);
60036035
}
6004-
return getIndexedAccessTypeForTypeParameter(objectType, <TypeParameter>indexType);
6036+
// Otherwise we defer the operation by creating an indexed access type.
6037+
const id = objectType.id + "," + indexType.id;
6038+
return indexedAccessTypes[id] || (indexedAccessTypes[id] = createIndexedAccessType(objectType, indexType));
60056039
}
60066040
const apparentType = getApparentType(objectType);
60076041
if (indexType.flags & TypeFlags.Union && !(indexType.flags & TypeFlags.Primitive)) {
@@ -6034,6 +6068,9 @@ namespace ts {
60346068
type.aliasSymbol = getAliasSymbolForTypeNode(node);
60356069
type.aliasTypeArguments = getAliasTypeArgumentsForTypeNode(node);
60366070
links.resolvedType = type;
6071+
// Eagerly resolve the constraint type which forces an error if the constraint type circularly
6072+
// references itself through one or more type aliases.
6073+
getConstraintTypeFromMappedType(type);
60376074
}
60386075
return links.resolvedType;
60396076
}
@@ -7153,12 +7190,24 @@ namespace ts {
71537190
}
71547191

71557192
if (target.flags & TypeFlags.TypeParameter) {
7156-
// Given a type parameter K with a constraint keyof T, a type S is
7157-
// assignable to K if S is assignable to keyof T.
7158-
const constraint = getConstraintOfTypeParameter(<TypeParameter>target);
7159-
if (constraint && constraint.flags & TypeFlags.Index) {
7160-
if (result = isRelatedTo(source, constraint, reportErrors)) {
7161-
return result;
7193+
// A source type { [P in keyof T]: X } is related to a target type T if X is related to T[P].
7194+
if (getObjectFlags(source) & ObjectFlags.Mapped && getConstraintTypeFromMappedType(<MappedType>source) === getIndexType(target)) {
7195+
if (!(<MappedType>source).declaration.questionToken) {
7196+
const templateType = getTemplateTypeFromMappedType(<MappedType>source);
7197+
const indexedAccessType = getIndexedAccessType(target, getTypeParameterFromMappedType(<MappedType>source));
7198+
if (result = isRelatedTo(templateType, indexedAccessType, reportErrors)) {
7199+
return result;
7200+
}
7201+
}
7202+
}
7203+
else {
7204+
// Given a type parameter K with a constraint keyof T, a type S is
7205+
// assignable to K if S is assignable to keyof T.
7206+
const constraint = getConstraintOfTypeParameter(<TypeParameter>target);
7207+
if (constraint && constraint.flags & TypeFlags.Index) {
7208+
if (result = isRelatedTo(source, constraint, reportErrors)) {
7209+
return result;
7210+
}
71627211
}
71637212
}
71647213
}
@@ -7178,22 +7227,41 @@ namespace ts {
71787227
}
71797228
}
71807229
}
7230+
else if (target.flags & TypeFlags.IndexedAccess) {
7231+
// if we have indexed access types with identical index types, see if relationship holds for
7232+
// the two object types.
7233+
if (source.flags & TypeFlags.IndexedAccess && (<IndexedAccessType>source).indexType === (<IndexedAccessType>target).indexType) {
7234+
if (result = isRelatedTo((<IndexedAccessType>source).objectType, (<IndexedAccessType>target).objectType, reportErrors)) {
7235+
return result;
7236+
}
7237+
}
7238+
}
71817239

71827240
if (source.flags & TypeFlags.TypeParameter) {
7183-
let constraint = getConstraintOfTypeParameter(<TypeParameter>source);
7184-
7185-
if (!constraint || constraint.flags & TypeFlags.Any) {
7186-
constraint = emptyObjectType;
7241+
// A source type T is related to a target type { [P in keyof T]: X } if T[P] is related to X.
7242+
if (getObjectFlags(target) & ObjectFlags.Mapped && getConstraintTypeFromMappedType(<MappedType>target) === getIndexType(source)) {
7243+
const indexedAccessType = getIndexedAccessType(source, getTypeParameterFromMappedType(<MappedType>target));
7244+
const templateType = getTemplateTypeFromMappedType(<MappedType>target);
7245+
if (result = isRelatedTo(indexedAccessType, templateType, reportErrors)) {
7246+
return result;
7247+
}
71877248
}
7249+
else {
7250+
let constraint = getConstraintOfTypeParameter(<TypeParameter>source);
71887251

7189-
// The constraint may need to be further instantiated with its 'this' type.
7190-
constraint = getTypeWithThisArgument(constraint, source);
7252+
if (!constraint || constraint.flags & TypeFlags.Any) {
7253+
constraint = emptyObjectType;
7254+
}
71917255

7192-
// Report constraint errors only if the constraint is not the empty object type
7193-
const reportConstraintErrors = reportErrors && constraint !== emptyObjectType;
7194-
if (result = isRelatedTo(constraint, target, reportConstraintErrors)) {
7195-
errorInfo = saveErrorInfo;
7196-
return result;
7256+
// The constraint may need to be further instantiated with its 'this' type.
7257+
constraint = getTypeWithThisArgument(constraint, source);
7258+
7259+
// Report constraint errors only if the constraint is not the empty object type
7260+
const reportConstraintErrors = reportErrors && constraint !== emptyObjectType;
7261+
if (result = isRelatedTo(constraint, target, reportConstraintErrors)) {
7262+
errorInfo = saveErrorInfo;
7263+
return result;
7264+
}
71977265
}
71987266
}
71997267
else {
Collapse file

‎src/compiler/diagnosticMessages.json‎

Copy file name to clipboardExpand all lines: src/compiler/diagnosticMessages.json
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1731,7 +1731,7 @@
17311731
"category": "Error",
17321732
"code": 2535
17331733
},
1734-
"Type '{0}' is not constrained to 'keyof {1}'.": {
1734+
"Type '{0}' cannot be used to index type '{1}'.": {
17351735
"category": "Error",
17361736
"code": 2536
17371737
},
Collapse file

‎src/compiler/types.ts‎

Copy file name to clipboardExpand all lines: src/compiler/types.ts
+1-3Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2974,8 +2974,6 @@ namespace ts {
29742974
/* @internal */
29752975
resolvedIndexType: IndexType;
29762976
/* @internal */
2977-
resolvedIndexedAccessTypes: IndexedAccessType[];
2978-
/* @internal */
29792977
isThisType?: boolean;
29802978
}
29812979

@@ -2985,7 +2983,7 @@ namespace ts {
29852983

29862984
export interface IndexedAccessType extends Type {
29872985
objectType: Type;
2988-
indexType: TypeParameter;
2986+
indexType: Type;
29892987
}
29902988

29912989
export const enum SignatureKind {

0 commit comments

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