From b9a01cb6ba52f647bc6da2e3dcca9fb10f4288fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 29 Oct 2023 00:21:40 +0200 Subject: [PATCH 1/5] Instantiate `nameType` of filtering mapped types when creating index for their mapped types --- src/compiler/checker.ts | 18 ++-- .../reference/keyRemappingKeyofResult.types | 8 +- .../keyRemappingKeyofResult2.symbols | 84 +++++++++++++++++++ .../reference/keyRemappingKeyofResult2.types | 59 +++++++++++++ .../reference/mappedTypeAsClauses.types | 10 +-- .../compiler/keyRemappingKeyofResult2.ts | 34 ++++++++ 6 files changed, 198 insertions(+), 15 deletions(-) create mode 100644 tests/baselines/reference/keyRemappingKeyofResult2.symbols create mode 100644 tests/baselines/reference/keyRemappingKeyofResult2.types create mode 100644 tests/cases/compiler/keyRemappingKeyofResult2.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 254dbf5753248..ef70947fd95fe 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17449,7 +17449,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { * reduction in the constraintType) when possible. * @param noIndexSignatures Indicates if _string_ index signatures should be elided. (other index signatures are always reported) */ - function getIndexTypeForMappedType(type: MappedType, indexFlags: IndexFlags) { + function getIndexTypeForMappedType(type: MappedType, indexFlags: IndexFlags): Type { const typeParameter = getTypeParameterFromMappedType(type); const constraintType = getConstraintTypeFromMappedType(type); const nameType = getNameTypeFromMappedType(type.target as MappedType || type); @@ -17468,6 +17468,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const modifiersType = getApparentType(getModifiersTypeFromMappedType(type)); // The 'T' in 'keyof T' forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType(modifiersType, TypeFlags.StringOrNumberLiteralOrUnique, !!(indexFlags & IndexFlags.StringsOnly), addMemberForKeyType); } + else if (isFilteringMappedType(type)) { + const modifiersIndex = getIndexType(getModifiersTypeFromMappedType(type), indexFlags, UnionReduction.None); + const mapper = makeUnaryTypeMapper(getTypeParameterFromMappedType(type), modifiersIndex); + const nameMapper = combineTypeMappers(type.mapper, mapper); + return instantiateType(nameType, nameMapper)!; + } else { // we have a generic index and a homomorphic mapping (but a distributive key remapping) - we need to defer the whole `keyof whatever` for later // since it's not safe to resolve the shape of modifier type @@ -17554,13 +17560,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return !!(keyType.flags & include || keyType.flags & TypeFlags.Intersection && some((keyType as IntersectionType).types, t => isKeyTypeIncluded(t, include))); } - function getLiteralTypeFromProperties(type: Type, include: TypeFlags, includeOrigin: boolean) { + function getLiteralTypeFromProperties(type: Type, include: TypeFlags, includeOrigin: boolean, unionReduction = UnionReduction.Literal) { const origin = includeOrigin && (getObjectFlags(type) & (ObjectFlags.ClassOrInterface | ObjectFlags.Reference) || type.aliasSymbol) ? createOriginIndexType(type) : undefined; const propertyTypes = map(getPropertiesOfType(type), prop => getLiteralTypeFromProperty(prop, include)); const indexKeyTypes = map(getIndexInfosOfType(type), info => info !== enumNumberIndexInfo && isKeyTypeIncluded(info.keyType, include) ? info.keyType === stringType && include & TypeFlags.Number ? stringOrNumberType : info.keyType : neverType); - return getUnionType(concatenate(propertyTypes, indexKeyTypes), UnionReduction.Literal, /*aliasSymbol*/ undefined, /*aliasTypeArguments*/ undefined, origin); + return getUnionType(concatenate(propertyTypes, indexKeyTypes), unionReduction, /*aliasSymbol*/ undefined, /*aliasTypeArguments*/ undefined, origin); } function shouldDeferIndexType(type: Type, indexFlags = IndexFlags.None) { @@ -17571,16 +17577,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { type.flags & TypeFlags.Intersection && maybeTypeOfKind(type, TypeFlags.Instantiable) && some((type as IntersectionType).types, isEmptyAnonymousObjectType)); } - function getIndexType(type: Type, indexFlags = defaultIndexFlags): Type { + function getIndexType(type: Type, indexFlags = defaultIndexFlags, unionReduction?: UnionReduction): Type { type = getReducedType(type); return shouldDeferIndexType(type, indexFlags) ? getIndexTypeForGenericType(type as InstantiableType | UnionOrIntersectionType, indexFlags) : type.flags & TypeFlags.Union ? getIntersectionType(map((type as UnionType).types, t => getIndexType(t, indexFlags))) : - type.flags & TypeFlags.Intersection ? getUnionType(map((type as IntersectionType).types, t => getIndexType(t, indexFlags))) : + type.flags & TypeFlags.Intersection ? getUnionType(map((type as IntersectionType).types, t => getIndexType(t, indexFlags, unionReduction)), unionReduction) : getObjectFlags(type) & ObjectFlags.Mapped ? getIndexTypeForMappedType(type as MappedType, indexFlags) : type === wildcardType ? wildcardType : type.flags & TypeFlags.Unknown ? neverType : type.flags & (TypeFlags.Any | TypeFlags.Never) ? keyofConstraintType : - getLiteralTypeFromProperties(type, (indexFlags & IndexFlags.NoIndexSignatures ? TypeFlags.StringLiteral : TypeFlags.StringLike) | (indexFlags & IndexFlags.StringsOnly ? 0 : TypeFlags.NumberLike | TypeFlags.ESSymbolLike), indexFlags === defaultIndexFlags); + getLiteralTypeFromProperties(type, (indexFlags & IndexFlags.NoIndexSignatures ? TypeFlags.StringLiteral : TypeFlags.StringLike) | (indexFlags & IndexFlags.StringsOnly ? 0 : TypeFlags.NumberLike | TypeFlags.ESSymbolLike), indexFlags === defaultIndexFlags, unionReduction); } function getExtractStringType(type: Type) { diff --git a/tests/baselines/reference/keyRemappingKeyofResult.types b/tests/baselines/reference/keyRemappingKeyofResult.types index 01569ae0ddeed..00f60952258f3 100644 --- a/tests/baselines/reference/keyRemappingKeyofResult.types +++ b/tests/baselines/reference/keyRemappingKeyofResult.types @@ -156,19 +156,19 @@ function g() { // no string index signature, right? type Oops = keyof Remapped; ->Oops : keyof { [K in keyof ({ [k: string]: any; str: any; [sym]: any; } & T) as K extends unknown ? {} extends Record ? never : K : never]: any; } +>Oops : unique symbol | "str" | (keyof T extends unknown ? {} extends Record ? never : keyof T : never) let x: Oops; ->x : keyof { [K in keyof ({ [k: string]: any; str: any; [sym]: any; } & T) as K extends unknown ? {} extends Record ? never : K : never]: any; } +>x : unique symbol | "str" | (keyof T extends unknown ? {} extends Record ? never : keyof T : never) x = sym; >x = sym : unique symbol ->x : keyof { [K in keyof ({ [k: string]: any; str: any; [sym]: any; } & T) as K extends unknown ? {} extends Record ? never : K : never]: any; } +>x : unique symbol | "str" | (keyof T extends unknown ? {} extends Record ? never : keyof T : never) >sym : unique symbol x = "str"; >x = "str" : "str" ->x : keyof { [K in keyof ({ [k: string]: any; str: any; [sym]: any; } & T) as K extends unknown ? {} extends Record ? never : K : never]: any; } +>x : unique symbol | "str" | (keyof T extends unknown ? {} extends Record ? never : keyof T : never) >"str" : "str" } diff --git a/tests/baselines/reference/keyRemappingKeyofResult2.symbols b/tests/baselines/reference/keyRemappingKeyofResult2.symbols new file mode 100644 index 0000000000000..b3fd16667f89f --- /dev/null +++ b/tests/baselines/reference/keyRemappingKeyofResult2.symbols @@ -0,0 +1,84 @@ +//// [tests/cases/compiler/keyRemappingKeyofResult2.ts] //// + +=== keyRemappingKeyofResult2.ts === +// https://github.com/microsoft/TypeScript/issues/56239 + +type Values = T[keyof T]; +>Values : Symbol(Values, Decl(keyRemappingKeyofResult2.ts, 0, 0)) +>T : Symbol(T, Decl(keyRemappingKeyofResult2.ts, 2, 12)) +>T : Symbol(T, Decl(keyRemappingKeyofResult2.ts, 2, 12)) +>T : Symbol(T, Decl(keyRemappingKeyofResult2.ts, 2, 12)) + +type ProvidedActor = { +>ProvidedActor : Symbol(ProvidedActor, Decl(keyRemappingKeyofResult2.ts, 2, 28)) + + src: string; +>src : Symbol(src, Decl(keyRemappingKeyofResult2.ts, 4, 22)) + + logic: unknown; +>logic : Symbol(logic, Decl(keyRemappingKeyofResult2.ts, 5, 14)) + +}; + +interface StateMachineConfig { +>StateMachineConfig : Symbol(StateMachineConfig, Decl(keyRemappingKeyofResult2.ts, 7, 2)) +>TActors : Symbol(TActors, Decl(keyRemappingKeyofResult2.ts, 9, 29)) +>ProvidedActor : Symbol(ProvidedActor, Decl(keyRemappingKeyofResult2.ts, 2, 28)) + + invoke: { +>invoke : Symbol(StateMachineConfig.invoke, Decl(keyRemappingKeyofResult2.ts, 9, 61)) + + src: TActors["src"]; +>src : Symbol(src, Decl(keyRemappingKeyofResult2.ts, 10, 11)) +>TActors : Symbol(TActors, Decl(keyRemappingKeyofResult2.ts, 9, 29)) + + }; +} + +declare function setup>(_: { +>setup : Symbol(setup, Decl(keyRemappingKeyofResult2.ts, 13, 1)) +>TActors : Symbol(TActors, Decl(keyRemappingKeyofResult2.ts, 15, 23)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>_ : Symbol(_, Decl(keyRemappingKeyofResult2.ts, 15, 64)) + + actors: { +>actors : Symbol(actors, Decl(keyRemappingKeyofResult2.ts, 15, 68)) + + [K in keyof TActors]: TActors[K]; +>K : Symbol(K, Decl(keyRemappingKeyofResult2.ts, 17, 5)) +>TActors : Symbol(TActors, Decl(keyRemappingKeyofResult2.ts, 15, 23)) +>TActors : Symbol(TActors, Decl(keyRemappingKeyofResult2.ts, 15, 23)) +>K : Symbol(K, Decl(keyRemappingKeyofResult2.ts, 17, 5)) + + }; +}): { + createMachine: ( +>createMachine : Symbol(createMachine, Decl(keyRemappingKeyofResult2.ts, 19, 5)) + + config: StateMachineConfig< +>config : Symbol(config, Decl(keyRemappingKeyofResult2.ts, 20, 18)) +>StateMachineConfig : Symbol(StateMachineConfig, Decl(keyRemappingKeyofResult2.ts, 7, 2)) + + Values<{ +>Values : Symbol(Values, Decl(keyRemappingKeyofResult2.ts, 0, 0)) + + [K in keyof TActors as K & string]: { +>K : Symbol(K, Decl(keyRemappingKeyofResult2.ts, 23, 9)) +>TActors : Symbol(TActors, Decl(keyRemappingKeyofResult2.ts, 15, 23)) +>K : Symbol(K, Decl(keyRemappingKeyofResult2.ts, 23, 9)) + + src: K; +>src : Symbol(src, Decl(keyRemappingKeyofResult2.ts, 23, 45)) +>K : Symbol(K, Decl(keyRemappingKeyofResult2.ts, 23, 9)) + + logic: TActors[K]; +>logic : Symbol(logic, Decl(keyRemappingKeyofResult2.ts, 24, 17)) +>TActors : Symbol(TActors, Decl(keyRemappingKeyofResult2.ts, 15, 23)) +>K : Symbol(K, Decl(keyRemappingKeyofResult2.ts, 23, 9)) + + }; + }> + >, + ) => void; +}; + diff --git a/tests/baselines/reference/keyRemappingKeyofResult2.types b/tests/baselines/reference/keyRemappingKeyofResult2.types new file mode 100644 index 0000000000000..fac82eacbafc0 --- /dev/null +++ b/tests/baselines/reference/keyRemappingKeyofResult2.types @@ -0,0 +1,59 @@ +//// [tests/cases/compiler/keyRemappingKeyofResult2.ts] //// + +=== keyRemappingKeyofResult2.ts === +// https://github.com/microsoft/TypeScript/issues/56239 + +type Values = T[keyof T]; +>Values : Values + +type ProvidedActor = { +>ProvidedActor : { src: string; logic: unknown; } + + src: string; +>src : string + + logic: unknown; +>logic : unknown + +}; + +interface StateMachineConfig { + invoke: { +>invoke : { src: TActors["src"]; } + + src: TActors["src"]; +>src : TActors["src"] + + }; +} + +declare function setup>(_: { +>setup : >(_: { actors: { [K in keyof TActors]: TActors[K]; }; }) => { createMachine: (config: StateMachineConfig>) => void; } +>_ : { actors: { [K in keyof TActors]: TActors[K]; }; } + + actors: { +>actors : { [K in keyof TActors]: TActors[K]; } + + [K in keyof TActors]: TActors[K]; + }; +}): { + createMachine: ( +>createMachine : (config: StateMachineConfig>) => void + + config: StateMachineConfig< +>config : StateMachineConfig> + + Values<{ + [K in keyof TActors as K & string]: { + src: K; +>src : K + + logic: TActors[K]; +>logic : TActors[K] + + }; + }> + >, + ) => void; +}; + diff --git a/tests/baselines/reference/mappedTypeAsClauses.types b/tests/baselines/reference/mappedTypeAsClauses.types index f92414a60d1fa..8304b4c90bf8d 100644 --- a/tests/baselines/reference/mappedTypeAsClauses.types +++ b/tests/baselines/reference/mappedTypeAsClauses.types @@ -262,16 +262,16 @@ type NameMap = { 'a': 'x', 'b': 'y', 'c': 'z' }; // Distributive, will be simplified type TS0 = keyof { [P in keyof T as keyof Record]: string }; ->TS0 : keyof { [P in keyof T as P]: string; } +>TS0 : keyof T type TS1 = keyof { [P in keyof T as Extract]: string }; ->TS1 : keyof { [P in keyof T as Extract]: string; } +>TS1 : Extract type TS2 = keyof { [P in keyof T as P & ('a' | 'b' | 'c')]: string }; ->TS2 : keyof { [P in keyof T as P & ("a" | "b" | "c")]: string; } +>TS2 : keyof T & ("a" | "b" | "c") type TS3 = keyof { [P in keyof T as Exclude]: string }; ->TS3 : keyof { [P in keyof T as Exclude]: string; } +>TS3 : Exclude type TS4 = keyof { [P in keyof T as NameMap[P & keyof NameMap]]: string }; >TS4 : keyof { [P in keyof T as NameMap[P & keyof NameMap]]: string; } @@ -280,7 +280,7 @@ type TS5 = keyof { [P in keyof T & keyof NameMap as NameMap[P]]: string }; >TS5 : NameMap[keyof T & "a"] | NameMap[keyof T & "b"] | NameMap[keyof T & "c"] type TS6 = keyof { [ K in keyof T as V & (K extends U ? K : never)]: string }; ->TS6 : keyof { [K in keyof T as V & (K extends U ? K : never)]: string; } +>TS6 : V & (keyof T extends U ? U & keyof T : never) // Non-distributive, won't be simplified diff --git a/tests/cases/compiler/keyRemappingKeyofResult2.ts b/tests/cases/compiler/keyRemappingKeyofResult2.ts new file mode 100644 index 0000000000000..b77b7af1760a9 --- /dev/null +++ b/tests/cases/compiler/keyRemappingKeyofResult2.ts @@ -0,0 +1,34 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/56239 + +type Values = T[keyof T]; + +type ProvidedActor = { + src: string; + logic: unknown; +}; + +interface StateMachineConfig { + invoke: { + src: TActors["src"]; + }; +} + +declare function setup>(_: { + actors: { + [K in keyof TActors]: TActors[K]; + }; +}): { + createMachine: ( + config: StateMachineConfig< + Values<{ + [K in keyof TActors as K & string]: { + src: K; + logic: TActors[K]; + }; + }> + >, + ) => void; +}; From e13ad3897b9ae4daeec16c18c8a07a9b3a09b092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 29 Oct 2023 00:46:20 +0200 Subject: [PATCH 2/5] just always instantiate the name type --- src/compiler/checker.ts | 4 ++-- tests/baselines/reference/mappedTypeAsClauses.types | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ef70947fd95fe..c53f46c999e49 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17468,11 +17468,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const modifiersType = getApparentType(getModifiersTypeFromMappedType(type)); // The 'T' in 'keyof T' forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType(modifiersType, TypeFlags.StringOrNumberLiteralOrUnique, !!(indexFlags & IndexFlags.StringsOnly), addMemberForKeyType); } - else if (isFilteringMappedType(type)) { + else if (nameType) { const modifiersIndex = getIndexType(getModifiersTypeFromMappedType(type), indexFlags, UnionReduction.None); const mapper = makeUnaryTypeMapper(getTypeParameterFromMappedType(type), modifiersIndex); const nameMapper = combineTypeMappers(type.mapper, mapper); - return instantiateType(nameType, nameMapper)!; + return instantiateType(nameType, nameMapper); } else { // we have a generic index and a homomorphic mapping (but a distributive key remapping) - we need to defer the whole `keyof whatever` for later diff --git a/tests/baselines/reference/mappedTypeAsClauses.types b/tests/baselines/reference/mappedTypeAsClauses.types index 8304b4c90bf8d..7ce8c408d0b0a 100644 --- a/tests/baselines/reference/mappedTypeAsClauses.types +++ b/tests/baselines/reference/mappedTypeAsClauses.types @@ -274,7 +274,7 @@ type TS3 = keyof { [P in keyof T as Exclude]: string }; >TS3 : Exclude type TS4 = keyof { [P in keyof T as NameMap[P & keyof NameMap]]: string }; ->TS4 : keyof { [P in keyof T as NameMap[P & keyof NameMap]]: string; } +>TS4 : NameMap[keyof T & keyof NameMap] type TS5 = keyof { [P in keyof T & keyof NameMap as NameMap[P]]: string }; >TS5 : NameMap[keyof T & "a"] | NameMap[keyof T & "b"] | NameMap[keyof T & "c"] From 4c69959e8f33486d7730d5d6579e14a2d3b63de1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Wed, 27 Dec 2023 01:05:09 +0100 Subject: [PATCH 3/5] Revert `nameType` intantiation --- src/compiler/checker.ts | 18 +++---- .../reference/keyRemappingKeyofResult.types | 8 +-- .../keyRemappingKeyofResult2.errors.txt | 51 +++++++++++++++++++ .../reference/mappedTypeAsClauses.types | 10 ++-- 4 files changed, 66 insertions(+), 21 deletions(-) create mode 100644 tests/baselines/reference/keyRemappingKeyofResult2.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 406c91412be51..3d4f288f1293b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17637,7 +17637,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { * reduction in the constraintType) when possible. * @param noIndexSignatures Indicates if _string_ index signatures should be elided. (other index signatures are always reported) */ - function getIndexTypeForMappedType(type: MappedType, indexFlags: IndexFlags): Type { + function getIndexTypeForMappedType(type: MappedType, indexFlags: IndexFlags) { const typeParameter = getTypeParameterFromMappedType(type); const constraintType = getConstraintTypeFromMappedType(type); const nameType = getNameTypeFromMappedType(type.target as MappedType || type); @@ -17651,12 +17651,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // a circular definition. For this reason, we only eagerly manifest the keys if the constraint is non-generic. if (isGenericIndexType(constraintType)) { if (isMappedTypeWithKeyofConstraintDeclaration(type)) { - if (nameType) { - const modifiersIndex = getIndexType(getModifiersTypeFromMappedType(type), indexFlags, UnionReduction.None); - const mapper = makeUnaryTypeMapper(getTypeParameterFromMappedType(type), modifiersIndex); - const nameMapper = combineTypeMappers(type.mapper, mapper); - return instantiateType(nameType, nameMapper); - } // We have a generic index and a homomorphic mapping (but a distributive key remapping) - we need to defer // the whole `keyof whatever` for later since it's not safe to resolve the shape of modifier type. return getIndexTypeForGenericType(type, indexFlags); @@ -17745,13 +17739,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return !!(keyType.flags & include || keyType.flags & TypeFlags.Intersection && some((keyType as IntersectionType).types, t => isKeyTypeIncluded(t, include))); } - function getLiteralTypeFromProperties(type: Type, include: TypeFlags, includeOrigin: boolean, unionReduction = UnionReduction.Literal) { + function getLiteralTypeFromProperties(type: Type, include: TypeFlags, includeOrigin: boolean) { const origin = includeOrigin && (getObjectFlags(type) & (ObjectFlags.ClassOrInterface | ObjectFlags.Reference) || type.aliasSymbol) ? createOriginIndexType(type) : undefined; const propertyTypes = map(getPropertiesOfType(type), prop => getLiteralTypeFromProperty(prop, include)); const indexKeyTypes = map(getIndexInfosOfType(type), info => info !== enumNumberIndexInfo && isKeyTypeIncluded(info.keyType, include) ? info.keyType === stringType && include & TypeFlags.Number ? stringOrNumberType : info.keyType : neverType); - return getUnionType(concatenate(propertyTypes, indexKeyTypes), unionReduction, /*aliasSymbol*/ undefined, /*aliasTypeArguments*/ undefined, origin); + return getUnionType(concatenate(propertyTypes, indexKeyTypes), UnionReduction.Literal, /*aliasSymbol*/ undefined, /*aliasTypeArguments*/ undefined, origin); } function shouldDeferIndexType(type: Type, indexFlags = IndexFlags.None) { @@ -17762,16 +17756,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { type.flags & TypeFlags.Intersection && maybeTypeOfKind(type, TypeFlags.Instantiable) && some((type as IntersectionType).types, isEmptyAnonymousObjectType)); } - function getIndexType(type: Type, indexFlags = defaultIndexFlags, unionReduction?: UnionReduction): Type { + function getIndexType(type: Type, indexFlags = defaultIndexFlags): Type { type = getReducedType(type); return shouldDeferIndexType(type, indexFlags) ? getIndexTypeForGenericType(type as InstantiableType | UnionOrIntersectionType, indexFlags) : type.flags & TypeFlags.Union ? getIntersectionType(map((type as UnionType).types, t => getIndexType(t, indexFlags))) : - type.flags & TypeFlags.Intersection ? getUnionType(map((type as IntersectionType).types, t => getIndexType(t, indexFlags, unionReduction)), unionReduction) : + type.flags & TypeFlags.Intersection ? getUnionType(map((type as IntersectionType).types, t => getIndexType(t, indexFlags))) : getObjectFlags(type) & ObjectFlags.Mapped ? getIndexTypeForMappedType(type as MappedType, indexFlags) : type === wildcardType ? wildcardType : type.flags & TypeFlags.Unknown ? neverType : type.flags & (TypeFlags.Any | TypeFlags.Never) ? keyofConstraintType : - getLiteralTypeFromProperties(type, (indexFlags & IndexFlags.NoIndexSignatures ? TypeFlags.StringLiteral : TypeFlags.StringLike) | (indexFlags & IndexFlags.StringsOnly ? 0 : TypeFlags.NumberLike | TypeFlags.ESSymbolLike), indexFlags === defaultIndexFlags, unionReduction); + getLiteralTypeFromProperties(type, (indexFlags & IndexFlags.NoIndexSignatures ? TypeFlags.StringLiteral : TypeFlags.StringLike) | (indexFlags & IndexFlags.StringsOnly ? 0 : TypeFlags.NumberLike | TypeFlags.ESSymbolLike), indexFlags === defaultIndexFlags); } function getExtractStringType(type: Type) { diff --git a/tests/baselines/reference/keyRemappingKeyofResult.types b/tests/baselines/reference/keyRemappingKeyofResult.types index c061ca49d6702..01569ae0ddeed 100644 --- a/tests/baselines/reference/keyRemappingKeyofResult.types +++ b/tests/baselines/reference/keyRemappingKeyofResult.types @@ -156,19 +156,19 @@ function g() { // no string index signature, right? type Oops = keyof Remapped; ->Oops : unique symbol | "str" | (keyof T extends infer T_1 ? T_1 extends keyof T ? T_1 extends unknown ? {} extends Record ? never : T_1 : never : never : never) +>Oops : keyof { [K in keyof ({ [k: string]: any; str: any; [sym]: any; } & T) as K extends unknown ? {} extends Record ? never : K : never]: any; } let x: Oops; ->x : unique symbol | "str" | (keyof T extends infer T_1 ? T_1 extends keyof T ? T_1 extends unknown ? {} extends Record ? never : T_1 : never : never : never) +>x : keyof { [K in keyof ({ [k: string]: any; str: any; [sym]: any; } & T) as K extends unknown ? {} extends Record ? never : K : never]: any; } x = sym; >x = sym : unique symbol ->x : unique symbol | "str" | (keyof T extends infer T_1 ? T_1 extends keyof T ? T_1 extends unknown ? {} extends Record ? never : T_1 : never : never : never) +>x : keyof { [K in keyof ({ [k: string]: any; str: any; [sym]: any; } & T) as K extends unknown ? {} extends Record ? never : K : never]: any; } >sym : unique symbol x = "str"; >x = "str" : "str" ->x : unique symbol | "str" | (keyof T extends infer T_1 ? T_1 extends keyof T ? T_1 extends unknown ? {} extends Record ? never : T_1 : never : never : never) +>x : keyof { [K in keyof ({ [k: string]: any; str: any; [sym]: any; } & T) as K extends unknown ? {} extends Record ? never : K : never]: any; } >"str" : "str" } diff --git a/tests/baselines/reference/keyRemappingKeyofResult2.errors.txt b/tests/baselines/reference/keyRemappingKeyofResult2.errors.txt new file mode 100644 index 0000000000000..6ee25e23d479a --- /dev/null +++ b/tests/baselines/reference/keyRemappingKeyofResult2.errors.txt @@ -0,0 +1,51 @@ +keyRemappingKeyofResult2.ts(23,7): error TS2344: Type 'Values<{ [K in keyof TActors as K & string]: { src: K; logic: TActors[K]; }; }>' does not satisfy the constraint 'ProvidedActor'. + Types of property 'src' are incompatible. + Type 'keyof { [K in keyof TActors as K & string]: { src: K; logic: TActors[K]; }; }' is not assignable to type 'string'. + Type 'string | number | symbol' is not assignable to type 'string'. + Type 'number' is not assignable to type 'string'. + + +==== keyRemappingKeyofResult2.ts (1 errors) ==== + // https://github.com/microsoft/TypeScript/issues/56239 + + type Values = T[keyof T]; + + type ProvidedActor = { + src: string; + logic: unknown; + }; + + interface StateMachineConfig { + invoke: { + src: TActors["src"]; + }; + } + + declare function setup>(_: { + actors: { + [K in keyof TActors]: TActors[K]; + }; + }): { + createMachine: ( + config: StateMachineConfig< + Values<{ + ~~~~~~~~ + [K in keyof TActors as K & string]: { + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + src: K; + ~~~~~~~~~~~~~~~~~ + logic: TActors[K]; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + }; + ~~~~~~~~~~ + }> + ~~~~~~~~ +!!! error TS2344: Type 'Values<{ [K in keyof TActors as K & string]: { src: K; logic: TActors[K]; }; }>' does not satisfy the constraint 'ProvidedActor'. +!!! error TS2344: Types of property 'src' are incompatible. +!!! error TS2344: Type 'keyof { [K in keyof TActors as K & string]: { src: K; logic: TActors[K]; }; }' is not assignable to type 'string'. +!!! error TS2344: Type 'string | number | symbol' is not assignable to type 'string'. +!!! error TS2344: Type 'number' is not assignable to type 'string'. + >, + ) => void; + }; + \ No newline at end of file diff --git a/tests/baselines/reference/mappedTypeAsClauses.types b/tests/baselines/reference/mappedTypeAsClauses.types index bf85b85b1d5de..168e8bd6eaf84 100644 --- a/tests/baselines/reference/mappedTypeAsClauses.types +++ b/tests/baselines/reference/mappedTypeAsClauses.types @@ -267,16 +267,16 @@ type NameMap = { 'a': 'x', 'b': 'y', 'c': 'z' }; // Distributive, will be simplified type TS0 = keyof { [P in keyof T as keyof Record]: string }; ->TS0 : keyof T +>TS0 : keyof { [P in keyof T as P]: string; } type TS1 = keyof { [P in keyof T as Extract]: string }; ->TS1 : Extract +>TS1 : keyof { [P in keyof T as Extract]: string; } type TS2 = keyof { [P in keyof T as P & ('a' | 'b' | 'c')]: string }; ->TS2 : keyof T & ("a" | "b" | "c") +>TS2 : keyof { [P in keyof T as P & ("a" | "b" | "c")]: string; } type TS3 = keyof { [P in keyof T as Exclude]: string }; ->TS3 : Exclude +>TS3 : keyof { [P in keyof T as Exclude]: string; } type TS4 = keyof { [P in keyof T as NameMap[P & keyof NameMap]]: string }; >TS4 : keyof { [P in keyof T as NameMap[P & keyof NameMap]]: string; } @@ -285,7 +285,7 @@ type TS5 = keyof { [P in keyof T & keyof NameMap as NameMap[P]]: string }; >TS5 : keyof { [P in keyof T & keyof NameMap as NameMap[P]]: string; } type TS6 = keyof { [ K in keyof T as V & (K extends U ? K : never)]: string }; ->TS6 : V & (keyof T extends infer T_1 ? T_1 extends keyof T ? T_1 extends U ? T_1 : never : never : never) +>TS6 : keyof { [K in keyof T as V & (K extends U ? K : never)]: string; } // Non-distributive, won't be simplified From 585091e4a61ec84836e261f94534bccdadb6504d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Wed, 27 Dec 2023 01:12:19 +0100 Subject: [PATCH 4/5] Improve relationship checking --- src/compiler/checker.ts | 7 ++- .../keyRemappingKeyofResult2.errors.txt | 51 ------------------- 2 files changed, 6 insertions(+), 52 deletions(-) delete mode 100644 tests/baselines/reference/keyRemappingKeyofResult2.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3d4f288f1293b..cceba12331626 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -22565,7 +22565,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } else if (sourceFlags & TypeFlags.Index) { - if (result = isRelatedTo(keyofConstraintType, target, RecursionFlags.Source, reportErrors)) { + const indexType = source as IndexType; + let sourceKeys = keyofConstraintType; + if (isGenericMappedType(indexType.type)) { + sourceKeys = getMappedTypeNameTypeKind(indexType.type) !== MappedTypeNameTypeKind.None ? getNameTypeFromMappedType(indexType.type)! : getConstraintTypeFromMappedType(indexType.type); + } + if (result = isRelatedTo(sourceKeys, target, RecursionFlags.Source, reportErrors)) { return result; } } diff --git a/tests/baselines/reference/keyRemappingKeyofResult2.errors.txt b/tests/baselines/reference/keyRemappingKeyofResult2.errors.txt deleted file mode 100644 index 6ee25e23d479a..0000000000000 --- a/tests/baselines/reference/keyRemappingKeyofResult2.errors.txt +++ /dev/null @@ -1,51 +0,0 @@ -keyRemappingKeyofResult2.ts(23,7): error TS2344: Type 'Values<{ [K in keyof TActors as K & string]: { src: K; logic: TActors[K]; }; }>' does not satisfy the constraint 'ProvidedActor'. - Types of property 'src' are incompatible. - Type 'keyof { [K in keyof TActors as K & string]: { src: K; logic: TActors[K]; }; }' is not assignable to type 'string'. - Type 'string | number | symbol' is not assignable to type 'string'. - Type 'number' is not assignable to type 'string'. - - -==== keyRemappingKeyofResult2.ts (1 errors) ==== - // https://github.com/microsoft/TypeScript/issues/56239 - - type Values = T[keyof T]; - - type ProvidedActor = { - src: string; - logic: unknown; - }; - - interface StateMachineConfig { - invoke: { - src: TActors["src"]; - }; - } - - declare function setup>(_: { - actors: { - [K in keyof TActors]: TActors[K]; - }; - }): { - createMachine: ( - config: StateMachineConfig< - Values<{ - ~~~~~~~~ - [K in keyof TActors as K & string]: { - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - src: K; - ~~~~~~~~~~~~~~~~~ - logic: TActors[K]; - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - }; - ~~~~~~~~~~ - }> - ~~~~~~~~ -!!! error TS2344: Type 'Values<{ [K in keyof TActors as K & string]: { src: K; logic: TActors[K]; }; }>' does not satisfy the constraint 'ProvidedActor'. -!!! error TS2344: Types of property 'src' are incompatible. -!!! error TS2344: Type 'keyof { [K in keyof TActors as K & string]: { src: K; logic: TActors[K]; }; }' is not assignable to type 'string'. -!!! error TS2344: Type 'string | number | symbol' is not assignable to type 'string'. -!!! error TS2344: Type 'number' is not assignable to type 'string'. - >, - ) => void; - }; - \ No newline at end of file From ac236428c32dfd16d1feeacb224ee6c9a4257674 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 28 Dec 2023 10:01:50 +0100 Subject: [PATCH 5/5] Reuse the logic in `getGenericMappedTypeKeys` --- src/compiler/checker.ts | 60 +++++++------- .../keyRemappingKeyofResult.errors.txt | 80 +++++++++++++++++++ .../reference/keyRemappingKeyofResult.js | 2 + .../reference/keyRemappingKeyofResult.symbols | 3 + .../reference/keyRemappingKeyofResult.types | 5 ++ .../substitutionTypeForIndexedAccessType2.js | 3 - ...stitutionTypeForIndexedAccessType2.symbols | 3 - ...ubstitutionTypeForIndexedAccessType2.types | 3 - .../cases/compiler/keyRemappingKeyofResult.ts | 1 + .../substitutionTypeForIndexedAccessType2.ts | 3 - 10 files changed, 118 insertions(+), 45 deletions(-) create mode 100644 tests/baselines/reference/keyRemappingKeyofResult.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cceba12331626..77a68eca22f2f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13984,6 +13984,27 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return isTypeAssignableTo(nameType, getTypeParameterFromMappedType(type)) ? MappedTypeNameTypeKind.Filtering : MappedTypeNameTypeKind.Remapping; } + // generic mapped types that don't simplify or have a constraint still have a very simple set of keys - their nameType or constraintType. + // In many ways, this is a deferred version of what `getIndexTypeForMappedType` does to actually resolve the keys for _non_-generic types + function getGenericMappedTypeKeys(type: MappedType) { + const nameType = getNameTypeFromMappedType(type); + if (nameType && isMappedTypeWithKeyofConstraintDeclaration(type)) { + // we need to get the apparent mappings and union them with the generic mappings, since some properties may be + // missing from the `constraintType` which will otherwise be mapped in the object + const modifiersType = getApparentType(getModifiersTypeFromMappedType(type)); + const mappedKeys: Type[] = []; + forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType( + modifiersType, + TypeFlags.StringOrNumberLiteralOrUnique, + /*stringsOnly*/ false, + t => void mappedKeys.push(instantiateType(nameType, appendTypeMapping(type.mapper, getTypeParameterFromMappedType(type), t))), + ); + // We still need to include the non-apparent (and thus still generic) keys since when this gets used in comparisons the other side might include them + return getUnionType([...mappedKeys, nameType]); + } + return nameType || getConstraintTypeFromMappedType(type); + } + function resolveStructuredTypeMembers(type: StructuredType): ResolvedType { if (!(type as ResolvedType).members) { if (type.flags & TypeFlags.Object) { @@ -22352,34 +22373,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return Ternary.True; } } - else if (isGenericMappedType(targetType)) { - // generic mapped types that don't simplify or have a constraint still have a very simple set of keys we can compare against - // - their nameType or constraintType. - // In many ways, this comparison is a deferred version of what `getIndexTypeForMappedType` does to actually resolve the keys for _non_-generic types - - const nameType = getNameTypeFromMappedType(targetType); - const constraintType = getConstraintTypeFromMappedType(targetType); - let targetKeys; - if (nameType && isMappedTypeWithKeyofConstraintDeclaration(targetType)) { - // we need to get the apparent mappings and union them with the generic mappings, since some properties may be - // missing from the `constraintType` which will otherwise be mapped in the object - const modifiersType = getApparentType(getModifiersTypeFromMappedType(targetType)); - const mappedKeys: Type[] = []; - forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType( - modifiersType, - TypeFlags.StringOrNumberLiteralOrUnique, - /*stringsOnly*/ false, - t => void mappedKeys.push(instantiateType(nameType, appendTypeMapping(targetType.mapper, getTypeParameterFromMappedType(targetType), t))), - ); - // We still need to include the non-apparent (and thus still generic) keys in the target side of the comparison (in case they're in the source side) - targetKeys = getUnionType([...mappedKeys, nameType]); - } - else { - targetKeys = nameType || constraintType; - } - if (isRelatedTo(source, targetKeys, RecursionFlags.Target, reportErrors) === Ternary.True) { - return Ternary.True; - } + else if (isGenericMappedType(targetType) && isRelatedTo(source, getGenericMappedTypeKeys(targetType), RecursionFlags.Target, reportErrors) === Ternary.True) { + return Ternary.True; } } } @@ -22565,12 +22560,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } else if (sourceFlags & TypeFlags.Index) { - const indexType = source as IndexType; - let sourceKeys = keyofConstraintType; - if (isGenericMappedType(indexType.type)) { - sourceKeys = getMappedTypeNameTypeKind(indexType.type) !== MappedTypeNameTypeKind.None ? getNameTypeFromMappedType(indexType.type)! : getConstraintTypeFromMappedType(indexType.type); + const sourceType = (source as IndexType).type; + if (isGenericMappedType(sourceType) && isRelatedTo(getGenericMappedTypeKeys(sourceType), target, RecursionFlags.Source, reportErrors) === Ternary.True) { + return Ternary.True; } - if (result = isRelatedTo(sourceKeys, target, RecursionFlags.Source, reportErrors)) { + if (result = isRelatedTo(keyofConstraintType, target, RecursionFlags.Source, reportErrors)) { return result; } } diff --git a/tests/baselines/reference/keyRemappingKeyofResult.errors.txt b/tests/baselines/reference/keyRemappingKeyofResult.errors.txt new file mode 100644 index 0000000000000..c92c299465e8b --- /dev/null +++ b/tests/baselines/reference/keyRemappingKeyofResult.errors.txt @@ -0,0 +1,80 @@ +keyRemappingKeyofResult.ts(69,5): error TS2322: Type 'string' is not assignable to type 'keyof Remapped'. + Type '"whatever"' is not assignable to type 'unique symbol | "str" | DistributiveNonIndex'. + + +==== keyRemappingKeyofResult.ts (1 errors) ==== + const sym = Symbol("") + type Orig = { [k: string]: any, str: any, [sym]: any } + + type Okay = Exclude + // type Okay = string | number | typeof sym + + type Remapped = { [K in keyof Orig as {} extends Record ? never : K]: any } + /* type Remapped = { + str: any; + [sym]: any; + } */ + // no string index signature, right? + + type Oops = Exclude + declare let x: Oops; + x = sym; + x = "str"; + // type Oops = typeof sym <-- what happened to "str"? + + // equivalently, with an unresolved generic (no `exclude` shenanigans, since conditions won't execute): + function f() { + type Orig = { [k: string]: any, str: any, [sym]: any } & T; + + type Okay = keyof Orig; + let a: Okay; + a = "str"; + a = sym; + a = "whatever"; + // type Okay = string | number | typeof sym + + type Remapped = { [K in keyof Orig as {} extends Record ? never : K]: any } + /* type Remapped = { + str: any; + [sym]: any; + } */ + // no string index signature, right? + + type Oops = keyof Remapped; + let x: Oops; + x = sym; + x = "str"; + } + + // and another generic case with a _distributive_ mapping, to trigger a different branch in `getIndexType` + function g() { + type Orig = { [k: string]: any, str: any, [sym]: any } & T; + + type Okay = keyof Orig; + let a: Okay; + a = "str"; + a = sym; + a = "whatever"; + // type Okay = string | number | typeof sym + + type NonIndex = {} extends Record ? never : T; + type DistributiveNonIndex = T extends unknown ? NonIndex : never; + + type Remapped = { [K in keyof Orig as DistributiveNonIndex]: any } + /* type Remapped = { + str: any; + [sym]: any; + } */ + // no string index signature, right? + + type Oops = keyof Remapped; + let x: Oops; + x = sym; + x = "str"; + x = "whatever"; // error + ~ +!!! error TS2322: Type 'string' is not assignable to type 'keyof Remapped'. +!!! error TS2322: Type '"whatever"' is not assignable to type 'unique symbol | "str" | DistributiveNonIndex'. + } + + export {}; \ No newline at end of file diff --git a/tests/baselines/reference/keyRemappingKeyofResult.js b/tests/baselines/reference/keyRemappingKeyofResult.js index 3b51e02dc735e..9e4d1ea336c3f 100644 --- a/tests/baselines/reference/keyRemappingKeyofResult.js +++ b/tests/baselines/reference/keyRemappingKeyofResult.js @@ -69,6 +69,7 @@ function g() { let x: Oops; x = sym; x = "str"; + x = "whatever"; // error } export {}; @@ -97,5 +98,6 @@ function g() { let x; x = sym; x = "str"; + x = "whatever"; // error } export {}; diff --git a/tests/baselines/reference/keyRemappingKeyofResult.symbols b/tests/baselines/reference/keyRemappingKeyofResult.symbols index 722c000f268fa..3be5cf2460e84 100644 --- a/tests/baselines/reference/keyRemappingKeyofResult.symbols +++ b/tests/baselines/reference/keyRemappingKeyofResult.symbols @@ -190,6 +190,9 @@ function g() { x = "str"; >x : Symbol(x, Decl(keyRemappingKeyofResult.ts, 65, 7)) + + x = "whatever"; // error +>x : Symbol(x, Decl(keyRemappingKeyofResult.ts, 65, 7)) } export {}; diff --git a/tests/baselines/reference/keyRemappingKeyofResult.types b/tests/baselines/reference/keyRemappingKeyofResult.types index 01569ae0ddeed..f07883f69637d 100644 --- a/tests/baselines/reference/keyRemappingKeyofResult.types +++ b/tests/baselines/reference/keyRemappingKeyofResult.types @@ -170,6 +170,11 @@ function g() { >x = "str" : "str" >x : keyof { [K in keyof ({ [k: string]: any; str: any; [sym]: any; } & T) as K extends unknown ? {} extends Record ? never : K : never]: any; } >"str" : "str" + + x = "whatever"; // error +>x = "whatever" : "whatever" +>x : keyof { [K in keyof ({ [k: string]: any; str: any; [sym]: any; } & T) as K extends unknown ? {} extends Record ? never : K : never]: any; } +>"whatever" : "whatever" } export {}; diff --git a/tests/baselines/reference/substitutionTypeForIndexedAccessType2.js b/tests/baselines/reference/substitutionTypeForIndexedAccessType2.js index a73af0b42ab1f..ff8728571aa47 100644 --- a/tests/baselines/reference/substitutionTypeForIndexedAccessType2.js +++ b/tests/baselines/reference/substitutionTypeForIndexedAccessType2.js @@ -10,9 +10,6 @@ type Str = T type Bar = T extends Foo ? T['foo'] extends string - // Type 'T["foo"]' does not satisfy the constraint 'string'. - // Type 'string | undefined' is not assignable to type 'string'. - // Type 'undefined' is not assignable to type 'string'.(2344) ? Str : never : never diff --git a/tests/baselines/reference/substitutionTypeForIndexedAccessType2.symbols b/tests/baselines/reference/substitutionTypeForIndexedAccessType2.symbols index fc2995682b466..f9e6e9a2ce0fa 100644 --- a/tests/baselines/reference/substitutionTypeForIndexedAccessType2.symbols +++ b/tests/baselines/reference/substitutionTypeForIndexedAccessType2.symbols @@ -24,9 +24,6 @@ type Bar = ? T['foo'] extends string >T : Symbol(T, Decl(substitutionTypeForIndexedAccessType2.ts, 6, 9)) - // Type 'T["foo"]' does not satisfy the constraint 'string'. - // Type 'string | undefined' is not assignable to type 'string'. - // Type 'undefined' is not assignable to type 'string'.(2344) ? Str >Str : Symbol(Str, Decl(substitutionTypeForIndexedAccessType2.ts, 2, 1)) >T : Symbol(T, Decl(substitutionTypeForIndexedAccessType2.ts, 6, 9)) diff --git a/tests/baselines/reference/substitutionTypeForIndexedAccessType2.types b/tests/baselines/reference/substitutionTypeForIndexedAccessType2.types index d5319c245e977..8fbb5644bc878 100644 --- a/tests/baselines/reference/substitutionTypeForIndexedAccessType2.types +++ b/tests/baselines/reference/substitutionTypeForIndexedAccessType2.types @@ -14,9 +14,6 @@ type Bar = T extends Foo ? T['foo'] extends string - // Type 'T["foo"]' does not satisfy the constraint 'string'. - // Type 'string | undefined' is not assignable to type 'string'. - // Type 'undefined' is not assignable to type 'string'.(2344) ? Str : never : never diff --git a/tests/cases/compiler/keyRemappingKeyofResult.ts b/tests/cases/compiler/keyRemappingKeyofResult.ts index fcf3f835ac691..ba0b0939ae3cb 100644 --- a/tests/cases/compiler/keyRemappingKeyofResult.ts +++ b/tests/cases/compiler/keyRemappingKeyofResult.ts @@ -67,6 +67,7 @@ function g() { let x: Oops; x = sym; x = "str"; + x = "whatever"; // error } export {}; \ No newline at end of file diff --git a/tests/cases/compiler/substitutionTypeForIndexedAccessType2.ts b/tests/cases/compiler/substitutionTypeForIndexedAccessType2.ts index c687879efd463..97c0f6f538db5 100644 --- a/tests/cases/compiler/substitutionTypeForIndexedAccessType2.ts +++ b/tests/cases/compiler/substitutionTypeForIndexedAccessType2.ts @@ -7,9 +7,6 @@ type Str = T type Bar = T extends Foo ? T['foo'] extends string - // Type 'T["foo"]' does not satisfy the constraint 'string'. - // Type 'string | undefined' is not assignable to type 'string'. - // Type 'undefined' is not assignable to type 'string'.(2344) ? Str : never : never \ No newline at end of file