Skip to content

Commit 856a579

Browse files
committed
Fix a relationship check for partial generic mapped targets
1 parent 1654ebf commit 856a579

File tree

5 files changed

+322
-7
lines changed

5 files changed

+322
-7
lines changed

src/compiler/checker.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23701,8 +23701,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2370123701
// Check if source type `S` is related to target type `{ [P in Q]: T }` or `{ [P in Q as R]: T}`.
2370223702
const keysRemapped = !!target.declaration.nameType;
2370323703
const templateType = getTemplateTypeFromMappedType(target);
23704-
const modifiers = getMappedTypeModifiers(target);
23705-
if (!(modifiers & MappedTypeModifiers.ExcludeOptional)) {
23704+
const combinedOptionality = getCombinedMappedTypeOptionality(target);
23705+
if (combinedOptionality !== -1) {
2370623706
// If the mapped type has shape `{ [P in Q]: T[P] }`,
2370723707
// source `S` is related to target if `T` = `S`, i.e. `S` is related to `{ [P in Q]: S[P] }`.
2370823708
if (
@@ -23717,7 +23717,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2371723717
const targetKeys = keysRemapped ? getNameTypeFromMappedType(target)! : getConstraintTypeFromMappedType(target);
2371823718
// Type of the keys of source type `S`, i.e. `keyof S`.
2371923719
const sourceKeys = getIndexType(source, IndexFlags.NoIndexSignatures);
23720-
const includeOptional = modifiers & MappedTypeModifiers.IncludeOptional;
23720+
const includeOptional = combinedOptionality === 1;
2372123721
const filteredByApplicability = includeOptional ? intersectTypes(targetKeys, sourceKeys) : undefined;
2372223722
// A source type `S` is related to a target type `{ [P in Q]: T }` if `Q` is related to `keyof S` and `S[Q]` is related to `T`.
2372323723
// A source type `S` is related to a target type `{ [P in Q as R]: T }` if `R` is related to `keyof S` and `S[R]` is related to `T.
@@ -23734,10 +23734,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2373423734
// Fastpath: When the template type has the form `Obj[P]` where `P` is the mapped type parameter, directly compare source `S` with `Obj`
2373523735
// to avoid creating the (potentially very large) number of new intermediate types made by manufacturing `S[P]`.
2373623736
const nonNullComponent = extractTypesOfKind(templateType, ~TypeFlags.Nullable);
23737-
if (!keysRemapped && nonNullComponent.flags & TypeFlags.IndexedAccess && (nonNullComponent as IndexedAccessType).indexType === typeParameter) {
23738-
if (result = isRelatedTo(source, (nonNullComponent as IndexedAccessType).objectType, RecursionFlags.Target, reportErrors)) {
23739-
return result;
23740-
}
23737+
if (!keysRemapped && nonNullComponent.flags & TypeFlags.IndexedAccess && (nonNullComponent as IndexedAccessType).indexType === typeParameter && (result = isRelatedTo(source, (nonNullComponent as IndexedAccessType).objectType, RecursionFlags.Target, reportErrors))) {
23738+
return result;
2374123739
}
2374223740
else {
2374323741
// We need to compare the type of a property on the source type `S` to the type of the same property on the target type,
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
mappedTypeRelationships2.ts(21,38): error TS2344: Type 'ABC' does not satisfy the constraint 'WithNumber<Record<keyof T, any>>'.
2+
3+
4+
==== mappedTypeRelationships2.ts (1 errors) ====
5+
// https://github.com/microsoft/TypeScript/issues/62717
6+
7+
type Alias1<T extends object, U extends { [K in keyof T]?: any }> = U;
8+
type Alias2<T extends object, U extends Partial<Record<keyof T, any>>> = U;
9+
10+
type AB = { a: string; b: string };
11+
type B = { b: string };
12+
13+
type Test1 = Alias1<AB, B>; // ok
14+
type Test2 = Alias2<AB, B>; // ok
15+
16+
type Test3<T extends AB> = Alias1<T, B>; // ok
17+
type Test4<T extends AB> = Alias2<T, B>; // ok
18+
19+
type WithNumber<T> = { [K in keyof T]: T[K] | number };
20+
type Alias3<T extends object, U extends WithNumber<Record<keyof T, any>>> = U;
21+
22+
type ABC = { a: string; b: string; c: string };
23+
24+
type Test5 = Alias3<AB, ABC>; // ok
25+
type Test6<T extends AB> = Alias3<T, ABC>; // error
26+
~~~
27+
!!! error TS2344: Type 'ABC' does not satisfy the constraint 'WithNumber<Record<keyof T, any>>'.
28+
29+
type Alias4<T extends object, U extends WithNumber<Partial<Record<keyof T, any>>>> = U;
30+
31+
type Test7 = Alias4<AB, ABC>; // ok
32+
type Test8<T extends AB> = Alias4<T, ABC>; // ok
33+
34+
type Alias5<T extends object, U extends Partial<WithNumber<Record<keyof T, any>>>> = U;
35+
36+
type Test9 = Alias5<AB, ABC>; // ok
37+
type Test10<T extends AB> = Alias5<T, ABC>; // ok
38+
39+
export {};
40+
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
//// [tests/cases/conformance/types/mapped/mappedTypeRelationships2.ts] ////
2+
3+
=== mappedTypeRelationships2.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/62717
5+
6+
type Alias1<T extends object, U extends { [K in keyof T]?: any }> = U;
7+
>Alias1 : Symbol(Alias1, Decl(mappedTypeRelationships2.ts, 0, 0))
8+
>T : Symbol(T, Decl(mappedTypeRelationships2.ts, 2, 12))
9+
>U : Symbol(U, Decl(mappedTypeRelationships2.ts, 2, 29))
10+
>K : Symbol(K, Decl(mappedTypeRelationships2.ts, 2, 43))
11+
>T : Symbol(T, Decl(mappedTypeRelationships2.ts, 2, 12))
12+
>U : Symbol(U, Decl(mappedTypeRelationships2.ts, 2, 29))
13+
14+
type Alias2<T extends object, U extends Partial<Record<keyof T, any>>> = U;
15+
>Alias2 : Symbol(Alias2, Decl(mappedTypeRelationships2.ts, 2, 70))
16+
>T : Symbol(T, Decl(mappedTypeRelationships2.ts, 3, 12))
17+
>U : Symbol(U, Decl(mappedTypeRelationships2.ts, 3, 29))
18+
>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --))
19+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
20+
>T : Symbol(T, Decl(mappedTypeRelationships2.ts, 3, 12))
21+
>U : Symbol(U, Decl(mappedTypeRelationships2.ts, 3, 29))
22+
23+
type AB = { a: string; b: string };
24+
>AB : Symbol(AB, Decl(mappedTypeRelationships2.ts, 3, 75))
25+
>a : Symbol(a, Decl(mappedTypeRelationships2.ts, 5, 11))
26+
>b : Symbol(b, Decl(mappedTypeRelationships2.ts, 5, 22))
27+
28+
type B = { b: string };
29+
>B : Symbol(B, Decl(mappedTypeRelationships2.ts, 5, 35))
30+
>b : Symbol(b, Decl(mappedTypeRelationships2.ts, 6, 10))
31+
32+
type Test1 = Alias1<AB, B>; // ok
33+
>Test1 : Symbol(Test1, Decl(mappedTypeRelationships2.ts, 6, 23))
34+
>Alias1 : Symbol(Alias1, Decl(mappedTypeRelationships2.ts, 0, 0))
35+
>AB : Symbol(AB, Decl(mappedTypeRelationships2.ts, 3, 75))
36+
>B : Symbol(B, Decl(mappedTypeRelationships2.ts, 5, 35))
37+
38+
type Test2 = Alias2<AB, B>; // ok
39+
>Test2 : Symbol(Test2, Decl(mappedTypeRelationships2.ts, 8, 27))
40+
>Alias2 : Symbol(Alias2, Decl(mappedTypeRelationships2.ts, 2, 70))
41+
>AB : Symbol(AB, Decl(mappedTypeRelationships2.ts, 3, 75))
42+
>B : Symbol(B, Decl(mappedTypeRelationships2.ts, 5, 35))
43+
44+
type Test3<T extends AB> = Alias1<T, B>; // ok
45+
>Test3 : Symbol(Test3, Decl(mappedTypeRelationships2.ts, 9, 27))
46+
>T : Symbol(T, Decl(mappedTypeRelationships2.ts, 11, 11))
47+
>AB : Symbol(AB, Decl(mappedTypeRelationships2.ts, 3, 75))
48+
>Alias1 : Symbol(Alias1, Decl(mappedTypeRelationships2.ts, 0, 0))
49+
>T : Symbol(T, Decl(mappedTypeRelationships2.ts, 11, 11))
50+
>B : Symbol(B, Decl(mappedTypeRelationships2.ts, 5, 35))
51+
52+
type Test4<T extends AB> = Alias2<T, B>; // ok
53+
>Test4 : Symbol(Test4, Decl(mappedTypeRelationships2.ts, 11, 40))
54+
>T : Symbol(T, Decl(mappedTypeRelationships2.ts, 12, 11))
55+
>AB : Symbol(AB, Decl(mappedTypeRelationships2.ts, 3, 75))
56+
>Alias2 : Symbol(Alias2, Decl(mappedTypeRelationships2.ts, 2, 70))
57+
>T : Symbol(T, Decl(mappedTypeRelationships2.ts, 12, 11))
58+
>B : Symbol(B, Decl(mappedTypeRelationships2.ts, 5, 35))
59+
60+
type WithNumber<T> = { [K in keyof T]: T[K] | number };
61+
>WithNumber : Symbol(WithNumber, Decl(mappedTypeRelationships2.ts, 12, 40))
62+
>T : Symbol(T, Decl(mappedTypeRelationships2.ts, 14, 16))
63+
>K : Symbol(K, Decl(mappedTypeRelationships2.ts, 14, 24))
64+
>T : Symbol(T, Decl(mappedTypeRelationships2.ts, 14, 16))
65+
>T : Symbol(T, Decl(mappedTypeRelationships2.ts, 14, 16))
66+
>K : Symbol(K, Decl(mappedTypeRelationships2.ts, 14, 24))
67+
68+
type Alias3<T extends object, U extends WithNumber<Record<keyof T, any>>> = U;
69+
>Alias3 : Symbol(Alias3, Decl(mappedTypeRelationships2.ts, 14, 55))
70+
>T : Symbol(T, Decl(mappedTypeRelationships2.ts, 15, 12))
71+
>U : Symbol(U, Decl(mappedTypeRelationships2.ts, 15, 29))
72+
>WithNumber : Symbol(WithNumber, Decl(mappedTypeRelationships2.ts, 12, 40))
73+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
74+
>T : Symbol(T, Decl(mappedTypeRelationships2.ts, 15, 12))
75+
>U : Symbol(U, Decl(mappedTypeRelationships2.ts, 15, 29))
76+
77+
type ABC = { a: string; b: string; c: string };
78+
>ABC : Symbol(ABC, Decl(mappedTypeRelationships2.ts, 15, 78))
79+
>a : Symbol(a, Decl(mappedTypeRelationships2.ts, 17, 12))
80+
>b : Symbol(b, Decl(mappedTypeRelationships2.ts, 17, 23))
81+
>c : Symbol(c, Decl(mappedTypeRelationships2.ts, 17, 34))
82+
83+
type Test5 = Alias3<AB, ABC>; // ok
84+
>Test5 : Symbol(Test5, Decl(mappedTypeRelationships2.ts, 17, 47))
85+
>Alias3 : Symbol(Alias3, Decl(mappedTypeRelationships2.ts, 14, 55))
86+
>AB : Symbol(AB, Decl(mappedTypeRelationships2.ts, 3, 75))
87+
>ABC : Symbol(ABC, Decl(mappedTypeRelationships2.ts, 15, 78))
88+
89+
type Test6<T extends AB> = Alias3<T, ABC>; // error
90+
>Test6 : Symbol(Test6, Decl(mappedTypeRelationships2.ts, 19, 29))
91+
>T : Symbol(T, Decl(mappedTypeRelationships2.ts, 20, 11))
92+
>AB : Symbol(AB, Decl(mappedTypeRelationships2.ts, 3, 75))
93+
>Alias3 : Symbol(Alias3, Decl(mappedTypeRelationships2.ts, 14, 55))
94+
>T : Symbol(T, Decl(mappedTypeRelationships2.ts, 20, 11))
95+
>ABC : Symbol(ABC, Decl(mappedTypeRelationships2.ts, 15, 78))
96+
97+
type Alias4<T extends object, U extends WithNumber<Partial<Record<keyof T, any>>>> = U;
98+
>Alias4 : Symbol(Alias4, Decl(mappedTypeRelationships2.ts, 20, 42))
99+
>T : Symbol(T, Decl(mappedTypeRelationships2.ts, 22, 12))
100+
>U : Symbol(U, Decl(mappedTypeRelationships2.ts, 22, 29))
101+
>WithNumber : Symbol(WithNumber, Decl(mappedTypeRelationships2.ts, 12, 40))
102+
>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --))
103+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
104+
>T : Symbol(T, Decl(mappedTypeRelationships2.ts, 22, 12))
105+
>U : Symbol(U, Decl(mappedTypeRelationships2.ts, 22, 29))
106+
107+
type Test7 = Alias4<AB, ABC>; // ok
108+
>Test7 : Symbol(Test7, Decl(mappedTypeRelationships2.ts, 22, 87))
109+
>Alias4 : Symbol(Alias4, Decl(mappedTypeRelationships2.ts, 20, 42))
110+
>AB : Symbol(AB, Decl(mappedTypeRelationships2.ts, 3, 75))
111+
>ABC : Symbol(ABC, Decl(mappedTypeRelationships2.ts, 15, 78))
112+
113+
type Test8<T extends AB> = Alias4<T, ABC>; // ok
114+
>Test8 : Symbol(Test8, Decl(mappedTypeRelationships2.ts, 24, 29))
115+
>T : Symbol(T, Decl(mappedTypeRelationships2.ts, 25, 11))
116+
>AB : Symbol(AB, Decl(mappedTypeRelationships2.ts, 3, 75))
117+
>Alias4 : Symbol(Alias4, Decl(mappedTypeRelationships2.ts, 20, 42))
118+
>T : Symbol(T, Decl(mappedTypeRelationships2.ts, 25, 11))
119+
>ABC : Symbol(ABC, Decl(mappedTypeRelationships2.ts, 15, 78))
120+
121+
type Alias5<T extends object, U extends Partial<WithNumber<Record<keyof T, any>>>> = U;
122+
>Alias5 : Symbol(Alias5, Decl(mappedTypeRelationships2.ts, 25, 42))
123+
>T : Symbol(T, Decl(mappedTypeRelationships2.ts, 27, 12))
124+
>U : Symbol(U, Decl(mappedTypeRelationships2.ts, 27, 29))
125+
>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --))
126+
>WithNumber : Symbol(WithNumber, Decl(mappedTypeRelationships2.ts, 12, 40))
127+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
128+
>T : Symbol(T, Decl(mappedTypeRelationships2.ts, 27, 12))
129+
>U : Symbol(U, Decl(mappedTypeRelationships2.ts, 27, 29))
130+
131+
type Test9 = Alias5<AB, ABC>; // ok
132+
>Test9 : Symbol(Test9, Decl(mappedTypeRelationships2.ts, 27, 87))
133+
>Alias5 : Symbol(Alias5, Decl(mappedTypeRelationships2.ts, 25, 42))
134+
>AB : Symbol(AB, Decl(mappedTypeRelationships2.ts, 3, 75))
135+
>ABC : Symbol(ABC, Decl(mappedTypeRelationships2.ts, 15, 78))
136+
137+
type Test10<T extends AB> = Alias5<T, ABC>; // ok
138+
>Test10 : Symbol(Test10, Decl(mappedTypeRelationships2.ts, 29, 29))
139+
>T : Symbol(T, Decl(mappedTypeRelationships2.ts, 30, 12))
140+
>AB : Symbol(AB, Decl(mappedTypeRelationships2.ts, 3, 75))
141+
>Alias5 : Symbol(Alias5, Decl(mappedTypeRelationships2.ts, 25, 42))
142+
>T : Symbol(T, Decl(mappedTypeRelationships2.ts, 30, 12))
143+
>ABC : Symbol(ABC, Decl(mappedTypeRelationships2.ts, 15, 78))
144+
145+
export {};
146+
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
//// [tests/cases/conformance/types/mapped/mappedTypeRelationships2.ts] ////
2+
3+
=== mappedTypeRelationships2.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/62717
5+
6+
type Alias1<T extends object, U extends { [K in keyof T]?: any }> = U;
7+
>Alias1 : U
8+
> : ^
9+
10+
type Alias2<T extends object, U extends Partial<Record<keyof T, any>>> = U;
11+
>Alias2 : U
12+
> : ^
13+
14+
type AB = { a: string; b: string };
15+
>AB : AB
16+
> : ^^
17+
>a : string
18+
> : ^^^^^^
19+
>b : string
20+
> : ^^^^^^
21+
22+
type B = { b: string };
23+
>B : B
24+
> : ^
25+
>b : string
26+
> : ^^^^^^
27+
28+
type Test1 = Alias1<AB, B>; // ok
29+
>Test1 : B
30+
> : ^
31+
32+
type Test2 = Alias2<AB, B>; // ok
33+
>Test2 : B
34+
> : ^
35+
36+
type Test3<T extends AB> = Alias1<T, B>; // ok
37+
>Test3 : B
38+
> : ^
39+
40+
type Test4<T extends AB> = Alias2<T, B>; // ok
41+
>Test4 : B
42+
> : ^
43+
44+
type WithNumber<T> = { [K in keyof T]: T[K] | number };
45+
>WithNumber : WithNumber<T>
46+
> : ^^^^^^^^^^^^^
47+
48+
type Alias3<T extends object, U extends WithNumber<Record<keyof T, any>>> = U;
49+
>Alias3 : U
50+
> : ^
51+
52+
type ABC = { a: string; b: string; c: string };
53+
>ABC : ABC
54+
> : ^^^
55+
>a : string
56+
> : ^^^^^^
57+
>b : string
58+
> : ^^^^^^
59+
>c : string
60+
> : ^^^^^^
61+
62+
type Test5 = Alias3<AB, ABC>; // ok
63+
>Test5 : ABC
64+
> : ^^^
65+
66+
type Test6<T extends AB> = Alias3<T, ABC>; // error
67+
>Test6 : ABC
68+
> : ^^^
69+
70+
type Alias4<T extends object, U extends WithNumber<Partial<Record<keyof T, any>>>> = U;
71+
>Alias4 : U
72+
> : ^
73+
74+
type Test7 = Alias4<AB, ABC>; // ok
75+
>Test7 : ABC
76+
> : ^^^
77+
78+
type Test8<T extends AB> = Alias4<T, ABC>; // ok
79+
>Test8 : ABC
80+
> : ^^^
81+
82+
type Alias5<T extends object, U extends Partial<WithNumber<Record<keyof T, any>>>> = U;
83+
>Alias5 : U
84+
> : ^
85+
86+
type Test9 = Alias5<AB, ABC>; // ok
87+
>Test9 : ABC
88+
> : ^^^
89+
90+
type Test10<T extends AB> = Alias5<T, ABC>; // ok
91+
>Test10 : ABC
92+
> : ^^^
93+
94+
export {};
95+
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// @strict: true
2+
// @noEmit: true
3+
4+
// https://github.com/microsoft/TypeScript/issues/62717
5+
6+
type Alias1<T extends object, U extends { [K in keyof T]?: any }> = U;
7+
type Alias2<T extends object, U extends Partial<Record<keyof T, any>>> = U;
8+
9+
type AB = { a: string; b: string };
10+
type B = { b: string };
11+
12+
type Test1 = Alias1<AB, B>; // ok
13+
type Test2 = Alias2<AB, B>; // ok
14+
15+
type Test3<T extends AB> = Alias1<T, B>; // ok
16+
type Test4<T extends AB> = Alias2<T, B>; // ok
17+
18+
type WithNumber<T> = { [K in keyof T]: T[K] | number };
19+
type Alias3<T extends object, U extends WithNumber<Record<keyof T, any>>> = U;
20+
21+
type ABC = { a: string; b: string; c: string };
22+
23+
type Test5 = Alias3<AB, ABC>; // ok
24+
type Test6<T extends AB> = Alias3<T, ABC>; // error
25+
26+
type Alias4<T extends object, U extends WithNumber<Partial<Record<keyof T, any>>>> = U;
27+
28+
type Test7 = Alias4<AB, ABC>; // ok
29+
type Test8<T extends AB> = Alias4<T, ABC>; // ok
30+
31+
type Alias5<T extends object, U extends Partial<WithNumber<Record<keyof T, any>>>> = U;
32+
33+
type Test9 = Alias5<AB, ABC>; // ok
34+
type Test10<T extends AB> = Alias5<T, ABC>; // ok
35+
36+
export {};

0 commit comments

Comments
 (0)