Skip to content

Commit 95c8a92

Browse files
committed
Add getIndexedAccessOrUndefined function
1 parent 5de3ee8 commit 95c8a92

File tree

2 files changed

+51
-40
lines changed

2 files changed

+51
-40
lines changed

src/compiler/checker.ts

Lines changed: 47 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -7195,8 +7195,7 @@ namespace ts {
71957195
return type;
71967196
}
71977197
if (type.flags & TypeFlags.Index) {
7198-
const keys = getIndexType(getApparentType((<IndexType>type).type));
7199-
return isIndexType ? filterType(keys, t => !!(t.flags & TypeFlags.Literal)) : keys;
7198+
return getIndexType(getApparentType((<IndexType>type).type));
72007199
}
72017200
if (type.flags & TypeFlags.Conditional) {
72027201
if ((<ConditionalType>type).root.isDistributive) {
@@ -7491,8 +7490,8 @@ namespace ts {
74917490
function getConstraintFromIndexedAccess(type: IndexedAccessType) {
74927491
const objectType = getConstraintOfType(type.objectType) || type.objectType;
74937492
if (objectType !== type.objectType) {
7494-
const constraint = getIndexedAccessType(objectType, type.indexType, /*accessNode*/ undefined, errorType);
7495-
if (constraint && constraint !== errorType) {
7493+
const constraint = getIndexedAccessTypeOrUndefined(objectType, type.indexType);
7494+
if (constraint) {
74967495
return constraint;
74977496
}
74987497
}
@@ -7686,8 +7685,8 @@ namespace ts {
76867685
if (t.flags & TypeFlags.IndexedAccess) {
76877686
const baseObjectType = getBaseConstraint((<IndexedAccessType>t).objectType);
76887687
const baseIndexType = getBaseConstraint((<IndexedAccessType>t).indexType);
7689-
const baseIndexedAccess = baseObjectType && baseIndexType ? getIndexedAccessType(baseObjectType, baseIndexType, /*accessNode*/ undefined, errorType) : undefined;
7690-
return baseIndexedAccess && baseIndexedAccess !== errorType ? getBaseConstraint(baseIndexedAccess) : undefined;
7688+
const baseIndexedAccess = baseObjectType && baseIndexType && getIndexedAccessTypeOrUndefined(baseObjectType, baseIndexType);
7689+
return baseIndexedAccess && getBaseConstraint(baseIndexedAccess);
76917690
}
76927691
if (t.flags & TypeFlags.Conditional) {
76937692
const constraint = getConstraintFromConditionalType(<ConditionalType>t);
@@ -9875,7 +9874,7 @@ namespace ts {
98759874
return false;
98769875
}
98779876

9878-
function getPropertyTypeForIndexType(originalObjectType: Type, objectType: Type, indexType: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression | undefined, cacheSymbol: boolean, missingType: Type) {
9877+
function getPropertyTypeForIndexType(originalObjectType: Type, objectType: Type, indexType: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression | undefined, cacheSymbol: boolean) {
98799878
const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? accessNode : undefined;
98809879
const propName = isTypeUsableAsPropertyName(indexType) ?
98819880
getPropertyNameFromType(indexType) :
@@ -9892,7 +9891,7 @@ namespace ts {
98929891
markPropertyAsReferenced(prop, accessExpression, /*isThisAccess*/ accessExpression.expression.kind === SyntaxKind.ThisKeyword);
98939892
if (isAssignmentTarget(accessExpression) && (isReferenceToReadonlyEntity(accessExpression, prop) || isReferenceThroughNamespaceImport(accessExpression))) {
98949893
error(accessExpression.argumentExpression, Diagnostics.Cannot_assign_to_0_because_it_is_a_read_only_property, symbolToString(prop));
9895-
return missingType;
9894+
return undefined;
98969895
}
98979896
if (cacheSymbol) {
98989897
getNodeLinks(accessNode!).resolvedSymbol = prop;
@@ -9921,16 +9920,19 @@ namespace ts {
99219920
if (objectType.flags & (TypeFlags.Any | TypeFlags.Never)) {
99229921
return objectType;
99239922
}
9924-
const indexInfo = accessExpression && isAssignmentTarget(accessExpression) && isGenericObjectType(originalObjectType) ? undefined :
9925-
isTypeAssignableToKind(indexType, TypeFlags.NumberLike) && getIndexInfoOfType(objectType, IndexKind.Number) ||
9926-
getIndexInfoOfType(objectType, IndexKind.String) ||
9927-
undefined;
9923+
const isAssignment = accessExpression && (isAssignmentTarget(accessExpression) || isDeleteTarget(accessExpression));
9924+
if (isAssignment && maybeTypeOfKind(originalObjectType, TypeFlags.Instantiable)) {
9925+
error(accessExpression, Diagnostics.Type_0_cannot_be_indexed_by_type_1, typeToString(originalObjectType), typeToString(indexType));
9926+
return undefined;
9927+
}
9928+
const indexInfo = isTypeAssignableToKind(indexType, TypeFlags.NumberLike) && getIndexInfoOfType(objectType, IndexKind.Number) ||
9929+
getIndexInfoOfType(objectType, IndexKind.String);
99289930
if (indexInfo) {
99299931
if (accessNode && !isTypeAssignableToKind(indexType, TypeFlags.String | TypeFlags.Number)) {
99309932
const indexNode = getIndexNodeForAccessExpression(accessNode);
99319933
error(indexNode, Diagnostics.Type_0_cannot_be_used_as_an_index_type, typeToString(indexType));
99329934
}
9933-
else if (accessExpression && indexInfo.isReadonly && (isAssignmentTarget(accessExpression) || isDeleteTarget(accessExpression))) {
9935+
else if (isAssignment && indexInfo.isReadonly) {
99349936
error(accessExpression, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(objectType));
99359937
}
99369938
return indexInfo.type;
@@ -9964,7 +9966,7 @@ namespace ts {
99649966
}
99659967
}
99669968
}
9967-
return missingType;
9969+
return undefined;
99689970
}
99699971
}
99709972
if (isJSLiteralType(objectType)) {
@@ -9985,7 +9987,7 @@ namespace ts {
99859987
if (isTypeAny(indexType)) {
99869988
return indexType;
99879989
}
9988-
return missingType;
9990+
return undefined;
99899991
}
99909992

99919993
function getIndexNodeForAccessExpression(accessNode: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression) {
@@ -10076,7 +10078,11 @@ namespace ts {
1007610078
return instantiateType(getTemplateTypeFromMappedType(objectType), templateMapper);
1007710079
}
1007810080

10079-
function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression, missingType = accessNode ? errorType : unknownType): Type {
10081+
function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression, writing?: boolean): Type {
10082+
return getIndexedAccessTypeOrUndefined(objectType, indexType, accessNode, writing) || (accessNode ? errorType : unknownType);
10083+
}
10084+
10085+
function getIndexedAccessTypeOrUndefined(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression, writing?: boolean): Type | undefined {
1008010086
if (objectType === wildcardType || indexType === wildcardType) {
1008110087
return wildcardType;
1008210088
}
@@ -10105,25 +10111,25 @@ namespace ts {
1010510111
const propTypes: Type[] = [];
1010610112
let wasMissingProp = false;
1010710113
for (const t of (<UnionType>indexType).types) {
10108-
const propType = getPropertyTypeForIndexType(objectType, apparentObjectType, t, accessNode, /*cacheSymbol*/ false, missingType);
10109-
if (propType === missingType) {
10110-
if (!accessNode) {
10111-
// If there's no error node, we can immeditely stop, since error reporting is off
10112-
return missingType;
10113-
}
10114-
else {
10115-
// Otherwise we set a flag and return at the end of the loop so we still mark all errors
10116-
wasMissingProp = true;
10117-
}
10114+
const propType = getPropertyTypeForIndexType(objectType, apparentObjectType, t, accessNode, /*cacheSymbol*/ false);
10115+
if (propType) {
10116+
propTypes.push(propType);
10117+
}
10118+
else if (!accessNode) {
10119+
// If there's no error node, we can immeditely stop, since error reporting is off
10120+
return undefined;
10121+
}
10122+
else {
10123+
// Otherwise we set a flag and return at the end of the loop so we still mark all errors
10124+
wasMissingProp = true;
1011810125
}
10119-
propTypes.push(propType);
1012010126
}
1012110127
if (wasMissingProp) {
10122-
return missingType;
10128+
return undefined;
1012310129
}
10124-
return !accessNode || getAssignmentTargetKind(accessNode) === AssignmentKind.None ? getUnionType(propTypes) : getIntersectionType(propTypes);
10130+
return writing ? getIntersectionType(propTypes) : getUnionType(propTypes);
1012510131
}
10126-
return getPropertyTypeForIndexType(objectType, apparentObjectType, indexType, accessNode, /*cacheSymbol*/ true, missingType);
10132+
return getPropertyTypeForIndexType(objectType, apparentObjectType, indexType, accessNode, /*cacheSymbol*/ true);
1012710133
}
1012810134

1012910135
function getTypeFromIndexedAccessTypeNode(node: IndexedAccessTypeNode) {
@@ -11512,10 +11518,10 @@ namespace ts {
1151211518
let reportedError = false;
1151311519
for (let status = iterator.next(); !status.done; status = iterator.next()) {
1151411520
const { errorNode: prop, innerExpression: next, nameType, errorMessage } = status.value;
11515-
const targetPropType = getIndexedAccessType(target, nameType, /*accessNode*/ undefined, errorType);
11516-
if (targetPropType === errorType || targetPropType.flags & TypeFlags.IndexedAccess) continue; // Don't elaborate on indexes on generic variables
11517-
const sourcePropType = getIndexedAccessType(source, nameType, /*accessNode*/ undefined, errorType);
11518-
if (sourcePropType !== errorType && targetPropType !== errorType && !checkTypeRelatedTo(sourcePropType, targetPropType, relation, /*errorNode*/ undefined)) {
11521+
const targetPropType = getIndexedAccessTypeOrUndefined(target, nameType);
11522+
if (!targetPropType || targetPropType.flags & TypeFlags.IndexedAccess) continue; // Don't elaborate on indexes on generic variables
11523+
const sourcePropType = getIndexedAccessTypeOrUndefined(source, nameType);
11524+
if (sourcePropType && !checkTypeRelatedTo(sourcePropType, targetPropType, relation, /*errorNode*/ undefined)) {
1151911525
const elaborated = next && elaborateError(next, sourcePropType, targetPropType, relation, /*headMessage*/ undefined);
1152011526
if (elaborated) {
1152111527
reportedError = true;
@@ -12819,9 +12825,7 @@ namespace ts {
1281912825
if (indexType.flags & TypeFlags.StructuredOrInstantiable) {
1282012826
const keyType = getLowerBoundOfKeyType(indexType, /*isIndexType*/ true);
1282112827
if (keyType !== indexType && !(keyType.flags & TypeFlags.Never)) {
12822-
const targetType = keyType.flags & TypeFlags.Union ?
12823-
getIntersectionType(map((<UnionType>keyType).types, t => getIndexedAccessType(objectType, t))) :
12824-
getIndexedAccessType(objectType, keyType);
12828+
const targetType = getIndexedAccessType(objectType, keyType, /*accessNode*/ undefined, /*writing*/ true);
1282512829
if (result = isRelatedTo(source, targetType, reportErrors)) {
1282612830
return result;
1282712831
}
@@ -12830,7 +12834,8 @@ namespace ts {
1283012834
else {
1283112835
const constraint = getConstraintOfType(objectType);
1283212836
if (constraint) {
12833-
if (result = isRelatedTo(source, getIndexedAccessType(constraint, indexType), reportErrors)) {
12837+
const targetType = getIndexedAccessType(constraint, indexType, /*accessNode*/ undefined, /*writing*/ true);
12838+
if (result = isRelatedTo(source, targetType, reportErrors)) {
1283412839
return result;
1283512840
}
1283612841
}
@@ -19975,7 +19980,9 @@ namespace ts {
1997519980
return errorType;
1997619981
}
1997719982

19978-
return checkIndexedAccessIndexType(getIndexedAccessType(objectType, isForInVariableForNumericPropertyNames(indexExpression) ? numberType : indexType, node), node);
19983+
const effectiveIndexType = isForInVariableForNumericPropertyNames(indexExpression) ? numberType : indexType;
19984+
const indexedAccessType = getIndexedAccessType(objectType, effectiveIndexType, node, isAssignmentTarget(node));
19985+
return checkIndexedAccessIndexType(indexedAccessType, node);
1997919986
}
1998019987

1998119988
function checkThatExpressionIsProperSymbolReference(expression: Expression, expressionType: Type, reportError: boolean): boolean {
@@ -24570,7 +24577,7 @@ namespace ts {
2457024577
return type;
2457124578
}
2457224579
error(accessNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(objectType));
24573-
return type;
24580+
return errorType;
2457424581
}
2457524582

2457624583
function checkIndexedAccessType(node: IndexedAccessTypeNode) {

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2152,6 +2152,10 @@
21522152
"category": "Error",
21532153
"code": 2593
21542154
},
2155+
"Type '{0}' cannot be indexed by type '{1}'.": {
2156+
"category": "Error",
2157+
"code": 2594
2158+
},
21552159
"JSX element attributes type '{0}' may not be a union type.": {
21562160
"category": "Error",
21572161
"code": 2600

0 commit comments

Comments
 (0)