diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 390c843b0c968..838d9efdd632e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23464,16 +23464,22 @@ 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, forSource: boolean) { + 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); + const apparentKeys = getUnionType(mappedKeys); + if (forSource && apparentKeys.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 + return stringNumberSymbolType; + } + return apparentKeys; } function structuredTypeRelatedToWorker(source: Type, target: Type, reportErrors: boolean, intersectionState: IntersectionState, saveErrorInfo: ReturnType): Ternary { @@ -23657,7 +23663,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { 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 mappedKeys = getApparentMappedTypeKeys(nameType, targetType); + const mappedKeys = getApparentMappedTypeKeys(nameType, targetType, /*forSource*/ false); // 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]); } @@ -23864,7 +23870,7 @@ 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)); + const sourceMappedKeys = nameType && isMappedTypeWithKeyofConstraintDeclaration(mappedType) ? getApparentMappedTypeKeys(nameType, mappedType, /*forSource*/ true) : (nameType || getConstraintTypeFromMappedType(mappedType)); if (result = isRelatedTo(sourceMappedKeys, target, RecursionFlags.Source, reportErrors)) { return result; } diff --git a/tests/baselines/reference/keyRemappingKeyofResult.errors.txt b/tests/baselines/reference/keyRemappingKeyofResult(strict=false).errors.txt similarity index 62% rename from tests/baselines/reference/keyRemappingKeyofResult.errors.txt rename to tests/baselines/reference/keyRemappingKeyofResult(strict=false).errors.txt index c92c299465e8b..0a1328441d721 100644 --- a/tests/baselines/reference/keyRemappingKeyofResult.errors.txt +++ b/tests/baselines/reference/keyRemappingKeyofResult(strict=false).errors.txt @@ -1,8 +1,11 @@ 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(83,3): error TS2322: Type 'string' is not assignable to type 'Extract'. +keyRemappingKeyofResult.ts(91,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 (1 errors) ==== +==== keyRemappingKeyofResult.ts (3 errors) ==== const sym = Symbol("") type Orig = { [k: string]: any, str: any, [sym]: any } @@ -25,21 +28,21 @@ keyRemappingKeyofResult.ts(69,5): error TS2322: Type 'string' is not assignable // 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; @@ -49,7 +52,7 @@ keyRemappingKeyofResult.ts(69,5): error TS2322: Type 'string' is not assignable // 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"; @@ -59,14 +62,14 @@ keyRemappingKeyofResult.ts(69,5): error TS2322: Type 'string' is not assignable 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; @@ -77,4 +80,33 @@ keyRemappingKeyofResult.ts(69,5): error TS2322: Type 'string' is not assignable !!! error TS2322: Type '"whatever"' is not assignable to type 'unique symbol | "str" | DistributiveNonIndex'. } - 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 + ~ +!!! 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 9e4d1ea336c3f..1d6bf38b7c271 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,14 +57,14 @@ 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; @@ -72,7 +72,31 @@ function g() { x = "whatever"; // error } -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(""); @@ -100,4 +124,12 @@ function g() { x = "str"; x = "whatever"; // error } +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 3be5cf2460e84..d262ff3c96d2d 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)) @@ -195,4 +195,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, 69, 1)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 73, 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, 75, 5)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 73, 16)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 73, 16)) +>P : Symbol(P, Decl(keyRemappingKeyofResult.ts, 75, 5)) +>P : Symbol(P, Decl(keyRemappingKeyofResult.ts, 75, 5)) + + }, + string +>; + +function test_57827(z: StringKeys) { +>test_57827 : Symbol(test_57827, Decl(keyRemappingKeyofResult.ts, 78, 2)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 80, 20)) +>z : Symbol(z, Decl(keyRemappingKeyofResult.ts, 80, 23)) +>StringKeys : Symbol(StringKeys, Decl(keyRemappingKeyofResult.ts, 69, 1)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 80, 20)) + + const f: string = z; +>f : Symbol(f, Decl(keyRemappingKeyofResult.ts, 81, 7)) +>z : Symbol(z, Decl(keyRemappingKeyofResult.ts, 80, 23)) + + z = "foo"; // error +>z : Symbol(z, Decl(keyRemappingKeyofResult.ts, 80, 23)) +} + +type StringKeys2 = keyof { +>StringKeys2 : Symbol(StringKeys2, Decl(keyRemappingKeyofResult.ts, 83, 1)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 85, 17)) + + [P in keyof T as T[P] extends string ? P : never]: any; +>P : Symbol(P, Decl(keyRemappingKeyofResult.ts, 86, 3)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 85, 17)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 85, 17)) +>P : Symbol(P, Decl(keyRemappingKeyofResult.ts, 86, 3)) +>P : Symbol(P, Decl(keyRemappingKeyofResult.ts, 86, 3)) + +}; + +function h(z: StringKeys2) { +>h : Symbol(h, Decl(keyRemappingKeyofResult.ts, 87, 2)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 89, 11)) +>z : Symbol(z, Decl(keyRemappingKeyofResult.ts, 89, 14)) +>StringKeys2 : Symbol(StringKeys2, Decl(keyRemappingKeyofResult.ts, 83, 1)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 89, 11)) + + z = "foo"; // error +>z : Symbol(z, Decl(keyRemappingKeyofResult.ts, 89, 14)) + + const f: string = z; // ok +>f : Symbol(f, Decl(keyRemappingKeyofResult.ts, 91, 7)) +>z : Symbol(z, Decl(keyRemappingKeyofResult.ts, 89, 14)) +} + export {}; + diff --git a/tests/baselines/reference/keyRemappingKeyofResult.types b/tests/baselines/reference/keyRemappingKeyofResult(strict=false).types similarity index 79% rename from tests/baselines/reference/keyRemappingKeyofResult.types rename to tests/baselines/reference/keyRemappingKeyofResult(strict=false).types index ce371fb30e5de..ebdf7971f7685 100644 --- a/tests/baselines/reference/keyRemappingKeyofResult.types +++ b/tests/baselines/reference/keyRemappingKeyofResult(strict=false).types @@ -81,7 +81,7 @@ function f() { > : ^^^ >sym : unique symbol > : ^^^^^^^^^^^^^ - + type Okay = keyof Orig; >Okay : string | number | unique symbol | keyof T > : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -115,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; } > : ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -125,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; } > : ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -167,7 +167,7 @@ function g() { > : ^^^ >sym : unique symbol > : ^^^^^^^^^^^^^ - + type Okay = keyof Orig; >Okay : string | number | unique symbol | keyof T > : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -209,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; } > : ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -219,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; } > : ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -253,4 +253,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..0a1328441d721 --- /dev/null +++ b/tests/baselines/reference/keyRemappingKeyofResult(strict=true).errors.txt @@ -0,0 +1,112 @@ +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(83,3): error TS2322: Type 'string' is not assignable to type 'Extract'. +keyRemappingKeyofResult.ts(91,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 (3 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'. + } + + // 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..1d6bf38b7c271 --- /dev/null +++ b/tests/baselines/reference/keyRemappingKeyofResult(strict=true).js @@ -0,0 +1,135 @@ +//// [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"; + x = "whatever"; // error +} + +// 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"; + x = "whatever"; // error +} +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..d262ff3c96d2d --- /dev/null +++ b/tests/baselines/reference/keyRemappingKeyofResult(strict=true).symbols @@ -0,0 +1,261 @@ +//// [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)) + + x = "whatever"; // error +>x : Symbol(x, Decl(keyRemappingKeyofResult.ts, 65, 7)) +} + +// https://github.com/microsoft/TypeScript/issues/57827 + +type StringKeys = Extract< +>StringKeys : Symbol(StringKeys, Decl(keyRemappingKeyofResult.ts, 69, 1)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 73, 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, 75, 5)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 73, 16)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 73, 16)) +>P : Symbol(P, Decl(keyRemappingKeyofResult.ts, 75, 5)) +>P : Symbol(P, Decl(keyRemappingKeyofResult.ts, 75, 5)) + + }, + string +>; + +function test_57827(z: StringKeys) { +>test_57827 : Symbol(test_57827, Decl(keyRemappingKeyofResult.ts, 78, 2)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 80, 20)) +>z : Symbol(z, Decl(keyRemappingKeyofResult.ts, 80, 23)) +>StringKeys : Symbol(StringKeys, Decl(keyRemappingKeyofResult.ts, 69, 1)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 80, 20)) + + const f: string = z; +>f : Symbol(f, Decl(keyRemappingKeyofResult.ts, 81, 7)) +>z : Symbol(z, Decl(keyRemappingKeyofResult.ts, 80, 23)) + + z = "foo"; // error +>z : Symbol(z, Decl(keyRemappingKeyofResult.ts, 80, 23)) +} + +type StringKeys2 = keyof { +>StringKeys2 : Symbol(StringKeys2, Decl(keyRemappingKeyofResult.ts, 83, 1)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 85, 17)) + + [P in keyof T as T[P] extends string ? P : never]: any; +>P : Symbol(P, Decl(keyRemappingKeyofResult.ts, 86, 3)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 85, 17)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 85, 17)) +>P : Symbol(P, Decl(keyRemappingKeyofResult.ts, 86, 3)) +>P : Symbol(P, Decl(keyRemappingKeyofResult.ts, 86, 3)) + +}; + +function h(z: StringKeys2) { +>h : Symbol(h, Decl(keyRemappingKeyofResult.ts, 87, 2)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 89, 11)) +>z : Symbol(z, Decl(keyRemappingKeyofResult.ts, 89, 14)) +>StringKeys2 : Symbol(StringKeys2, Decl(keyRemappingKeyofResult.ts, 83, 1)) +>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 89, 11)) + + z = "foo"; // error +>z : Symbol(z, Decl(keyRemappingKeyofResult.ts, 89, 14)) + + const f: string = z; // ok +>f : Symbol(f, Decl(keyRemappingKeyofResult.ts, 91, 7)) +>z : Symbol(z, Decl(keyRemappingKeyofResult.ts, 89, 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..ebdf7971f7685 --- /dev/null +++ b/tests/baselines/reference/keyRemappingKeyofResult(strict=true).types @@ -0,0 +1,318 @@ +//// [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" +> : ^^^^^ + + 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" +> : ^^^^^^^^^^ +} + +// 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 ba0b0939ae3cb..f7957cb7ffec8 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,14 +56,14 @@ 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; @@ -70,4 +71,27 @@ function g() { x = "whatever"; // error } -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 {};