diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3d044fa5bf0f4..a33516c702fc3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -22895,14 +22895,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return result; } - function getApparentMappedTypeKeys(nameType: Type, targetType: MappedType) { - const modifiersType = getApparentType(getModifiersTypeFromMappedType(targetType)); + function getApparentMappedTypeKeys(nameType: Type, mappedType: MappedType) { + const modifiersType = getApparentType(getModifiersTypeFromMappedType(mappedType)); const mappedKeys: Type[] = []; forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType( modifiersType, TypeFlags.StringOrNumberLiteralOrUnique, /*stringsOnly*/ false, - t => void mappedKeys.push(instantiateType(nameType, appendTypeMapping(targetType.mapper, getTypeParameterFromMappedType(targetType), t))), + t => void mappedKeys.push(instantiateType(nameType, appendTypeMapping(mappedType.mapper, getTypeParameterFromMappedType(mappedType), t))), ); return getUnionType(mappedKeys); } @@ -23277,7 +23277,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // allow assignments of index types of identical (or similar enough) mapped types. // eg, `keyof {[X in keyof A]: Obj[X]}` should be assignable to `keyof {[Y in keyof A]: Tup[Y]}` because both map over the same set of keys (`keyof A`). // Without this source-side breakdown, a `keyof {[X in keyof A]: Obj[X]}` style type won't be assignable to anything except itself, which is much too strict. - const sourceMappedKeys = nameType && isMappedTypeWithKeyofConstraintDeclaration(mappedType) ? getApparentMappedTypeKeys(nameType, mappedType) : (nameType || getConstraintTypeFromMappedType(mappedType)); + let sourceMappedKeys: Type; + if (nameType && isMappedTypeWithKeyofConstraintDeclaration(mappedType)) { + sourceMappedKeys = getApparentMappedTypeKeys(nameType, mappedType); + if (sourceMappedKeys.flags & TypeFlags.Never) { + // modifiers type of mapped type is often `unknown`, `keyof unknown` is `never` and that's assignable to everything + // letting this through is too permissive so we use the apparent type of an index type here instead + sourceMappedKeys = stringNumberSymbolType; + } + } + else { + sourceMappedKeys = nameType || getConstraintTypeFromMappedType(mappedType); + } if (result = isRelatedTo(sourceMappedKeys, target, RecursionFlags.Source, reportErrors)) { return result; } diff --git a/tests/baselines/reference/keyRemappingKeyofResult(strict=false).errors.txt b/tests/baselines/reference/keyRemappingKeyofResult(strict=false).errors.txt new file mode 100644 index 0000000000000..a072e3be92b17 --- /dev/null +++ b/tests/baselines/reference/keyRemappingKeyofResult(strict=false).errors.txt @@ -0,0 +1,106 @@ +keyRemappingKeyofResult.ts(82,3): error TS2322: Type 'string' is not assignable to type 'Extract'. +keyRemappingKeyofResult.ts(90,3): error TS2322: Type 'string' is not assignable to type 'keyof { [P in keyof T as T[P] extends string ? P : never]: any; }'. + Type 'string' is not assignable to type 'T[P] extends string ? P : never'. + + +==== keyRemappingKeyofResult.ts (2 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"; + } + + // https://github.com/microsoft/TypeScript/issues/57827 + + type StringKeys = Extract< + keyof { + [P in keyof T as T[P] extends string ? P : never]: any; + }, + string + >; + + function test_57827(z: StringKeys) { + const f: string = z; + z = "foo"; // error + ~ +!!! error TS2322: Type 'string' is not assignable to type 'Extract'. + } + + type StringKeys2 = keyof { + [P in keyof T as T[P] extends string ? P : never]: any; + }; + + function h(z: StringKeys2) { + z = "foo"; // error + ~ +!!! error TS2322: Type 'string' is not assignable to type 'keyof { [P in keyof T as T[P] extends string ? P : never]: any; }'. +!!! error TS2322: Type 'string' is not assignable to type 'T[P] extends string ? P : never'. + const f: string = z; // ok + } + + export {}; + \ No newline at end of file diff --git a/tests/baselines/reference/keyRemappingKeyofResult.js b/tests/baselines/reference/keyRemappingKeyofResult(strict=false).js similarity index 80% rename from tests/baselines/reference/keyRemappingKeyofResult.js rename to tests/baselines/reference/keyRemappingKeyofResult(strict=false).js index 3b51e02dc735e..c46149d6c8d44 100644 --- a/tests/baselines/reference/keyRemappingKeyofResult.js +++ b/tests/baselines/reference/keyRemappingKeyofResult(strict=false).js @@ -23,21 +23,21 @@ x = "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; @@ -47,7 +47,7 @@ function f() { // 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"; @@ -57,21 +57,45 @@ function g() { 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"; } -export {}; +// https://github.com/microsoft/TypeScript/issues/57827 + +type StringKeys = Extract< + keyof { + [P in keyof T as T[P] extends string ? P : never]: any; + }, + string +>; + +function test_57827(z: StringKeys) { + const f: string = z; + z = "foo"; // error +} + +type StringKeys2 = keyof { + [P in keyof T as T[P] extends string ? P : never]: any; +}; + +function h(z: StringKeys2) { + z = "foo"; // error + const f: string = z; // ok +} + +export {}; + //// [keyRemappingKeyofResult.js] const sym = Symbol(""); @@ -98,4 +122,12 @@ function g() { x = sym; x = "str"; } +function test_57827(z) { + const f = z; + z = "foo"; // error +} +function h(z) { + z = "foo"; // error + const f = z; // ok +} export {}; diff --git a/tests/baselines/reference/keyRemappingKeyofResult.symbols b/tests/baselines/reference/keyRemappingKeyofResult(strict=false).symbols similarity index 75% rename from tests/baselines/reference/keyRemappingKeyofResult.symbols rename to tests/baselines/reference/keyRemappingKeyofResult(strict=false).symbols index 722c000f268fa..cea827e0f2f5c 100644 --- a/tests/baselines/reference/keyRemappingKeyofResult.symbols +++ b/tests/baselines/reference/keyRemappingKeyofResult(strict=false).symbols @@ -63,7 +63,7 @@ function f() { >[sym] : Symbol([sym], Decl(keyRemappingKeyofResult.ts, 21, 45)) >sym : Symbol(sym, Decl(keyRemappingKeyofResult.ts, 0, 5)) >T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 20, 11)) - + type Okay = keyof Orig; >Okay : Symbol(Okay, Decl(keyRemappingKeyofResult.ts, 21, 63)) >Orig : Symbol(Orig, Decl(keyRemappingKeyofResult.ts, 20, 17)) @@ -83,7 +83,7 @@ function f() { >a : Symbol(a, Decl(keyRemappingKeyofResult.ts, 24, 7)) // type Okay = string | number | typeof sym - + type Remapped = { [K in keyof Orig as {} extends Record ? never : K]: any } >Remapped : Symbol(Remapped, Decl(keyRemappingKeyofResult.ts, 27, 19)) >K : Symbol(K, Decl(keyRemappingKeyofResult.ts, 30, 23)) @@ -97,7 +97,7 @@ function f() { [sym]: any; } */ // no string index signature, right? - + type Oops = keyof Remapped; >Oops : Symbol(Oops, Decl(keyRemappingKeyofResult.ts, 30, 87)) >Remapped : Symbol(Remapped, Decl(keyRemappingKeyofResult.ts, 27, 19)) @@ -126,7 +126,7 @@ function g() { >[sym] : Symbol([sym], Decl(keyRemappingKeyofResult.ts, 45, 45)) >sym : Symbol(sym, Decl(keyRemappingKeyofResult.ts, 0, 5)) >T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 44, 11)) - + type Okay = keyof Orig; >Okay : Symbol(Okay, Decl(keyRemappingKeyofResult.ts, 45, 63)) >Orig : Symbol(Orig, Decl(keyRemappingKeyofResult.ts, 44, 17)) @@ -162,7 +162,7 @@ function g() { >T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 55, 30)) >NonIndex : Symbol(NonIndex, Decl(keyRemappingKeyofResult.ts, 51, 19)) >T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 55, 30)) - + type Remapped = { [K in keyof Orig as DistributiveNonIndex]: any } >Remapped : Symbol(Remapped, Decl(keyRemappingKeyofResult.ts, 55, 95)) >K : Symbol(K, Decl(keyRemappingKeyofResult.ts, 57, 23)) @@ -175,7 +175,7 @@ function g() { [sym]: any; } */ // no string index signature, right? - + type Oops = keyof Remapped; >Oops : Symbol(Oops, Decl(keyRemappingKeyofResult.ts, 57, 73)) >Remapped : Symbol(Remapped, Decl(keyRemappingKeyofResult.ts, 55, 95)) @@ -192,4 +192,67 @@ function g() { >x : Symbol(x, Decl(keyRemappingKeyofResult.ts, 65, 7)) } +// https://github.com/microsoft/TypeScript/issues/57827 + +type StringKeys = Extract< +>StringKeys : Symbol(StringKeys, Decl(keyRemappingKeyofResult.ts, 68, 1)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 72, 16)) +>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --)) + + keyof { + [P in keyof T as T[P] extends string ? P : never]: any; +>P : Symbol(P, Decl(keyRemappingKeyofResult.ts, 74, 5)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 72, 16)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 72, 16)) +>P : Symbol(P, Decl(keyRemappingKeyofResult.ts, 74, 5)) +>P : Symbol(P, Decl(keyRemappingKeyofResult.ts, 74, 5)) + + }, + string +>; + +function test_57827(z: StringKeys) { +>test_57827 : Symbol(test_57827, Decl(keyRemappingKeyofResult.ts, 77, 2)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 79, 20)) +>z : Symbol(z, Decl(keyRemappingKeyofResult.ts, 79, 23)) +>StringKeys : Symbol(StringKeys, Decl(keyRemappingKeyofResult.ts, 68, 1)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 79, 20)) + + const f: string = z; +>f : Symbol(f, Decl(keyRemappingKeyofResult.ts, 80, 7)) +>z : Symbol(z, Decl(keyRemappingKeyofResult.ts, 79, 23)) + + z = "foo"; // error +>z : Symbol(z, Decl(keyRemappingKeyofResult.ts, 79, 23)) +} + +type StringKeys2 = keyof { +>StringKeys2 : Symbol(StringKeys2, Decl(keyRemappingKeyofResult.ts, 82, 1)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 84, 17)) + + [P in keyof T as T[P] extends string ? P : never]: any; +>P : Symbol(P, Decl(keyRemappingKeyofResult.ts, 85, 3)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 84, 17)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 84, 17)) +>P : Symbol(P, Decl(keyRemappingKeyofResult.ts, 85, 3)) +>P : Symbol(P, Decl(keyRemappingKeyofResult.ts, 85, 3)) + +}; + +function h(z: StringKeys2) { +>h : Symbol(h, Decl(keyRemappingKeyofResult.ts, 86, 2)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 88, 11)) +>z : Symbol(z, Decl(keyRemappingKeyofResult.ts, 88, 14)) +>StringKeys2 : Symbol(StringKeys2, Decl(keyRemappingKeyofResult.ts, 82, 1)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 88, 11)) + + z = "foo"; // error +>z : Symbol(z, Decl(keyRemappingKeyofResult.ts, 88, 14)) + + const f: string = z; // ok +>f : Symbol(f, Decl(keyRemappingKeyofResult.ts, 90, 7)) +>z : Symbol(z, Decl(keyRemappingKeyofResult.ts, 88, 14)) +} + export {}; + diff --git a/tests/baselines/reference/keyRemappingKeyofResult.types b/tests/baselines/reference/keyRemappingKeyofResult(strict=false).types similarity index 78% rename from tests/baselines/reference/keyRemappingKeyofResult.types rename to tests/baselines/reference/keyRemappingKeyofResult(strict=false).types index 50949e385daa2..83fe33b0104a2 100644 --- a/tests/baselines/reference/keyRemappingKeyofResult.types +++ b/tests/baselines/reference/keyRemappingKeyofResult(strict=false).types @@ -17,7 +17,9 @@ type Orig = { [k: string]: any, str: any, [sym]: any } >k : string > : ^^^^^^ >str : any +> : ^^^ >[sym] : any +> : ^^^ >sym : unique symbol > : ^^^^^^^^^^^^^ @@ -74,10 +76,12 @@ function f() { >k : string > : ^^^^^^ >str : any +> : ^^^ >[sym] : any +> : ^^^ >sym : unique symbol > : ^^^^^^^^^^^^^ - + type Okay = keyof Orig; >Okay : string | number | unique symbol | keyof T > : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -111,7 +115,7 @@ function f() { > : ^^^^^^^^^^ // type Okay = string | number | typeof sym - + type Remapped = { [K in keyof Orig as {} extends Record ? never : K]: any } >Remapped : { [K in keyof ({ [k: string]: any; str: any; [sym]: any; } & T) as {} extends Record ? never : K]: any; } > : ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -121,7 +125,7 @@ function f() { [sym]: any; } */ // no string index signature, right? - + type Oops = keyof Remapped; >Oops : keyof { [K in keyof ({ [k: string]: any; str: any; [sym]: any; } & T) as {} extends Record ? never : K]: any; } > : ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -158,10 +162,12 @@ function g() { >k : string > : ^^^^^^ >str : any +> : ^^^ >[sym] : any +> : ^^^ >sym : unique symbol > : ^^^^^^^^^^^^^ - + type Okay = keyof Orig; >Okay : string | number | unique symbol | keyof T > : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -203,7 +209,7 @@ function g() { type DistributiveNonIndex = T extends unknown ? NonIndex : never; >DistributiveNonIndex : T extends unknown ? {} extends Record ? never : T : never > : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - + type Remapped = { [K in keyof Orig as DistributiveNonIndex]: any } >Remapped : { [K in keyof ({ [k: string]: any; str: any; [sym]: any; } & T) as K extends unknown ? {} extends Record ? never : K : never]: any; } > : ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -213,7 +219,7 @@ function g() { [sym]: any; } */ // 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; } > : ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -239,4 +245,66 @@ function g() { > : ^^^^^ } +// https://github.com/microsoft/TypeScript/issues/57827 + +type StringKeys = Extract< +>StringKeys : StringKeys +> : ^^^^^^^^^^^^^ + + keyof { + [P in keyof T as T[P] extends string ? P : never]: any; + }, + string +>; + +function test_57827(z: StringKeys) { +>test_57827 : (z: StringKeys) => void +> : ^ ^^ ^^ ^^^^^^^^^ +>z : Extract +> : ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + const f: string = z; +>f : string +> : ^^^^^^ +>z : Extract +> : ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + z = "foo"; // error +>z = "foo" : "foo" +> : ^^^^^ +>z : Extract +> : ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>"foo" : "foo" +> : ^^^^^ +} + +type StringKeys2 = keyof { +>StringKeys2 : keyof { [P in keyof T as T[P] extends string ? P : never]: any; } +> : ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + [P in keyof T as T[P] extends string ? P : never]: any; +}; + +function h(z: StringKeys2) { +>h : (z: StringKeys2) => void +> : ^ ^^ ^^ ^^^^^^^^^ +>z : keyof { [P in keyof T as T[P] extends string ? P : never]: any; } +> : ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + z = "foo"; // error +>z = "foo" : "foo" +> : ^^^^^ +>z : keyof { [P in keyof T as T[P] extends string ? P : never]: any; } +> : ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>"foo" : "foo" +> : ^^^^^ + + const f: string = z; // ok +>f : string +> : ^^^^^^ +>z : string +> : ^^^^^^ +} + export {}; + diff --git a/tests/baselines/reference/keyRemappingKeyofResult(strict=true).errors.txt b/tests/baselines/reference/keyRemappingKeyofResult(strict=true).errors.txt new file mode 100644 index 0000000000000..a072e3be92b17 --- /dev/null +++ b/tests/baselines/reference/keyRemappingKeyofResult(strict=true).errors.txt @@ -0,0 +1,106 @@ +keyRemappingKeyofResult.ts(82,3): error TS2322: Type 'string' is not assignable to type 'Extract'. +keyRemappingKeyofResult.ts(90,3): error TS2322: Type 'string' is not assignable to type 'keyof { [P in keyof T as T[P] extends string ? P : never]: any; }'. + Type 'string' is not assignable to type 'T[P] extends string ? P : never'. + + +==== keyRemappingKeyofResult.ts (2 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"; + } + + // https://github.com/microsoft/TypeScript/issues/57827 + + type StringKeys = Extract< + keyof { + [P in keyof T as T[P] extends string ? P : never]: any; + }, + string + >; + + function test_57827(z: StringKeys) { + const f: string = z; + z = "foo"; // error + ~ +!!! error TS2322: Type 'string' is not assignable to type 'Extract'. + } + + type StringKeys2 = keyof { + [P in keyof T as T[P] extends string ? P : never]: any; + }; + + function h(z: StringKeys2) { + z = "foo"; // error + ~ +!!! error TS2322: Type 'string' is not assignable to type 'keyof { [P in keyof T as T[P] extends string ? P : never]: any; }'. +!!! error TS2322: Type 'string' is not assignable to type 'T[P] extends string ? P : never'. + const f: string = z; // ok + } + + export {}; + \ No newline at end of file diff --git a/tests/baselines/reference/keyRemappingKeyofResult(strict=true).js b/tests/baselines/reference/keyRemappingKeyofResult(strict=true).js new file mode 100644 index 0000000000000..c46149d6c8d44 --- /dev/null +++ b/tests/baselines/reference/keyRemappingKeyofResult(strict=true).js @@ -0,0 +1,133 @@ +//// [tests/cases/compiler/keyRemappingKeyofResult.ts] //// + +//// [keyRemappingKeyofResult.ts] +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"; +} + +// https://github.com/microsoft/TypeScript/issues/57827 + +type StringKeys = Extract< + keyof { + [P in keyof T as T[P] extends string ? P : never]: any; + }, + string +>; + +function test_57827(z: StringKeys) { + const f: string = z; + z = "foo"; // error +} + +type StringKeys2 = keyof { + [P in keyof T as T[P] extends string ? P : never]: any; +}; + +function h(z: StringKeys2) { + z = "foo"; // error + const f: string = z; // ok +} + +export {}; + + +//// [keyRemappingKeyofResult.js] +const sym = Symbol(""); +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() { + let a; + a = "str"; + a = sym; + a = "whatever"; + let x; + x = sym; + x = "str"; +} +// and another generic case with a _distributive_ mapping, to trigger a different branch in `getIndexType` +function g() { + let a; + a = "str"; + a = sym; + a = "whatever"; + let x; + x = sym; + x = "str"; +} +function test_57827(z) { + const f = z; + z = "foo"; // error +} +function h(z) { + z = "foo"; // error + const f = z; // ok +} +export {}; diff --git a/tests/baselines/reference/keyRemappingKeyofResult(strict=true).symbols b/tests/baselines/reference/keyRemappingKeyofResult(strict=true).symbols new file mode 100644 index 0000000000000..cea827e0f2f5c --- /dev/null +++ b/tests/baselines/reference/keyRemappingKeyofResult(strict=true).symbols @@ -0,0 +1,258 @@ +//// [tests/cases/compiler/keyRemappingKeyofResult.ts] //// + +=== keyRemappingKeyofResult.ts === +const sym = Symbol("") +>sym : Symbol(sym, Decl(keyRemappingKeyofResult.ts, 0, 5)) +>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) + +type Orig = { [k: string]: any, str: any, [sym]: any } +>Orig : Symbol(Orig, Decl(keyRemappingKeyofResult.ts, 0, 22)) +>k : Symbol(k, Decl(keyRemappingKeyofResult.ts, 1, 15)) +>str : Symbol(str, Decl(keyRemappingKeyofResult.ts, 1, 31)) +>[sym] : Symbol([sym], Decl(keyRemappingKeyofResult.ts, 1, 41)) +>sym : Symbol(sym, Decl(keyRemappingKeyofResult.ts, 0, 5)) + +type Okay = Exclude +>Okay : Symbol(Okay, Decl(keyRemappingKeyofResult.ts, 1, 54)) +>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --)) +>Orig : Symbol(Orig, Decl(keyRemappingKeyofResult.ts, 0, 22)) + +// type Okay = string | number | typeof sym + +type Remapped = { [K in keyof Orig as {} extends Record ? never : K]: any } +>Remapped : Symbol(Remapped, Decl(keyRemappingKeyofResult.ts, 3, 38)) +>K : Symbol(K, Decl(keyRemappingKeyofResult.ts, 6, 19)) +>Orig : Symbol(Orig, Decl(keyRemappingKeyofResult.ts, 0, 22)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>K : Symbol(K, Decl(keyRemappingKeyofResult.ts, 6, 19)) +>K : Symbol(K, Decl(keyRemappingKeyofResult.ts, 6, 19)) + +/* type Remapped = { + str: any; + [sym]: any; +} */ +// no string index signature, right? + +type Oops = Exclude +>Oops : Symbol(Oops, Decl(keyRemappingKeyofResult.ts, 6, 83)) +>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --)) +>Remapped : Symbol(Remapped, Decl(keyRemappingKeyofResult.ts, 3, 38)) + +declare let x: Oops; +>x : Symbol(x, Decl(keyRemappingKeyofResult.ts, 14, 11)) +>Oops : Symbol(Oops, Decl(keyRemappingKeyofResult.ts, 6, 83)) + +x = sym; +>x : Symbol(x, Decl(keyRemappingKeyofResult.ts, 14, 11)) +>sym : Symbol(sym, Decl(keyRemappingKeyofResult.ts, 0, 5)) + +x = "str"; +>x : Symbol(x, Decl(keyRemappingKeyofResult.ts, 14, 11)) + +// type Oops = typeof sym <-- what happened to "str"? + +// equivalently, with an unresolved generic (no `exclude` shenanigans, since conditions won't execute): +function f() { +>f : Symbol(f, Decl(keyRemappingKeyofResult.ts, 16, 10)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 20, 11)) + + type Orig = { [k: string]: any, str: any, [sym]: any } & T; +>Orig : Symbol(Orig, Decl(keyRemappingKeyofResult.ts, 20, 17)) +>k : Symbol(k, Decl(keyRemappingKeyofResult.ts, 21, 19)) +>str : Symbol(str, Decl(keyRemappingKeyofResult.ts, 21, 35)) +>[sym] : Symbol([sym], Decl(keyRemappingKeyofResult.ts, 21, 45)) +>sym : Symbol(sym, Decl(keyRemappingKeyofResult.ts, 0, 5)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 20, 11)) + + type Okay = keyof Orig; +>Okay : Symbol(Okay, Decl(keyRemappingKeyofResult.ts, 21, 63)) +>Orig : Symbol(Orig, Decl(keyRemappingKeyofResult.ts, 20, 17)) + + let a: Okay; +>a : Symbol(a, Decl(keyRemappingKeyofResult.ts, 24, 7)) +>Okay : Symbol(Okay, Decl(keyRemappingKeyofResult.ts, 21, 63)) + + a = "str"; +>a : Symbol(a, Decl(keyRemappingKeyofResult.ts, 24, 7)) + + a = sym; +>a : Symbol(a, Decl(keyRemappingKeyofResult.ts, 24, 7)) +>sym : Symbol(sym, Decl(keyRemappingKeyofResult.ts, 0, 5)) + + a = "whatever"; +>a : Symbol(a, Decl(keyRemappingKeyofResult.ts, 24, 7)) + + // type Okay = string | number | typeof sym + + type Remapped = { [K in keyof Orig as {} extends Record ? never : K]: any } +>Remapped : Symbol(Remapped, Decl(keyRemappingKeyofResult.ts, 27, 19)) +>K : Symbol(K, Decl(keyRemappingKeyofResult.ts, 30, 23)) +>Orig : Symbol(Orig, Decl(keyRemappingKeyofResult.ts, 20, 17)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>K : Symbol(K, Decl(keyRemappingKeyofResult.ts, 30, 23)) +>K : Symbol(K, Decl(keyRemappingKeyofResult.ts, 30, 23)) + + /* type Remapped = { + str: any; + [sym]: any; + } */ + // no string index signature, right? + + type Oops = keyof Remapped; +>Oops : Symbol(Oops, Decl(keyRemappingKeyofResult.ts, 30, 87)) +>Remapped : Symbol(Remapped, Decl(keyRemappingKeyofResult.ts, 27, 19)) + + let x: Oops; +>x : Symbol(x, Decl(keyRemappingKeyofResult.ts, 38, 7)) +>Oops : Symbol(Oops, Decl(keyRemappingKeyofResult.ts, 30, 87)) + + x = sym; +>x : Symbol(x, Decl(keyRemappingKeyofResult.ts, 38, 7)) +>sym : Symbol(sym, Decl(keyRemappingKeyofResult.ts, 0, 5)) + + x = "str"; +>x : Symbol(x, Decl(keyRemappingKeyofResult.ts, 38, 7)) +} + +// and another generic case with a _distributive_ mapping, to trigger a different branch in `getIndexType` +function g() { +>g : Symbol(g, Decl(keyRemappingKeyofResult.ts, 41, 1)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 44, 11)) + + type Orig = { [k: string]: any, str: any, [sym]: any } & T; +>Orig : Symbol(Orig, Decl(keyRemappingKeyofResult.ts, 44, 17)) +>k : Symbol(k, Decl(keyRemappingKeyofResult.ts, 45, 19)) +>str : Symbol(str, Decl(keyRemappingKeyofResult.ts, 45, 35)) +>[sym] : Symbol([sym], Decl(keyRemappingKeyofResult.ts, 45, 45)) +>sym : Symbol(sym, Decl(keyRemappingKeyofResult.ts, 0, 5)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 44, 11)) + + type Okay = keyof Orig; +>Okay : Symbol(Okay, Decl(keyRemappingKeyofResult.ts, 45, 63)) +>Orig : Symbol(Orig, Decl(keyRemappingKeyofResult.ts, 44, 17)) + + let a: Okay; +>a : Symbol(a, Decl(keyRemappingKeyofResult.ts, 48, 7)) +>Okay : Symbol(Okay, Decl(keyRemappingKeyofResult.ts, 45, 63)) + + a = "str"; +>a : Symbol(a, Decl(keyRemappingKeyofResult.ts, 48, 7)) + + a = sym; +>a : Symbol(a, Decl(keyRemappingKeyofResult.ts, 48, 7)) +>sym : Symbol(sym, Decl(keyRemappingKeyofResult.ts, 0, 5)) + + a = "whatever"; +>a : Symbol(a, Decl(keyRemappingKeyofResult.ts, 48, 7)) + + // type Okay = string | number | typeof sym + + type NonIndex = {} extends Record ? never : T; +>NonIndex : Symbol(NonIndex, Decl(keyRemappingKeyofResult.ts, 51, 19)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 54, 18)) +>PropertyKey : Symbol(PropertyKey, Decl(lib.es5.d.ts, --, --)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 54, 18)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 54, 18)) + + type DistributiveNonIndex = T extends unknown ? NonIndex : never; +>DistributiveNonIndex : Symbol(DistributiveNonIndex, Decl(keyRemappingKeyofResult.ts, 54, 81)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 55, 30)) +>PropertyKey : Symbol(PropertyKey, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 55, 30)) +>NonIndex : Symbol(NonIndex, Decl(keyRemappingKeyofResult.ts, 51, 19)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 55, 30)) + + type Remapped = { [K in keyof Orig as DistributiveNonIndex]: any } +>Remapped : Symbol(Remapped, Decl(keyRemappingKeyofResult.ts, 55, 95)) +>K : Symbol(K, Decl(keyRemappingKeyofResult.ts, 57, 23)) +>Orig : Symbol(Orig, Decl(keyRemappingKeyofResult.ts, 44, 17)) +>DistributiveNonIndex : Symbol(DistributiveNonIndex, Decl(keyRemappingKeyofResult.ts, 54, 81)) +>K : Symbol(K, Decl(keyRemappingKeyofResult.ts, 57, 23)) + + /* type Remapped = { + str: any; + [sym]: any; + } */ + // no string index signature, right? + + type Oops = keyof Remapped; +>Oops : Symbol(Oops, Decl(keyRemappingKeyofResult.ts, 57, 73)) +>Remapped : Symbol(Remapped, Decl(keyRemappingKeyofResult.ts, 55, 95)) + + let x: Oops; +>x : Symbol(x, Decl(keyRemappingKeyofResult.ts, 65, 7)) +>Oops : Symbol(Oops, Decl(keyRemappingKeyofResult.ts, 57, 73)) + + x = sym; +>x : Symbol(x, Decl(keyRemappingKeyofResult.ts, 65, 7)) +>sym : Symbol(sym, Decl(keyRemappingKeyofResult.ts, 0, 5)) + + x = "str"; +>x : Symbol(x, Decl(keyRemappingKeyofResult.ts, 65, 7)) +} + +// https://github.com/microsoft/TypeScript/issues/57827 + +type StringKeys = Extract< +>StringKeys : Symbol(StringKeys, Decl(keyRemappingKeyofResult.ts, 68, 1)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 72, 16)) +>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --)) + + keyof { + [P in keyof T as T[P] extends string ? P : never]: any; +>P : Symbol(P, Decl(keyRemappingKeyofResult.ts, 74, 5)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 72, 16)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 72, 16)) +>P : Symbol(P, Decl(keyRemappingKeyofResult.ts, 74, 5)) +>P : Symbol(P, Decl(keyRemappingKeyofResult.ts, 74, 5)) + + }, + string +>; + +function test_57827(z: StringKeys) { +>test_57827 : Symbol(test_57827, Decl(keyRemappingKeyofResult.ts, 77, 2)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 79, 20)) +>z : Symbol(z, Decl(keyRemappingKeyofResult.ts, 79, 23)) +>StringKeys : Symbol(StringKeys, Decl(keyRemappingKeyofResult.ts, 68, 1)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 79, 20)) + + const f: string = z; +>f : Symbol(f, Decl(keyRemappingKeyofResult.ts, 80, 7)) +>z : Symbol(z, Decl(keyRemappingKeyofResult.ts, 79, 23)) + + z = "foo"; // error +>z : Symbol(z, Decl(keyRemappingKeyofResult.ts, 79, 23)) +} + +type StringKeys2 = keyof { +>StringKeys2 : Symbol(StringKeys2, Decl(keyRemappingKeyofResult.ts, 82, 1)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 84, 17)) + + [P in keyof T as T[P] extends string ? P : never]: any; +>P : Symbol(P, Decl(keyRemappingKeyofResult.ts, 85, 3)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 84, 17)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 84, 17)) +>P : Symbol(P, Decl(keyRemappingKeyofResult.ts, 85, 3)) +>P : Symbol(P, Decl(keyRemappingKeyofResult.ts, 85, 3)) + +}; + +function h(z: StringKeys2) { +>h : Symbol(h, Decl(keyRemappingKeyofResult.ts, 86, 2)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 88, 11)) +>z : Symbol(z, Decl(keyRemappingKeyofResult.ts, 88, 14)) +>StringKeys2 : Symbol(StringKeys2, Decl(keyRemappingKeyofResult.ts, 82, 1)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 88, 11)) + + z = "foo"; // error +>z : Symbol(z, Decl(keyRemappingKeyofResult.ts, 88, 14)) + + const f: string = z; // ok +>f : Symbol(f, Decl(keyRemappingKeyofResult.ts, 90, 7)) +>z : Symbol(z, Decl(keyRemappingKeyofResult.ts, 88, 14)) +} + +export {}; + diff --git a/tests/baselines/reference/keyRemappingKeyofResult(strict=true).types b/tests/baselines/reference/keyRemappingKeyofResult(strict=true).types new file mode 100644 index 0000000000000..83fe33b0104a2 --- /dev/null +++ b/tests/baselines/reference/keyRemappingKeyofResult(strict=true).types @@ -0,0 +1,310 @@ +//// [tests/cases/compiler/keyRemappingKeyofResult.ts] //// + +=== keyRemappingKeyofResult.ts === +const sym = Symbol("") +>sym : unique symbol +> : ^^^^^^^^^^^^^ +>Symbol("") : unique symbol +> : ^^^^^^^^^^^^^ +>Symbol : SymbolConstructor +> : ^^^^^^^^^^^^^^^^^ +>"" : "" +> : ^^ + +type Orig = { [k: string]: any, str: any, [sym]: any } +>Orig : Orig +> : ^^^^ +>k : string +> : ^^^^^^ +>str : any +> : ^^^ +>[sym] : any +> : ^^^ +>sym : unique symbol +> : ^^^^^^^^^^^^^ + +type Okay = Exclude +>Okay : Okay +> : ^^^^ + +// type Okay = string | number | typeof sym + +type Remapped = { [K in keyof Orig as {} extends Record ? never : K]: any } +>Remapped : Remapped +> : ^^^^^^^^ + +/* type Remapped = { + str: any; + [sym]: any; +} */ +// no string index signature, right? + +type Oops = Exclude +>Oops : Oops +> : ^^^^ + +declare let x: Oops; +>x : Oops +> : ^^^^ + +x = sym; +>x = sym : unique symbol +> : ^^^^^^^^^^^^^ +>x : Oops +> : ^^^^ +>sym : unique symbol +> : ^^^^^^^^^^^^^ + +x = "str"; +>x = "str" : "str" +> : ^^^^^ +>x : Oops +> : ^^^^ +>"str" : "str" +> : ^^^^^ + +// type Oops = typeof sym <-- what happened to "str"? + +// equivalently, with an unresolved generic (no `exclude` shenanigans, since conditions won't execute): +function f() { +>f : () => void +> : ^ ^^^^^^^^^^^ + + type Orig = { [k: string]: any, str: any, [sym]: any } & T; +>Orig : { [k: string]: any; str: any; [sym]: any; } & T +> : ^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^ +>k : string +> : ^^^^^^ +>str : any +> : ^^^ +>[sym] : any +> : ^^^ +>sym : unique symbol +> : ^^^^^^^^^^^^^ + + type Okay = keyof Orig; +>Okay : string | number | unique symbol | keyof T +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + let a: Okay; +>a : string | number | unique symbol | keyof T +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + a = "str"; +>a = "str" : "str" +> : ^^^^^ +>a : string | number | unique symbol | keyof T +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>"str" : "str" +> : ^^^^^ + + a = sym; +>a = sym : unique symbol +> : ^^^^^^^^^^^^^ +>a : string | number | unique symbol | keyof T +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>sym : unique symbol +> : ^^^^^^^^^^^^^ + + a = "whatever"; +>a = "whatever" : "whatever" +> : ^^^^^^^^^^ +>a : string | number | unique symbol | keyof T +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>"whatever" : "whatever" +> : ^^^^^^^^^^ + + // type Okay = string | number | typeof sym + + type Remapped = { [K in keyof Orig as {} extends Record ? never : K]: any } +>Remapped : { [K in keyof ({ [k: string]: any; str: any; [sym]: any; } & T) as {} extends Record ? never : K]: any; } +> : ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + /* type Remapped = { + str: any; + [sym]: any; + } */ + // no string index signature, right? + + type Oops = keyof Remapped; +>Oops : keyof { [K in keyof ({ [k: string]: any; str: any; [sym]: any; } & T) as {} extends Record ? never : K]: any; } +> : ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + let x: Oops; +>x : keyof { [K in keyof ({ [k: string]: any; str: any; [sym]: any; } & T) as {} extends Record ? never : K]: any; } +> : ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + x = sym; +>x = sym : unique symbol +> : ^^^^^^^^^^^^^ +>x : keyof { [K in keyof ({ [k: string]: any; str: any; [sym]: any; } & T) as {} extends Record ? never : K]: any; } +> : ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>sym : unique symbol +> : ^^^^^^^^^^^^^ + + x = "str"; +>x = "str" : "str" +> : ^^^^^ +>x : keyof { [K in keyof ({ [k: string]: any; str: any; [sym]: any; } & T) as {} extends Record ? never : K]: any; } +> : ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>"str" : "str" +> : ^^^^^ +} + +// and another generic case with a _distributive_ mapping, to trigger a different branch in `getIndexType` +function g() { +>g : () => void +> : ^ ^^^^^^^^^^^ + + type Orig = { [k: string]: any, str: any, [sym]: any } & T; +>Orig : { [k: string]: any; str: any; [sym]: any; } & T +> : ^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^ +>k : string +> : ^^^^^^ +>str : any +> : ^^^ +>[sym] : any +> : ^^^ +>sym : unique symbol +> : ^^^^^^^^^^^^^ + + type Okay = keyof Orig; +>Okay : string | number | unique symbol | keyof T +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + let a: Okay; +>a : string | number | unique symbol | keyof T +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + a = "str"; +>a = "str" : "str" +> : ^^^^^ +>a : string | number | unique symbol | keyof T +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>"str" : "str" +> : ^^^^^ + + a = sym; +>a = sym : unique symbol +> : ^^^^^^^^^^^^^ +>a : string | number | unique symbol | keyof T +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>sym : unique symbol +> : ^^^^^^^^^^^^^ + + a = "whatever"; +>a = "whatever" : "whatever" +> : ^^^^^^^^^^ +>a : string | number | unique symbol | keyof T +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>"whatever" : "whatever" +> : ^^^^^^^^^^ + + // type Okay = string | number | typeof sym + + type NonIndex = {} extends Record ? never : T; +>NonIndex : {} extends Record ? never : T +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + type DistributiveNonIndex = T extends unknown ? NonIndex : never; +>DistributiveNonIndex : T extends unknown ? {} extends Record ? never : T : never +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + type Remapped = { [K in keyof Orig as DistributiveNonIndex]: any } +>Remapped : { [K in keyof ({ [k: string]: any; str: any; [sym]: any; } & T) as K extends unknown ? {} extends Record ? never : K : never]: any; } +> : ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + /* type Remapped = { + str: any; + [sym]: any; + } */ + // 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; } +> : ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + 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 = 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; } +> : ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>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; } +> : ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>"str" : "str" +> : ^^^^^ +} + +// https://github.com/microsoft/TypeScript/issues/57827 + +type StringKeys = Extract< +>StringKeys : StringKeys +> : ^^^^^^^^^^^^^ + + keyof { + [P in keyof T as T[P] extends string ? P : never]: any; + }, + string +>; + +function test_57827(z: StringKeys) { +>test_57827 : (z: StringKeys) => void +> : ^ ^^ ^^ ^^^^^^^^^ +>z : Extract +> : ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + const f: string = z; +>f : string +> : ^^^^^^ +>z : Extract +> : ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + z = "foo"; // error +>z = "foo" : "foo" +> : ^^^^^ +>z : Extract +> : ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>"foo" : "foo" +> : ^^^^^ +} + +type StringKeys2 = keyof { +>StringKeys2 : keyof { [P in keyof T as T[P] extends string ? P : never]: any; } +> : ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + [P in keyof T as T[P] extends string ? P : never]: any; +}; + +function h(z: StringKeys2) { +>h : (z: StringKeys2) => void +> : ^ ^^ ^^ ^^^^^^^^^ +>z : keyof { [P in keyof T as T[P] extends string ? P : never]: any; } +> : ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + z = "foo"; // error +>z = "foo" : "foo" +> : ^^^^^ +>z : keyof { [P in keyof T as T[P] extends string ? P : never]: any; } +> : ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>"foo" : "foo" +> : ^^^^^ + + const f: string = z; // ok +>f : string +> : ^^^^^^ +>z : string +> : ^^^^^^ +} + +export {}; + diff --git a/tests/cases/compiler/keyRemappingKeyofResult.ts b/tests/cases/compiler/keyRemappingKeyofResult.ts index fcf3f835ac691..2cec329384b29 100644 --- a/tests/cases/compiler/keyRemappingKeyofResult.ts +++ b/tests/cases/compiler/keyRemappingKeyofResult.ts @@ -1,3 +1,4 @@ +// @strict: true, false // @target: es6 const sym = Symbol("") type Orig = { [k: string]: any, str: any, [sym]: any } @@ -21,21 +22,21 @@ x = "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; @@ -45,7 +46,7 @@ function f() { // 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"; @@ -55,18 +56,41 @@ function g() { 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"; } -export {}; \ No newline at end of file +// https://github.com/microsoft/TypeScript/issues/57827 + +type StringKeys = Extract< + keyof { + [P in keyof T as T[P] extends string ? P : never]: any; + }, + string +>; + +function test_57827(z: StringKeys) { + const f: string = z; + z = "foo"; // error +} + +type StringKeys2 = keyof { + [P in keyof T as T[P] extends string ? P : never]: any; +}; + +function h(z: StringKeys2) { + z = "foo"; // error + const f: string = z; // ok +} + +export {};