@@ -406,6 +406,7 @@ import {
406406 ImportTypeNode,
407407 IndexedAccessType,
408408 IndexedAccessTypeNode,
409+ IndexFlags,
409410 IndexInfo,
410411 IndexKind,
411412 indexOfNode,
@@ -1451,6 +1452,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
14511452 var noImplicitThis = getStrictOptionValue(compilerOptions, "noImplicitThis");
14521453 var useUnknownInCatchVariables = getStrictOptionValue(compilerOptions, "useUnknownInCatchVariables");
14531454 var keyofStringsOnly = !!compilerOptions.keyofStringsOnly;
1455+ var defaultIndexFlags = keyofStringsOnly ? IndexFlags.StringsOnly : IndexFlags.None;
14541456 var freshObjectLiteralFlag = compilerOptions.suppressExcessPropertyErrors ? 0 : ObjectFlags.FreshLiteral;
14551457 var exactOptionalPropertyTypes = compilerOptions.exactOptionalPropertyTypes;
14561458
@@ -14155,6 +14157,23 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1415514157 return !prop.valueDeclaration && !!(getCheckFlags(prop) & CheckFlags.ContainsPrivate);
1415614158 }
1415714159
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+
1415814177 function elaborateNeverIntersection(errorInfo: DiagnosticMessageChain | undefined, type: Type) {
1415914178 if (type.flags & TypeFlags.Intersection && getObjectFlags(type) & ObjectFlags.IsNeverIntersection) {
1416014179 const neverProp = find(getPropertiesOfUnionOrIntersectionType(type as IntersectionType), isDiscriminantWithNeverType);
@@ -16810,10 +16829,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1681016829 return links.resolvedType;
1681116830 }
1681216831
16813- function createIndexType(type: InstantiableType | UnionOrIntersectionType, stringsOnly: boolean ) {
16832+ function createIndexType(type: InstantiableType | UnionOrIntersectionType, indexFlags: IndexFlags ) {
1681416833 const result = createType(TypeFlags.Index) as IndexType;
1681516834 result.type = type;
16816- result.stringsOnly = stringsOnly ;
16835+ result.indexFlags = indexFlags ;
1681716836 return result;
1681816837 }
1681916838
@@ -16823,10 +16842,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1682316842 return result;
1682416843 }
1682516844
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 ));
1683016849 }
1683116850
1683216851 /**
@@ -16836,11 +16855,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1683616855 * reduction in the constraintType) when possible.
1683716856 * @param noIndexSignatures Indicates if _string_ index signatures should be elided. (other index signatures are always reported)
1683816857 */
16839- function getIndexTypeForMappedType(type: MappedType, stringsOnly: boolean, noIndexSignatures: boolean | undefined ) {
16858+ function getIndexTypeForMappedType(type: MappedType, indexFlags: IndexFlags ) {
1684016859 const typeParameter = getTypeParameterFromMappedType(type);
1684116860 const constraintType = getConstraintTypeFromMappedType(type);
1684216861 const nameType = getNameTypeFromMappedType(type.target as MappedType || type);
16843- if (!nameType && !noIndexSignatures ) {
16862+ if (!nameType && !(indexFlags & IndexFlags.NoIndexSignatures) ) {
1684416863 // no mapping and no filtering required, just quickly bail to returning the constraint in the common case
1684516864 return constraintType;
1684616865 }
@@ -16853,12 +16872,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1685316872 // so we only eagerly manifest the keys if the constraint is nongeneric
1685416873 if (!isGenericIndexType(constraintType)) {
1685516874 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);
1685716876 }
1685816877 else {
1685916878 // we have a generic index and a homomorphic mapping (but a distributive key remapping) - we need to defer the whole `keyof whatever` for later
1686016879 // since it's not safe to resolve the shape of modifier type
16861- return getIndexTypeForGenericType(type, stringsOnly );
16880+ return getIndexTypeForGenericType(type, indexFlags );
1686216881 }
1686316882 }
1686416883 else {
@@ -16869,7 +16888,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1686916888 }
1687016889 // we had to pick apart the constraintType to potentially map/filter it - compare the final resulting list with the original constraintType,
1687116890 // 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);
1687316892 if (result.flags & TypeFlags.Union && constraintType.flags & TypeFlags.Union && getTypeListId((result as UnionType).types) === getTypeListId((constraintType as UnionType).types)){
1687416893 return constraintType;
1687516894 }
@@ -16938,36 +16957,25 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1693816957 /*aliasSymbol*/ undefined, /*aliasTypeArguments*/ undefined, origin);
1693916958 }
1694016959
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) {
1695316961 return !!(type.flags & TypeFlags.InstantiableNonPrimitive ||
1695416962 isGenericTupleType(type) ||
1695516963 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 ) ||
1695716965 type.flags & TypeFlags.Intersection && maybeTypeOfKind(type, TypeFlags.Instantiable) && some((type as IntersectionType).types, isEmptyAnonymousObjectType));
1695816966 }
1695916967
16960- function getIndexType(type: Type, stringsOnly = keyofStringsOnly, noIndexSignatures?: boolean ): Type {
16968+ function getIndexType(type: Type, indexFlags = defaultIndexFlags ): Type {
1696116969 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 ) :
1696616974 type === wildcardType ? wildcardType :
1696716975 type.flags & TypeFlags.Unknown ? neverType :
1696816976 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 );
1697116979 }
1697216980
1697316981 function getExtractStringType(type: Type) {
@@ -17580,6 +17588,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1758017588 if (objectType === wildcardType || indexType === wildcardType) {
1758117589 return wildcardType;
1758217590 }
17591+ objectType = getReducedType(objectType);
1758317592 // If the object type has a string index signature and no other members we know that the result will
1758417593 // always be the type of that index signature and we can simplify accordingly.
1758517594 if (isStringIndexSignatureOnlyType(objectType) && !(indexType.flags & TypeFlags.Nullable) && isTypeAssignableToKind(indexType, TypeFlags.String | TypeFlags.Number)) {
@@ -17596,7 +17605,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1759617605 // eagerly using the constraint type of 'this' at the given location.
1759717606 if (isGenericIndexType(indexType) || (accessNode && accessNode.kind !== SyntaxKind.IndexedAccessType ?
1759817607 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) )) {
1760017609 if (objectType.flags & TypeFlags.AnyOrUnknown) {
1760117610 return objectType;
1760217611 }
@@ -19016,11 +19025,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1901619025 return type; // Nested invocation of `inferTypeForHomomorphicMappedType` or the `source` instantiated into something unmappable
1901719026 }
1901819027
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-
1902419028 function getPermissiveInstantiation(type: Type) {
1902519029 return type.flags & (TypeFlags.Primitive | TypeFlags.AnyOrUnknown | TypeFlags.Never) ? type :
1902619030 type.permissiveInstantiation || (type.permissiveInstantiation = instantiateType(type, permissiveMapper));
@@ -21299,7 +21303,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2129921303 // false positives. For example, given 'T extends { [K in keyof T]: string }',
2130021304 // 'keyof T' has itself as its constraint and produces a Ternary.Maybe when
2130121305 // 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) {
2130321307 return Ternary.True;
2130421308 }
2130521309 }
@@ -21399,7 +21403,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2139921403 // If target has shape `{ [P in Q]: T }`, then its keys have type `Q`.
2140021404 const targetKeys = keysRemapped ? getNameTypeFromMappedType(target)! : getConstraintTypeFromMappedType(target);
2140121405 // 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 );
2140321407 const includeOptional = modifiers & MappedTypeModifiers.IncludeOptional;
2140421408 const filteredByApplicability = includeOptional ? intersectTypes(targetKeys, sourceKeys) : undefined;
2140521409 // 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 {
3852638530 // Check if the index type is assignable to 'keyof T' for the object type.
3852738531 const objectType = (type as IndexedAccessType).objectType;
3852838532 const indexType = (type as IndexedAccessType).indexType;
38529- if (isTypeAssignableTo(indexType, getIndexType(objectType, /*stringsOnly*/ false ))) {
38533+ if (isTypeAssignableTo(indexType, getIndexType(objectType, IndexFlags.None ))) {
3853038534 if (accessNode.kind === SyntaxKind.ElementAccessExpression && isAssignmentTarget(accessNode) &&
3853138535 getObjectFlags(objectType) & ObjectFlags.Mapped && getMappedTypeModifiers(objectType as MappedType) & MappedTypeModifiers.IncludeReadonly) {
3853238536 error(accessNode, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(objectType));
0 commit comments