Skip to content

Commit 04976bf

Browse files
committed
improve the fix
1 parent 8c95b0e commit 04976bf

10 files changed

Lines changed: 438 additions & 50 deletions

src/compiler/checker.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22895,16 +22895,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2289522895
return result;
2289622896
}
2289722897

22898-
function getApparentMappedTypeKeys(nameType: Type, targetType: MappedType) {
22899-
const modifiersType = getApparentType(getModifiersTypeFromMappedType(targetType));
22898+
function getApparentMappedTypeKeys(nameType: Type, mappedType: MappedType) {
22899+
const modifiersType = getApparentType(getModifiersTypeFromMappedType(mappedType));
2290022900
const mappedKeys: Type[] = [];
2290122901
forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType(
2290222902
modifiersType,
2290322903
TypeFlags.StringOrNumberLiteralOrUnique,
2290422904
/*stringsOnly*/ false,
22905-
t => void mappedKeys.push(instantiateType(nameType, appendTypeMapping(targetType.mapper, getTypeParameterFromMappedType(targetType), t))),
22905+
t => void mappedKeys.push(instantiateType(nameType, appendTypeMapping(mappedType.mapper, getTypeParameterFromMappedType(mappedType), t))),
2290622906
);
22907-
return mappedKeys.length ? getUnionType(mappedKeys) : stringNumberSymbolType;
22907+
return getUnionType(mappedKeys);
2290822908
}
2290922909

2291022910
function structuredTypeRelatedToWorker(source: Type, target: Type, reportErrors: boolean, intersectionState: IntersectionState, saveErrorInfo: ReturnType<typeof captureErrorCalculationState>): Ternary {
@@ -23277,7 +23277,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2327723277
// allow assignments of index types of identical (or similar enough) mapped types.
2327823278
// 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`).
2327923279
// 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.
23280-
const sourceMappedKeys = nameType && isMappedTypeWithKeyofConstraintDeclaration(mappedType) ? getApparentMappedTypeKeys(nameType, mappedType) : (nameType || getConstraintTypeFromMappedType(mappedType));
23280+
let sourceMappedKeys: Type;
23281+
if (nameType && isMappedTypeWithKeyofConstraintDeclaration(mappedType)) {
23282+
sourceMappedKeys = getApparentMappedTypeKeys(nameType, mappedType);
23283+
if (sourceMappedKeys.flags & TypeFlags.Never) {
23284+
// modifiers type of mapped type is often `unknown`, `keyof unknown` is `never` and that's assignable to everything
23285+
// letting this through is too permissive so we use the apparent type of an index type here instead
23286+
sourceMappedKeys = stringNumberSymbolType;
23287+
}
23288+
}
23289+
else {
23290+
sourceMappedKeys = nameType || getConstraintTypeFromMappedType(mappedType);
23291+
}
2328123292
if (result = isRelatedTo(sourceMappedKeys, target, RecursionFlags.Source, reportErrors)) {
2328223293
return result;
2328323294
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
keyRemappingKeyofResult.ts(82,3): error TS2322: Type 'string' is not assignable to type 'Extract<keyof { [P in keyof T as T[P] extends string ? P : never]: any; }, string>'.
2+
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; }'.
3+
Type 'string' is not assignable to type 'T[P] extends string ? P : never'.
4+
5+
6+
==== keyRemappingKeyofResult.ts (2 errors) ====
7+
const sym = Symbol("")
8+
type Orig = { [k: string]: any, str: any, [sym]: any }
9+
10+
type Okay = Exclude<keyof Orig, never>
11+
// type Okay = string | number | typeof sym
12+
13+
type Remapped = { [K in keyof Orig as {} extends Record<K, any> ? never : K]: any }
14+
/* type Remapped = {
15+
str: any;
16+
[sym]: any;
17+
} */
18+
// no string index signature, right?
19+
20+
type Oops = Exclude<keyof Remapped, never>
21+
declare let x: Oops;
22+
x = sym;
23+
x = "str";
24+
// type Oops = typeof sym <-- what happened to "str"?
25+
26+
// equivalently, with an unresolved generic (no `exclude` shenanigans, since conditions won't execute):
27+
function f<T>() {
28+
type Orig = { [k: string]: any, str: any, [sym]: any } & T;
29+
30+
type Okay = keyof Orig;
31+
let a: Okay;
32+
a = "str";
33+
a = sym;
34+
a = "whatever";
35+
// type Okay = string | number | typeof sym
36+
37+
type Remapped = { [K in keyof Orig as {} extends Record<K, any> ? never : K]: any }
38+
/* type Remapped = {
39+
str: any;
40+
[sym]: any;
41+
} */
42+
// no string index signature, right?
43+
44+
type Oops = keyof Remapped;
45+
let x: Oops;
46+
x = sym;
47+
x = "str";
48+
}
49+
50+
// and another generic case with a _distributive_ mapping, to trigger a different branch in `getIndexType`
51+
function g<T>() {
52+
type Orig = { [k: string]: any, str: any, [sym]: any } & T;
53+
54+
type Okay = keyof Orig;
55+
let a: Okay;
56+
a = "str";
57+
a = sym;
58+
a = "whatever";
59+
// type Okay = string | number | typeof sym
60+
61+
type NonIndex<T extends PropertyKey> = {} extends Record<T, any> ? never : T;
62+
type DistributiveNonIndex<T extends PropertyKey> = T extends unknown ? NonIndex<T> : never;
63+
64+
type Remapped = { [K in keyof Orig as DistributiveNonIndex<K>]: any }
65+
/* type Remapped = {
66+
str: any;
67+
[sym]: any;
68+
} */
69+
// no string index signature, right?
70+
71+
type Oops = keyof Remapped;
72+
let x: Oops;
73+
x = sym;
74+
x = "str";
75+
}
76+
77+
// https://github.com/microsoft/TypeScript/issues/57827
78+
79+
type StringKeys<T> = Extract<
80+
keyof {
81+
[P in keyof T as T[P] extends string ? P : never]: any;
82+
},
83+
string
84+
>;
85+
86+
function test_57827<T>(z: StringKeys<T>) {
87+
const f: string = z;
88+
z = "foo"; // error
89+
~
90+
!!! error TS2322: Type 'string' is not assignable to type 'Extract<keyof { [P in keyof T as T[P] extends string ? P : never]: any; }, string>'.
91+
}
92+
93+
type StringKeys2<T> = keyof {
94+
[P in keyof T as T[P] extends string ? P : never]: any;
95+
};
96+
97+
function h<T>(z: StringKeys2<T>) {
98+
z = "foo"; // error
99+
~
100+
!!! error TS2322: Type 'string' is not assignable to type 'keyof { [P in keyof T as T[P] extends string ? P : never]: any; }'.
101+
!!! error TS2322: Type 'string' is not assignable to type 'T[P] extends string ? P : never'.
102+
}
103+
104+
export {};
105+

tests/baselines/reference/keyRemappingKeyofResult(strict=false).js

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,21 @@ x = "str";
2323
// equivalently, with an unresolved generic (no `exclude` shenanigans, since conditions won't execute):
2424
function f<T>() {
2525
type Orig = { [k: string]: any, str: any, [sym]: any } & T;
26-
26+
2727
type Okay = keyof Orig;
2828
let a: Okay;
2929
a = "str";
3030
a = sym;
3131
a = "whatever";
3232
// type Okay = string | number | typeof sym
33-
33+
3434
type Remapped = { [K in keyof Orig as {} extends Record<K, any> ? never : K]: any }
3535
/* type Remapped = {
3636
str: any;
3737
[sym]: any;
3838
} */
3939
// no string index signature, right?
40-
40+
4141
type Oops = keyof Remapped;
4242
let x: Oops;
4343
x = sym;
@@ -47,7 +47,7 @@ function f<T>() {
4747
// and another generic case with a _distributive_ mapping, to trigger a different branch in `getIndexType`
4848
function g<T>() {
4949
type Orig = { [k: string]: any, str: any, [sym]: any } & T;
50-
50+
5151
type Okay = keyof Orig;
5252
let a: Okay;
5353
a = "str";
@@ -57,14 +57,14 @@ function g<T>() {
5757

5858
type NonIndex<T extends PropertyKey> = {} extends Record<T, any> ? never : T;
5959
type DistributiveNonIndex<T extends PropertyKey> = T extends unknown ? NonIndex<T> : never;
60-
60+
6161
type Remapped = { [K in keyof Orig as DistributiveNonIndex<K>]: any }
6262
/* type Remapped = {
6363
str: any;
6464
[sym]: any;
6565
} */
6666
// no string index signature, right?
67-
67+
6868
type Oops = keyof Remapped;
6969
let x: Oops;
7070
x = sym;
@@ -82,9 +82,19 @@ type StringKeys<T> = Extract<
8282

8383
function test_57827<T>(z: StringKeys<T>) {
8484
const f: string = z;
85+
z = "foo"; // error
8586
}
8687

87-
export {};
88+
type StringKeys2<T> = keyof {
89+
[P in keyof T as T[P] extends string ? P : never]: any;
90+
};
91+
92+
function h<T>(z: StringKeys2<T>) {
93+
z = "foo"; // error
94+
}
95+
96+
export {};
97+
8898

8999
//// [keyRemappingKeyofResult.js]
90100
const sym = Symbol("");
@@ -113,5 +123,9 @@ function g() {
113123
}
114124
function test_57827(z) {
115125
const f = z;
126+
z = "foo"; // error
127+
}
128+
function h(z) {
129+
z = "foo"; // error
116130
}
117131
export {};

tests/baselines/reference/keyRemappingKeyofResult(strict=false).symbols

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ function f<T>() {
6363
>[sym] : Symbol([sym], Decl(keyRemappingKeyofResult.ts, 21, 45))
6464
>sym : Symbol(sym, Decl(keyRemappingKeyofResult.ts, 0, 5))
6565
>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 20, 11))
66-
66+
6767
type Okay = keyof Orig;
6868
>Okay : Symbol(Okay, Decl(keyRemappingKeyofResult.ts, 21, 63))
6969
>Orig : Symbol(Orig, Decl(keyRemappingKeyofResult.ts, 20, 17))
@@ -83,7 +83,7 @@ function f<T>() {
8383
>a : Symbol(a, Decl(keyRemappingKeyofResult.ts, 24, 7))
8484

8585
// type Okay = string | number | typeof sym
86-
86+
8787
type Remapped = { [K in keyof Orig as {} extends Record<K, any> ? never : K]: any }
8888
>Remapped : Symbol(Remapped, Decl(keyRemappingKeyofResult.ts, 27, 19))
8989
>K : Symbol(K, Decl(keyRemappingKeyofResult.ts, 30, 23))
@@ -97,7 +97,7 @@ function f<T>() {
9797
[sym]: any;
9898
} */
9999
// no string index signature, right?
100-
100+
101101
type Oops = keyof Remapped;
102102
>Oops : Symbol(Oops, Decl(keyRemappingKeyofResult.ts, 30, 87))
103103
>Remapped : Symbol(Remapped, Decl(keyRemappingKeyofResult.ts, 27, 19))
@@ -126,7 +126,7 @@ function g<T>() {
126126
>[sym] : Symbol([sym], Decl(keyRemappingKeyofResult.ts, 45, 45))
127127
>sym : Symbol(sym, Decl(keyRemappingKeyofResult.ts, 0, 5))
128128
>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 44, 11))
129-
129+
130130
type Okay = keyof Orig;
131131
>Okay : Symbol(Okay, Decl(keyRemappingKeyofResult.ts, 45, 63))
132132
>Orig : Symbol(Orig, Decl(keyRemappingKeyofResult.ts, 44, 17))
@@ -162,7 +162,7 @@ function g<T>() {
162162
>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 55, 30))
163163
>NonIndex : Symbol(NonIndex, Decl(keyRemappingKeyofResult.ts, 51, 19))
164164
>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 55, 30))
165-
165+
166166
type Remapped = { [K in keyof Orig as DistributiveNonIndex<K>]: any }
167167
>Remapped : Symbol(Remapped, Decl(keyRemappingKeyofResult.ts, 55, 95))
168168
>K : Symbol(K, Decl(keyRemappingKeyofResult.ts, 57, 23))
@@ -175,7 +175,7 @@ function g<T>() {
175175
[sym]: any;
176176
} */
177177
// no string index signature, right?
178-
178+
179179
type Oops = keyof Remapped;
180180
>Oops : Symbol(Oops, Decl(keyRemappingKeyofResult.ts, 57, 73))
181181
>Remapped : Symbol(Remapped, Decl(keyRemappingKeyofResult.ts, 55, 95))
@@ -220,7 +220,35 @@ function test_57827<T>(z: StringKeys<T>) {
220220

221221
const f: string = z;
222222
>f : Symbol(f, Decl(keyRemappingKeyofResult.ts, 80, 7))
223+
>z : Symbol(z, Decl(keyRemappingKeyofResult.ts, 79, 23))
224+
225+
z = "foo"; // error
223226
>z : Symbol(z, Decl(keyRemappingKeyofResult.ts, 79, 23))
224227
}
225228

229+
type StringKeys2<T> = keyof {
230+
>StringKeys2 : Symbol(StringKeys2, Decl(keyRemappingKeyofResult.ts, 82, 1))
231+
>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 84, 17))
232+
233+
[P in keyof T as T[P] extends string ? P : never]: any;
234+
>P : Symbol(P, Decl(keyRemappingKeyofResult.ts, 85, 3))
235+
>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 84, 17))
236+
>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 84, 17))
237+
>P : Symbol(P, Decl(keyRemappingKeyofResult.ts, 85, 3))
238+
>P : Symbol(P, Decl(keyRemappingKeyofResult.ts, 85, 3))
239+
240+
};
241+
242+
function h<T>(z: StringKeys2<T>) {
243+
>h : Symbol(h, Decl(keyRemappingKeyofResult.ts, 86, 2))
244+
>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 88, 11))
245+
>z : Symbol(z, Decl(keyRemappingKeyofResult.ts, 88, 14))
246+
>StringKeys2 : Symbol(StringKeys2, Decl(keyRemappingKeyofResult.ts, 82, 1))
247+
>T : Symbol(T, Decl(keyRemappingKeyofResult.ts, 88, 11))
248+
249+
z = "foo"; // error
250+
>z : Symbol(z, Decl(keyRemappingKeyofResult.ts, 88, 14))
251+
}
252+
226253
export {};
254+

0 commit comments

Comments
 (0)