diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3d044fa5bf0f4..75fd291a681f9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14371,6 +14371,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) { 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 50949e385daa2..ce371fb30e5de 100644 --- a/tests/baselines/reference/keyRemappingKeyofResult.types +++ b/tests/baselines/reference/keyRemappingKeyofResult.types @@ -17,7 +17,9 @@ type Orig = { [k: string]: any, str: any, [sym]: any } >k : string > : ^^^^^^ >str : any +> : ^^^ >[sym] : any +> : ^^^ >sym : unique symbol > : ^^^^^^^^^^^^^ @@ -74,7 +76,9 @@ function f() { >k : string > : ^^^^^^ >str : any +> : ^^^ >[sym] : any +> : ^^^ >sym : unique symbol > : ^^^^^^^^^^^^^ @@ -158,7 +162,9 @@ function g() { >k : string > : ^^^^^^ >str : any +> : ^^^ >[sym] : any +> : ^^^ >sym : unique symbol > : ^^^^^^^^^^^^^ @@ -237,6 +243,14 @@ function g() { > : ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >"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/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/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..b65ebbf5b6a0e --- /dev/null +++ b/tests/baselines/reference/keyRemappingKeyofResult2.types @@ -0,0 +1,72 @@ +//// [tests/cases/compiler/keyRemappingKeyofResult2.ts] //// + +=== keyRemappingKeyofResult2.ts === +// https://github.com/microsoft/TypeScript/issues/56239 + +type Values = T[keyof T]; +>Values : Values +> : ^^^^^^^^^ + +type ProvidedActor = { +>ProvidedActor : ProvidedActor +> : ^^^^^^^^^^^^^ + + 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/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 4567d59489a15..d303e54936727 100644 --- a/tests/baselines/reference/substitutionTypeForIndexedAccessType2.types +++ b/tests/baselines/reference/substitutionTypeForIndexedAccessType2.types @@ -17,9 +17,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/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; +}; 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