@@ -406,6 +406,7 @@ import {
406
406
ImportTypeNode,
407
407
IndexedAccessType,
408
408
IndexedAccessTypeNode,
409
+ IndexFlags,
409
410
IndexInfo,
410
411
IndexKind,
411
412
indexOfNode,
@@ -1451,6 +1452,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1451
1452
var noImplicitThis = getStrictOptionValue(compilerOptions, "noImplicitThis");
1452
1453
var useUnknownInCatchVariables = getStrictOptionValue(compilerOptions, "useUnknownInCatchVariables");
1453
1454
var keyofStringsOnly = !!compilerOptions.keyofStringsOnly;
1455
+ var defaultIndexFlags = keyofStringsOnly ? IndexFlags.StringsOnly : IndexFlags.None;
1454
1456
var freshObjectLiteralFlag = compilerOptions.suppressExcessPropertyErrors ? 0 : ObjectFlags.FreshLiteral;
1455
1457
var exactOptionalPropertyTypes = compilerOptions.exactOptionalPropertyTypes;
1456
1458
@@ -14155,6 +14157,23 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
14155
14157
return !prop.valueDeclaration && !!(getCheckFlags(prop) & CheckFlags.ContainsPrivate);
14156
14158
}
14157
14159
14160
+ /**
14161
+ * A union type which is reducible upon instantiation (meaning some members are removed under certain instantiations)
14162
+ * must be kept generic, as that instantiation information needs to flow through the type system. By replacing all
14163
+ * type parameters in the union with a special never type that is treated as a literal in `getReducedType`, we can cause
14164
+ * the `getReducedType` logic to reduce the resulting type if possible (since only intersections with conflicting
14165
+ * literal-typed properties are reducible).
14166
+ */
14167
+ function isGenericReducibleType(type: Type): boolean {
14168
+ return !!(type.flags & TypeFlags.Union && (type as UnionType).objectFlags & ObjectFlags.ContainsIntersections && some((type as UnionType).types, isGenericReducibleType) ||
14169
+ type.flags & TypeFlags.Intersection && isReducibleIntersection(type as IntersectionType));
14170
+ }
14171
+
14172
+ function isReducibleIntersection(type: IntersectionType) {
14173
+ const uniqueFilled = type.uniqueLiteralFilledInstantiation || (type.uniqueLiteralFilledInstantiation = instantiateType(type, uniqueLiteralMapper));
14174
+ return getReducedType(uniqueFilled) !== uniqueFilled;
14175
+ }
14176
+
14158
14177
function elaborateNeverIntersection(errorInfo: DiagnosticMessageChain | undefined, type: Type) {
14159
14178
if (type.flags & TypeFlags.Intersection && getObjectFlags(type) & ObjectFlags.IsNeverIntersection) {
14160
14179
const neverProp = find(getPropertiesOfUnionOrIntersectionType(type as IntersectionType), isDiscriminantWithNeverType);
@@ -16810,10 +16829,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
16810
16829
return links.resolvedType;
16811
16830
}
16812
16831
16813
- function createIndexType(type: InstantiableType | UnionOrIntersectionType, stringsOnly: boolean ) {
16832
+ function createIndexType(type: InstantiableType | UnionOrIntersectionType, indexFlags: IndexFlags ) {
16814
16833
const result = createType(TypeFlags.Index) as IndexType;
16815
16834
result.type = type;
16816
- result.stringsOnly = stringsOnly ;
16835
+ result.indexFlags = indexFlags ;
16817
16836
return result;
16818
16837
}
16819
16838
@@ -16823,10 +16842,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
16823
16842
return result;
16824
16843
}
16825
16844
16826
- function getIndexTypeForGenericType(type: InstantiableType | UnionOrIntersectionType, stringsOnly: boolean ) {
16827
- return stringsOnly ?
16828
- type.resolvedStringIndexType || (type.resolvedStringIndexType = createIndexType(type, /*stringsOnly*/ true )) :
16829
- type.resolvedIndexType || (type.resolvedIndexType = createIndexType(type, /*stringsOnly*/ false ));
16845
+ function getIndexTypeForGenericType(type: InstantiableType | UnionOrIntersectionType, indexFlags: IndexFlags ) {
16846
+ return indexFlags & IndexFlags.StringsOnly ?
16847
+ type.resolvedStringIndexType || (type.resolvedStringIndexType = createIndexType(type, IndexFlags.StringsOnly )) :
16848
+ type.resolvedIndexType || (type.resolvedIndexType = createIndexType(type, IndexFlags.None ));
16830
16849
}
16831
16850
16832
16851
/**
@@ -16836,11 +16855,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
16836
16855
* reduction in the constraintType) when possible.
16837
16856
* @param noIndexSignatures Indicates if _string_ index signatures should be elided. (other index signatures are always reported)
16838
16857
*/
16839
- function getIndexTypeForMappedType(type: MappedType, stringsOnly: boolean, noIndexSignatures: boolean | undefined ) {
16858
+ function getIndexTypeForMappedType(type: MappedType, indexFlags: IndexFlags ) {
16840
16859
const typeParameter = getTypeParameterFromMappedType(type);
16841
16860
const constraintType = getConstraintTypeFromMappedType(type);
16842
16861
const nameType = getNameTypeFromMappedType(type.target as MappedType || type);
16843
- if (!nameType && !noIndexSignatures ) {
16862
+ if (!nameType && !(indexFlags & IndexFlags.NoIndexSignatures) ) {
16844
16863
// no mapping and no filtering required, just quickly bail to returning the constraint in the common case
16845
16864
return constraintType;
16846
16865
}
@@ -16853,12 +16872,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
16853
16872
// so we only eagerly manifest the keys if the constraint is nongeneric
16854
16873
if (!isGenericIndexType(constraintType)) {
16855
16874
const modifiersType = getApparentType(getModifiersTypeFromMappedType(type)); // The 'T' in 'keyof T'
16856
- forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType(modifiersType, TypeFlags.StringOrNumberLiteralOrUnique, stringsOnly , addMemberForKeyType);
16875
+ forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType(modifiersType, TypeFlags.StringOrNumberLiteralOrUnique, !!(indexFlags & IndexFlags.StringsOnly) , addMemberForKeyType);
16857
16876
}
16858
16877
else {
16859
16878
// we have a generic index and a homomorphic mapping (but a distributive key remapping) - we need to defer the whole `keyof whatever` for later
16860
16879
// since it's not safe to resolve the shape of modifier type
16861
- return getIndexTypeForGenericType(type, stringsOnly );
16880
+ return getIndexTypeForGenericType(type, indexFlags );
16862
16881
}
16863
16882
}
16864
16883
else {
@@ -16869,7 +16888,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
16869
16888
}
16870
16889
// we had to pick apart the constraintType to potentially map/filter it - compare the final resulting list with the original constraintType,
16871
16890
// so we can return the union that preserves aliases/origin data if possible
16872
- const result = noIndexSignatures ? filterType(getUnionType(keyTypes), t => !(t.flags & (TypeFlags.Any | TypeFlags.String))) : getUnionType(keyTypes);
16891
+ const result = indexFlags & IndexFlags.NoIndexSignatures ? filterType(getUnionType(keyTypes), t => !(t.flags & (TypeFlags.Any | TypeFlags.String))) : getUnionType(keyTypes);
16873
16892
if (result.flags & TypeFlags.Union && constraintType.flags & TypeFlags.Union && getTypeListId((result as UnionType).types) === getTypeListId((constraintType as UnionType).types)){
16874
16893
return constraintType;
16875
16894
}
@@ -16938,36 +16957,25 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
16938
16957
/*aliasSymbol*/ undefined, /*aliasTypeArguments*/ undefined, origin);
16939
16958
}
16940
16959
16941
- /**
16942
- * A union type which is reducible upon instantiation (meaning some members are removed under certain instantiations)
16943
- * must be kept generic, as that instantiation information needs to flow through the type system. By replacing all
16944
- * type parameters in the union with a special never type that is treated as a literal in `getReducedType`, we can cause the `getReducedType` logic
16945
- * to reduce the resulting type if possible (since only intersections with conflicting literal-typed properties are reducible).
16946
- */
16947
- function isPossiblyReducibleByInstantiation(type: Type): boolean {
16948
- const uniqueFilled = getUniqueLiteralFilledInstantiation(type);
16949
- return getReducedType(uniqueFilled) !== uniqueFilled;
16950
- }
16951
-
16952
- function shouldDeferIndexType(type: Type) {
16960
+ function shouldDeferIndexType(type: Type, indexFlags = IndexFlags.None) {
16953
16961
return !!(type.flags & TypeFlags.InstantiableNonPrimitive ||
16954
16962
isGenericTupleType(type) ||
16955
16963
isGenericMappedType(type) && !hasDistributiveNameType(type) ||
16956
- type.flags & TypeFlags.Union && some((type as UnionType).types, isPossiblyReducibleByInstantiation ) ||
16964
+ type.flags & TypeFlags.Union && !(indexFlags & IndexFlags.NoReducibleCheck) && isGenericReducibleType(type ) ||
16957
16965
type.flags & TypeFlags.Intersection && maybeTypeOfKind(type, TypeFlags.Instantiable) && some((type as IntersectionType).types, isEmptyAnonymousObjectType));
16958
16966
}
16959
16967
16960
- function getIndexType(type: Type, stringsOnly = keyofStringsOnly, noIndexSignatures?: boolean ): Type {
16968
+ function getIndexType(type: Type, indexFlags = defaultIndexFlags ): Type {
16961
16969
type = getReducedType(type);
16962
- return shouldDeferIndexType(type) ? getIndexTypeForGenericType(type as InstantiableType | UnionOrIntersectionType, stringsOnly ) :
16963
- type.flags & TypeFlags.Union ? getIntersectionType(map((type as UnionType).types, t => getIndexType(t, stringsOnly, noIndexSignatures ))) :
16964
- type.flags & TypeFlags.Intersection ? getUnionType(map((type as IntersectionType).types, t => getIndexType(t, stringsOnly, noIndexSignatures ))) :
16965
- getObjectFlags(type) & ObjectFlags.Mapped ? getIndexTypeForMappedType(type as MappedType, stringsOnly, noIndexSignatures ) :
16970
+ return shouldDeferIndexType(type, indexFlags ) ? getIndexTypeForGenericType(type as InstantiableType | UnionOrIntersectionType, indexFlags ) :
16971
+ type.flags & TypeFlags.Union ? getIntersectionType(map((type as UnionType).types, t => getIndexType(t, indexFlags ))) :
16972
+ type.flags & TypeFlags.Intersection ? getUnionType(map((type as IntersectionType).types, t => getIndexType(t, indexFlags ))) :
16973
+ getObjectFlags(type) & ObjectFlags.Mapped ? getIndexTypeForMappedType(type as MappedType, indexFlags ) :
16966
16974
type === wildcardType ? wildcardType :
16967
16975
type.flags & TypeFlags.Unknown ? neverType :
16968
16976
type.flags & (TypeFlags.Any | TypeFlags.Never) ? keyofConstraintType :
16969
- getLiteralTypeFromProperties(type, (noIndexSignatures ? TypeFlags.StringLiteral : TypeFlags.StringLike) | (stringsOnly ? 0 : TypeFlags.NumberLike | TypeFlags.ESSymbolLike),
16970
- stringsOnly === keyofStringsOnly && !noIndexSignatures );
16977
+ getLiteralTypeFromProperties(type, (indexFlags & IndexFlags.NoIndexSignatures ? TypeFlags.StringLiteral : TypeFlags.StringLike) | (indexFlags & IndexFlags.StringsOnly ? 0 : TypeFlags.NumberLike | TypeFlags.ESSymbolLike),
16978
+ indexFlags === defaultIndexFlags );
16971
16979
}
16972
16980
16973
16981
function getExtractStringType(type: Type) {
@@ -17580,6 +17588,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
17580
17588
if (objectType === wildcardType || indexType === wildcardType) {
17581
17589
return wildcardType;
17582
17590
}
17591
+ objectType = getReducedType(objectType);
17583
17592
// If the object type has a string index signature and no other members we know that the result will
17584
17593
// always be the type of that index signature and we can simplify accordingly.
17585
17594
if (isStringIndexSignatureOnlyType(objectType) && !(indexType.flags & TypeFlags.Nullable) && isTypeAssignableToKind(indexType, TypeFlags.String | TypeFlags.Number)) {
@@ -17596,7 +17605,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
17596
17605
// eagerly using the constraint type of 'this' at the given location.
17597
17606
if (isGenericIndexType(indexType) || (accessNode && accessNode.kind !== SyntaxKind.IndexedAccessType ?
17598
17607
isGenericTupleType(objectType) && !indexTypeLessThan(indexType, objectType.target.fixedLength) :
17599
- isGenericObjectType(objectType) && !(isTupleType(objectType) && indexTypeLessThan(indexType, objectType.target.fixedLength)))) {
17608
+ isGenericObjectType(objectType) && !(isTupleType(objectType) && indexTypeLessThan(indexType, objectType.target.fixedLength)) || isGenericReducibleType(objectType) )) {
17600
17609
if (objectType.flags & TypeFlags.AnyOrUnknown) {
17601
17610
return objectType;
17602
17611
}
@@ -19016,11 +19025,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
19016
19025
return type; // Nested invocation of `inferTypeForHomomorphicMappedType` or the `source` instantiated into something unmappable
19017
19026
}
19018
19027
19019
- function getUniqueLiteralFilledInstantiation(type: Type) {
19020
- return type.flags & (TypeFlags.Primitive | TypeFlags.AnyOrUnknown | TypeFlags.Never) ? type :
19021
- type.uniqueLiteralFilledInstantiation || (type.uniqueLiteralFilledInstantiation = instantiateType(type, uniqueLiteralMapper));
19022
- }
19023
-
19024
19028
function getPermissiveInstantiation(type: Type) {
19025
19029
return type.flags & (TypeFlags.Primitive | TypeFlags.AnyOrUnknown | TypeFlags.Never) ? type :
19026
19030
type.permissiveInstantiation || (type.permissiveInstantiation = instantiateType(type, permissiveMapper));
@@ -21299,7 +21303,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
21299
21303
// false positives. For example, given 'T extends { [K in keyof T]: string }',
21300
21304
// 'keyof T' has itself as its constraint and produces a Ternary.Maybe when
21301
21305
// related to other types.
21302
- if (isRelatedTo(source, getIndexType(constraint, (target as IndexType).stringsOnly ), RecursionFlags.Target, reportErrors) === Ternary.True) {
21306
+ if (isRelatedTo(source, getIndexType(constraint, (target as IndexType).indexFlags | IndexFlags.NoReducibleCheck ), RecursionFlags.Target, reportErrors) === Ternary.True) {
21303
21307
return Ternary.True;
21304
21308
}
21305
21309
}
@@ -21399,7 +21403,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
21399
21403
// If target has shape `{ [P in Q]: T }`, then its keys have type `Q`.
21400
21404
const targetKeys = keysRemapped ? getNameTypeFromMappedType(target)! : getConstraintTypeFromMappedType(target);
21401
21405
// Type of the keys of source type `S`, i.e. `keyof S`.
21402
- const sourceKeys = getIndexType(source, /*stringsOnly*/ undefined, /*noIndexSignatures*/ true );
21406
+ const sourceKeys = getIndexType(source, IndexFlags.NoIndexSignatures );
21403
21407
const includeOptional = modifiers & MappedTypeModifiers.IncludeOptional;
21404
21408
const filteredByApplicability = includeOptional ? intersectTypes(targetKeys, sourceKeys) : undefined;
21405
21409
// A source type `S` is related to a target type `{ [P in Q]: T }` if `Q` is related to `keyof S` and `S[Q]` is related to `T`.
@@ -38526,7 +38530,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
38526
38530
// Check if the index type is assignable to 'keyof T' for the object type.
38527
38531
const objectType = (type as IndexedAccessType).objectType;
38528
38532
const indexType = (type as IndexedAccessType).indexType;
38529
- if (isTypeAssignableTo(indexType, getIndexType(objectType, /*stringsOnly*/ false ))) {
38533
+ if (isTypeAssignableTo(indexType, getIndexType(objectType, IndexFlags.None ))) {
38530
38534
if (accessNode.kind === SyntaxKind.ElementAccessExpression && isAssignmentTarget(accessNode) &&
38531
38535
getObjectFlags(objectType) & ObjectFlags.Mapped && getMappedTypeModifiers(objectType as MappedType) & MappedTypeModifiers.IncludeReadonly) {
38532
38536
error(accessNode, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(objectType));
0 commit comments