Skip to content

Commit 0183d39

Browse files
committed
A whole new paradigm of index signature relating strategies
1 parent 8bdcc78 commit 0183d39

File tree

1 file changed

+54
-49
lines changed

1 file changed

+54
-49
lines changed

src/compiler/checker.ts

Lines changed: 54 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -3524,7 +3524,7 @@ namespace ts {
35243524
if (resolvedType.indexInfos) {
35253525
for (let info of resolvedType.indexInfos) {
35263526
if (resolvedType.objectFlags & ObjectFlags.ReverseMapped) {
3527-
info = createIndexInfo(info.indexType, anyType, info.isReadonly, info.declaration);
3527+
info = createIndexInfo(info.indexType, anyType, info.isReadonly, info.declaration);
35283528
}
35293529
typeElements.push(indexInfoToIndexSignatureDeclarationHelper(info, context));
35303530
}
@@ -6696,9 +6696,9 @@ namespace ts {
66966696
const readonlyMask = modifiers & MappedTypeModifiers.IncludeReadonly ? false : true;
66976697
const optionalMask = modifiers & MappedTypeModifiers.IncludeOptional ? 0 : SymbolFlags.Optional;
66986698
// TODO: Fix #26724 using the below once #26725 is fixed and `mappedRecursiveInference.ts` doesn't blow up with the change
6699-
//const mappedIndexInfos = indexInfos && map(indexInfos, indexInfo =>
6699+
// const mappedIndexInfos = indexInfos && map(indexInfos, indexInfo =>
67006700
// createIndexInfo(indexInfo.indexType, inferReverseMappedType(indexInfo.type, type.mappedType), readonlyMask && indexInfo.isReadonly)
6701-
//);
6701+
// );
67026702
const mappedIndexInfos = map(filter(indexInfos, i => !!(i.indexType.flags & TypeFlags.String)), i => createIndexInfo(i.indexType, inferReverseMappedType(i.type, type.mappedType), readonlyMask && i.isReadonly));
67036703
const members = createSymbolTable();
67046704
for (const prop of getPropertiesOfType(type.source)) {
@@ -7428,15 +7428,15 @@ namespace ts {
74287428
return resolveIndexOnIndexInfos(indexType, getIndexInfosOfType(type));
74297429
}
74307430

7431-
function getApplicableIndexInfosOfIndexOnType(type: Type, indexType: Type, contravariant?: boolean): IndexInfo[] | undefined {
7431+
function getApplicableIndexInfosOfIndexOnType(type: Type, indexType: Type, returnPartialSuccess?: boolean): IndexInfo[] | undefined {
74327432
if (!(indexType.flags & TypeFlags.Union)) {
74337433
const result = getResultingIndexInfoOfIndexOnType(type, indexType);
74347434
return result && [result];
74357435
}
74367436
let resultList: IndexInfo[] | undefined;
74377437
for (const nameType of (indexType as UnionType).types) {
74387438
const result = getResultingIndexInfoOfIndexOnType(type, nameType);
7439-
if (!result && !contravariant) {
7439+
if (!result && !returnPartialSuccess) {
74407440
return;
74417441
}
74427442
if (result) {
@@ -7446,8 +7446,8 @@ namespace ts {
74467446
return resultList;
74477447
}
74487448

7449-
function getResultingTypeOfIndexOnType(type: Type, indexType: Type, contravariant?: boolean): Type | undefined {
7450-
const resultInfos = getApplicableIndexInfosOfIndexOnType(type, indexType, contravariant);
7449+
function getResultingTypeOfIndexOnType(type: Type, indexType: Type, returnPartialSuccess?: boolean): Type | undefined {
7450+
const resultInfos = getApplicableIndexInfosOfIndexOnType(type, indexType, returnPartialSuccess);
74517451
return resultInfos && getUnionType(map(resultInfos, i => i.type));
74527452
}
74537453

@@ -9212,7 +9212,7 @@ namespace ts {
92129212
const base = getUnionType([
92139213
filterType(getUnionType(map(filter(getIndexInfosOfType(type) || emptyArray, info => info !== enumNumberIndexInfo), t => t.indexType)), t => !stringsOnly || isTypeAssignableTo(t, stringType)),
92149214
getLiteralTypeFromPropertyNames(type, stringsOnly ? TypeFlags.StringLiteral : TypeFlags.StringOrNumberLiteralOrUnique),
9215-
])
9215+
]);
92169216
if (!stringsOnly && maybeTypeOfKind(base, TypeFlags.String)) {
92179217
return getUnionType([base, numberType]);
92189218
}
@@ -10988,7 +10988,7 @@ namespace ts {
1098810988
return t.properties.length === 0 &&
1098910989
t.callSignatures.length === 0 &&
1099010990
t.constructSignatures.length === 0 &&
10991-
!length(t.indexInfos)
10991+
!length(t.indexInfos);
1099210992
}
1099310993

1099410994
function isEmptyObjectType(type: Type): boolean {
@@ -12289,7 +12289,9 @@ namespace ts {
1228912289
if (isIgnoredJsxProperty(source, prop, /*targetMemberType*/ undefined)) {
1229012290
continue;
1229112291
}
12292-
const targetType = getResultingTypeOfIndexOnType(target, getLiteralTypeFromPropertyName(prop, TypeFlags.StringOrNumberLiteralOrUnique));
12292+
const nameType = getLiteralTypeFromPropertyName(prop, TypeFlags.StringOrNumberLiteralOrUnique);
12293+
if (nameType.flags & TypeFlags.Never) continue;
12294+
const targetType = getResultingTypeOfIndexOnType(target, nameType);
1229312295
if (targetType) {
1229412296
const related = isRelatedTo(getTypeOfSymbol(prop), targetType, reportErrors);
1229512297
if (!related) {
@@ -12312,10 +12314,9 @@ namespace ts {
1231212314
if (!targetInfo) {
1231312315
return Ternary.True;
1231412316
}
12315-
const sourceInfo = getIndexInfosOfType(source)
12316-
const result = sourceInfo && indexInfosRelated(sourceInfo, targetInfo, sourceIsPrimitive, reportErrors);
12317-
if (result !== undefined && result !== Ternary.Maybe) {
12318-
return result;
12317+
const sourceInfo = getIndexInfosOfType(source);
12318+
if (sourceInfo) {
12319+
return indexInfosRelated(source, target, sourceIsPrimitive, reportErrors);
1231912320
}
1232012321
if (isGenericMappedType(source)) {
1232112322
// A generic mapped type { [P in K]: T } is related to an index signature { [x: string]: U }
@@ -12357,20 +12358,6 @@ namespace ts {
1235712358
return Ternary.False;
1235812359
}
1235912360

12360-
function isIndexTypeRelatedTo(source: IndexInfo, target: IndexInfo, sourceIsPrimitive: boolean, reportErrors: boolean, skipIndexError?: boolean): Ternary {
12361-
// Index signature of type any permits assignment from everything but primitives
12362-
if (!sourceIsPrimitive && target.type.flags & TypeFlags.AnyOrUnknown) {
12363-
return Ternary.True;
12364-
}
12365-
const related = isRelatedTo(source.type, target.type, reportErrors);
12366-
if (!related && reportErrors && !skipIndexError) {
12367-
const sourceType = typeToString(source.indexType);
12368-
const targetType = typeToString(target.indexType);
12369-
reportError(sourceType === targetType ? Diagnostics._0_index_signatures_are_incompatible : Diagnostics._0_and_1_index_signatures_are_incompatible, sourceType, targetType);
12370-
}
12371-
return related;
12372-
}
12373-
1237412361
function indexInfosIdentical(sourceInfos: IndexInfo[] | undefined, targetInfos: IndexInfo[] | undefined) {
1237512362
if (length(sourceInfos) !== length(targetInfos)) return Ternary.False;
1237612363
if (!sourceInfos || !targetInfos) return Ternary.True;
@@ -12393,28 +12380,46 @@ namespace ts {
1239312380
return Ternary.True;
1239412381
}
1239512382

12396-
function indexInfosRelated(sourceInfos: IndexInfo[] | undefined, targetInfos: IndexInfo[] | undefined, sourceIsPrimitive: boolean, reportErrors: boolean): Ternary {
12397-
if (!sourceInfos && !targetInfos) {
12398-
return Ternary.True;
12399-
}
12400-
if (sourceInfos && targetInfos) {
12401-
// If for every index signature in S there exists a related signature in T
12402-
targetLoop: for (const targetInfo of targetInfos) {
12403-
for (const sourceInfo of sourceInfos) {
12404-
if (isTypeIndexAssignableTo(sourceInfo.indexType, targetInfo.indexType)) {
12405-
if (!isIndexTypeRelatedTo(sourceInfo, targetInfo, sourceIsPrimitive, reportErrors)) return Ternary.False;
12406-
if (relation === identityRelation && sourceInfo.isReadonly !== targetInfo.isReadonly) return Ternary.False;
12407-
continue targetLoop;
12408-
}
12383+
function indexInfosRelated(source: Type, target: Type, sourceIsPrimitive: boolean, reportErrors: boolean): Ternary {
12384+
const targetInfos = getIndexInfosOfType(target);
12385+
if (!targetInfos) return Debug.fail("Existance of index infos in target should have been checked beforehand");
12386+
const sourceHasInferableIndex = isObjectTypeWithInferableIndex(source);
12387+
let result = Ternary.True;
12388+
for (const targetInfo of targetInfos) {
12389+
if (!sourceIsPrimitive && targetInfo.type.flags & TypeFlags.AnyOrUnknown) {
12390+
continue;
12391+
}
12392+
const indexingResult = getResultingTypeOfIndexOnType(source, targetInfo.indexType, sourceHasInferableIndex);
12393+
if (!indexingResult) {
12394+
if (sourceHasInferableIndex) {
12395+
continue;
1240912396
}
12410-
if (targetInfo === targetInfos[targetInfos.length - 1]) {
12411-
return Ternary.Maybe; // Iterated over every target signature without a definitive yes or no on every signature; allow fallback to inferred signature
12397+
if (reportErrors) {
12398+
reportError(Diagnostics.Index_signature_is_missing_in_type_0, typeToString(source));
1241212399
}
12400+
return Ternary.False;
12401+
}
12402+
result &= isRelatedTo(indexingResult, targetInfo.type, reportErrors);
12403+
if (!result) {
12404+
if (reportErrors) {
12405+
const sourceInfos = getApplicableIndexInfosOfIndexOnType(source, targetInfo.indexType, sourceHasInferableIndex);
12406+
for (const info of sourceInfos!) {
12407+
if (!isRelatedTo(info.type, targetInfo.type)) {
12408+
const sourceType = typeToString(info.indexType);
12409+
const targetType = typeToString(targetInfo.indexType);
12410+
reportError(sourceType === targetType ? Diagnostics._0_index_signatures_are_incompatible : Diagnostics._0_and_1_index_signatures_are_incompatible, sourceType, targetType);
12411+
break;
12412+
}
12413+
}
12414+
}
12415+
return result;
1241312416
}
12414-
// Then the signature lists are related
12415-
return Ternary.True;
1241612417
}
12417-
return Ternary.False;
12418+
if (result && sourceHasInferableIndex) {
12419+
// A type with an inferable index, provided none of its indexes are strictly incompatible, is compatible so long as its existing props are compatible
12420+
result &= eachPropertyRelatedToIndexesOf(source, target, reportErrors);
12421+
}
12422+
return result;
1241812423
}
1241912424

1242012425
function constructorVisibilitiesAreCompatible(sourceSignature: Signature, targetSignature: Signature, reportErrors: boolean) {
@@ -25305,7 +25310,7 @@ namespace ts {
2530525310
function checkIndexConstraints(type: Type) {
2530625311
const declaredIndexers = getIndexDeclarationsOfSymbol(type.symbol);
2530725312
const indexInfos = getIndexInfosOfType(type);
25308-
25313+
2530925314
if (indexInfos) {
2531025315
forEach(getPropertiesOfObjectType(type), prop => {
2531125316
const propType = getTypeOfSymbol(prop);
@@ -25365,10 +25370,10 @@ namespace ts {
2536525370
typeToString(info2.indexType),
2536625371
typeToString(info2.type)
2536725372
);
25368-
checkTypeAssignableTo(info1.type, info2.type, errorNode, undefined, rootChain);
25373+
checkTypeAssignableTo(info1.type, info2.type, errorNode, /*diagnosticMessage*/ undefined, rootChain);
2536925374
}
2537025375
}
25371-
})
25376+
});
2537225377
}
2537325378
}
2537425379

0 commit comments

Comments
 (0)