Skip to content

Commit 04521da

Browse files
committed
Let assignability checking of generic mapped types decompose
intersections on both the source and the target. Fixes microsoft#27484.
1 parent 85a3475 commit 04521da

8 files changed

+132
-5
lines changed

src/compiler/checker.ts

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11817,6 +11817,16 @@ namespace ts {
1181711817
return relation === definitelyAssignableRelation ? undefined : getConstraintOfType(type);
1181811818
}
1181911819

11820+
function typeRelatedToMappedTypeTemplateConstituent(source: Type, parameter: TypeParameter, templateConstituent: Type, reportErrors: boolean) {
11821+
if (templateConstituent.flags & TypeFlags.IndexedAccess &&
11822+
(<IndexedAccessType>templateConstituent).indexType === parameter) {
11823+
return isRelatedTo(source, (<IndexedAccessType>templateConstituent).objectType, reportErrors);
11824+
}
11825+
else {
11826+
return Ternary.False;
11827+
}
11828+
}
11829+
1182011830
function structuredTypeRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary {
1182111831
const flags = source.flags & target.flags;
1182211832
if (relation === identityRelation && !(flags & TypeFlags.Object)) {
@@ -11853,8 +11863,10 @@ namespace ts {
1185311863
let originalErrorInfo: DiagnosticMessageChain | undefined;
1185411864
const saveErrorInfo = errorInfo;
1185511865
if (target.flags & TypeFlags.TypeParameter) {
11856-
// A source type { [P in keyof T]: X } is related to a target type T if X is related to T[P].
11857-
if (getObjectFlags(source) & ObjectFlags.Mapped && getConstraintTypeFromMappedType(<MappedType>source) === getIndexType(target)) {
11866+
// A source type { [P in K]: X } is related to a target type T if keyof T is related to K
11867+
// and X is related to T[P].
11868+
if (getObjectFlags(source) & ObjectFlags.Mapped &&
11869+
isRelatedTo(getIndexType(target), getConstraintTypeFromMappedType(<MappedType>source))) {
1185811870
if (!(getMappedTypeModifiers(<MappedType>source) & MappedTypeModifiers.IncludeOptional)) {
1185911871
const templateType = getTemplateTypeFromMappedType(<MappedType>source);
1186011872
const indexedAccessType = getIndexedAccessType(target, getTypeParameterFromMappedType(<MappedType>source));
@@ -11904,9 +11916,22 @@ namespace ts {
1190411916
const template = getTemplateTypeFromMappedType(target);
1190511917
const modifiers = getMappedTypeModifiers(target);
1190611918
if (!(modifiers & MappedTypeModifiers.ExcludeOptional)) {
11907-
if (template.flags & TypeFlags.IndexedAccess && (<IndexedAccessType>template).objectType === source &&
11908-
(<IndexedAccessType>template).indexType === getTypeParameterFromMappedType(target)) {
11909-
return Ternary.True;
11919+
const parameter = getTypeParameterFromMappedType(target);
11920+
if (template.flags & TypeFlags.Intersection) {
11921+
// C.f. typeRelatedToEachType
11922+
result = Ternary.True;
11923+
for (const templateConstituent of (<IntersectionType>template).types) {
11924+
result &= typeRelatedToMappedTypeTemplateConstituent(source, parameter, templateConstituent, reportErrors);
11925+
if (!result) {
11926+
break;
11927+
}
11928+
}
11929+
}
11930+
else {
11931+
result = typeRelatedToMappedTypeTemplateConstituent(source, parameter, template, reportErrors);
11932+
}
11933+
if (result) {
11934+
return result;
1191011935
}
1191111936
// A source type T is related to a target type { [P in keyof T]: X } if T[P] is related to X.
1191211937
if (!isGenericMappedType(source) && getConstraintTypeFromMappedType(target) === getIndexType(source)) {

tests/baselines/reference/conditionalTypes1.errors.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(106,5): error TS2
2020
Type 'keyof T' is not assignable to type 'never'.
2121
Type 'string | number | symbol' is not assignable to type 'never'.
2222
Type 'string' is not assignable to type 'never'.
23+
Type 'Pick<T, { [K in keyof T]: T[K] extends Function ? never : K; }[keyof T]>' is not assignable to type 'T'.
2324
tests/cases/conformance/types/conditional/conditionalTypes1.ts(108,5): error TS2322: Type 'Pick<T, { [K in keyof T]: T[K] extends Function ? K : never; }[keyof T]>' is not assignable to type 'Pick<T, { [K in keyof T]: T[K] extends Function ? never : K; }[keyof T]>'.
2425
Type 'T[keyof T] extends Function ? never : keyof T' is not assignable to type 'T[keyof T] extends Function ? keyof T : never'.
2526
Type 'keyof T' is not assignable to type 'never'.
27+
Type 'Pick<T, { [K in keyof T]: T[K] extends Function ? K : never; }[keyof T]>' is not assignable to type 'T'.
2628
tests/cases/conformance/types/conditional/conditionalTypes1.ts(114,5): error TS2322: Type 'keyof T' is not assignable to type 'T[keyof T] extends Function ? keyof T : never'.
2729
Type 'string | number | symbol' is not assignable to type 'T[keyof T] extends Function ? keyof T : never'.
2830
Type 'string' is not assignable to type 'T[keyof T] extends Function ? keyof T : never'.
@@ -190,12 +192,14 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(288,43): error TS
190192
!!! error TS2322: Type 'keyof T' is not assignable to type 'never'.
191193
!!! error TS2322: Type 'string | number | symbol' is not assignable to type 'never'.
192194
!!! error TS2322: Type 'string' is not assignable to type 'never'.
195+
!!! error TS2322: Type 'Pick<T, { [K in keyof T]: T[K] extends Function ? never : K; }[keyof T]>' is not assignable to type 'T'.
193196
z = x;
194197
z = y; // Error
195198
~
196199
!!! error TS2322: Type 'Pick<T, { [K in keyof T]: T[K] extends Function ? K : never; }[keyof T]>' is not assignable to type 'Pick<T, { [K in keyof T]: T[K] extends Function ? never : K; }[keyof T]>'.
197200
!!! error TS2322: Type 'T[keyof T] extends Function ? never : keyof T' is not assignable to type 'T[keyof T] extends Function ? keyof T : never'.
198201
!!! error TS2322: Type 'keyof T' is not assignable to type 'never'.
202+
!!! error TS2322: Type 'Pick<T, { [K in keyof T]: T[K] extends Function ? K : never; }[keyof T]>' is not assignable to type 'T'.
199203
}
200204

201205
function f8<T>(x: keyof T, y: FunctionPropertyNames<T>, z: NonFunctionPropertyNames<T>) {
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//// [genericMappedTypeIntersections.ts]
2+
// Repro for #27484
3+
function makePropsForWrappedComponent<PassthroughProps, ExternalProps, InjectedProps>(
4+
outerProps: Readonly<PassthroughProps & ExternalProps>,
5+
injectedProps: Readonly<InjectedProps>): Readonly<PassthroughProps & InjectedProps> {
6+
return Object.assign({}, injectedProps, outerProps);
7+
}
8+
9+
10+
//// [genericMappedTypeIntersections.js]
11+
// Repro for #27484
12+
function makePropsForWrappedComponent(outerProps, injectedProps) {
13+
return Object.assign({}, injectedProps, outerProps);
14+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
=== tests/cases/compiler/genericMappedTypeIntersections.ts ===
2+
// Repro for #27484
3+
function makePropsForWrappedComponent<PassthroughProps, ExternalProps, InjectedProps>(
4+
>makePropsForWrappedComponent : Symbol(makePropsForWrappedComponent, Decl(genericMappedTypeIntersections.ts, 0, 0))
5+
>PassthroughProps : Symbol(PassthroughProps, Decl(genericMappedTypeIntersections.ts, 1, 38))
6+
>ExternalProps : Symbol(ExternalProps, Decl(genericMappedTypeIntersections.ts, 1, 55))
7+
>InjectedProps : Symbol(InjectedProps, Decl(genericMappedTypeIntersections.ts, 1, 70))
8+
9+
outerProps: Readonly<PassthroughProps & ExternalProps>,
10+
>outerProps : Symbol(outerProps, Decl(genericMappedTypeIntersections.ts, 1, 86))
11+
>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --))
12+
>PassthroughProps : Symbol(PassthroughProps, Decl(genericMappedTypeIntersections.ts, 1, 38))
13+
>ExternalProps : Symbol(ExternalProps, Decl(genericMappedTypeIntersections.ts, 1, 55))
14+
15+
injectedProps: Readonly<InjectedProps>): Readonly<PassthroughProps & InjectedProps> {
16+
>injectedProps : Symbol(injectedProps, Decl(genericMappedTypeIntersections.ts, 2, 59))
17+
>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --))
18+
>InjectedProps : Symbol(InjectedProps, Decl(genericMappedTypeIntersections.ts, 1, 70))
19+
>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --))
20+
>PassthroughProps : Symbol(PassthroughProps, Decl(genericMappedTypeIntersections.ts, 1, 38))
21+
>InjectedProps : Symbol(InjectedProps, Decl(genericMappedTypeIntersections.ts, 1, 70))
22+
23+
return Object.assign({}, injectedProps, outerProps);
24+
>Object.assign : Symbol(ObjectConstructor.assign, Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --))
25+
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
26+
>assign : Symbol(ObjectConstructor.assign, Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --))
27+
>injectedProps : Symbol(injectedProps, Decl(genericMappedTypeIntersections.ts, 2, 59))
28+
>outerProps : Symbol(outerProps, Decl(genericMappedTypeIntersections.ts, 1, 86))
29+
}
30+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
=== tests/cases/compiler/genericMappedTypeIntersections.ts ===
2+
// Repro for #27484
3+
function makePropsForWrappedComponent<PassthroughProps, ExternalProps, InjectedProps>(
4+
>makePropsForWrappedComponent : <PassthroughProps, ExternalProps, InjectedProps>(outerProps: Readonly<PassthroughProps & ExternalProps>, injectedProps: Readonly<InjectedProps>) => Readonly<PassthroughProps & InjectedProps>
5+
6+
outerProps: Readonly<PassthroughProps & ExternalProps>,
7+
>outerProps : Readonly<PassthroughProps & ExternalProps>
8+
9+
injectedProps: Readonly<InjectedProps>): Readonly<PassthroughProps & InjectedProps> {
10+
>injectedProps : Readonly<InjectedProps>
11+
12+
return Object.assign({}, injectedProps, outerProps);
13+
>Object.assign({}, injectedProps, outerProps) : Readonly<InjectedProps> & Readonly<PassthroughProps & ExternalProps>
14+
>Object.assign : { <T, U>(target: T, source: U): T & U; <T, U, V>(target: T, source1: U, source2: V): T & U & V; <T, U, V, W>(target: T, source1: U, source2: V, source3: W): T & U & V & W; (target: object, ...sources: any[]): any; }
15+
>Object : ObjectConstructor
16+
>assign : { <T, U>(target: T, source: U): T & U; <T, U, V>(target: T, source1: U, source2: V): T & U & V; <T, U, V, W>(target: T, source1: U, source2: V, source3: W): T & U & V & W; (target: object, ...sources: any[]): any; }
17+
>{} : {}
18+
>injectedProps : Readonly<InjectedProps>
19+
>outerProps : Readonly<PassthroughProps & ExternalProps>
20+
}
21+

tests/baselines/reference/mappedTypeRelationships.errors.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,29 +35,37 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(66,5): error TS2
3535
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(72,5): error TS2322: Type 'Partial<T>' is not assignable to type 'T'.
3636
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(78,5): error TS2322: Type 'Partial<Thing>' is not assignable to type 'Partial<T>'.
3737
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(88,5): error TS2322: Type 'Readonly<Thing>' is not assignable to type 'Readonly<T>'.
38+
Type 'Readonly<Thing>' is not assignable to type 'T'.
3839
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(127,5): error TS2322: Type 'Partial<U>' is not assignable to type 'Identity<U>'.
40+
Type 'Partial<U>' is not assignable to type 'U'.
3941
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(143,5): error TS2322: Type '{ [P in keyof T]: T[P]; }' is not assignable to type '{ [P in keyof T]: U[P]; }'.
4042
Type 'T[P]' is not assignable to type 'U[P]'.
4143
Type 'T' is not assignable to type 'U'.
44+
Type '{ [P in keyof T]: T[P]; }' is not assignable to type 'U'.
4245
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(148,5): error TS2322: Type '{ [P in keyof T]: T[P]; }' is not assignable to type '{ [P in keyof U]: U[P]; }'.
4346
Type 'keyof U' is not assignable to type 'keyof T'.
4447
Type 'string | number | symbol' is not assignable to type 'keyof T'.
4548
Type 'string' is not assignable to type 'keyof T'.
49+
Type '{ [P in keyof T]: T[P]; }' is not assignable to type 'U'.
4650
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(153,5): error TS2322: Type '{ [P in K]: T[P]; }' is not assignable to type '{ [P in keyof T]: T[P]; }'.
4751
Type 'keyof T' is not assignable to type 'K'.
4852
Type 'string | number | symbol' is not assignable to type 'K'.
4953
Type 'string' is not assignable to type 'K'.
54+
Type '{ [P in K]: T[P]; }' is not assignable to type 'T'.
5055
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(158,5): error TS2322: Type '{ [P in K]: T[P]; }' is not assignable to type '{ [P in keyof U]: U[P]; }'.
5156
Type 'keyof U' is not assignable to type 'K'.
5257
Type 'string | number | symbol' is not assignable to type 'K'.
5358
Type 'string' is not assignable to type 'K'.
59+
Type '{ [P in K]: T[P]; }' is not assignable to type 'U'.
5460
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(163,5): error TS2322: Type '{ [P in K]: T[P]; }' is not assignable to type '{ [P in keyof T]: U[P]; }'.
5561
Type 'keyof T' is not assignable to type 'K'.
5662
Type 'string | number | symbol' is not assignable to type 'K'.
5763
Type 'string' is not assignable to type 'K'.
64+
Type '{ [P in K]: T[P]; }' is not assignable to type 'U'.
5865
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(168,5): error TS2322: Type '{ [P in K]: T[P]; }' is not assignable to type '{ [P in K]: U[P]; }'.
5966
Type 'T[P]' is not assignable to type 'U[P]'.
6067
Type 'T' is not assignable to type 'U'.
68+
Type '{ [P in K]: T[P]; }' is not assignable to type 'U'.
6169

6270

6371
==== tests/cases/conformance/types/mapped/mappedTypeRelationships.ts (30 errors) ====
@@ -209,6 +217,7 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(168,5): error TS
209217
y = x; // Error
210218
~
211219
!!! error TS2322: Type 'Readonly<Thing>' is not assignable to type 'Readonly<T>'.
220+
!!! error TS2322: Type 'Readonly<Thing>' is not assignable to type 'T'.
212221
}
213222

214223
type Item = {
@@ -250,6 +259,7 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(168,5): error TS
250259
x = y; // Error
251260
~
252261
!!! error TS2322: Type 'Partial<U>' is not assignable to type 'Identity<U>'.
262+
!!! error TS2322: Type 'Partial<U>' is not assignable to type 'U'.
253263
y = x;
254264
}
255265

@@ -270,6 +280,7 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(168,5): error TS
270280
!!! error TS2322: Type '{ [P in keyof T]: T[P]; }' is not assignable to type '{ [P in keyof T]: U[P]; }'.
271281
!!! error TS2322: Type 'T[P]' is not assignable to type 'U[P]'.
272282
!!! error TS2322: Type 'T' is not assignable to type 'U'.
283+
!!! error TS2322: Type '{ [P in keyof T]: T[P]; }' is not assignable to type 'U'.
273284
}
274285

275286
function f72<T, U extends T>(x: { [P in keyof T]: T[P] }, y: { [P in keyof U]: U[P] }) {
@@ -280,6 +291,7 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(168,5): error TS
280291
!!! error TS2322: Type 'keyof U' is not assignable to type 'keyof T'.
281292
!!! error TS2322: Type 'string | number | symbol' is not assignable to type 'keyof T'.
282293
!!! error TS2322: Type 'string' is not assignable to type 'keyof T'.
294+
!!! error TS2322: Type '{ [P in keyof T]: T[P]; }' is not assignable to type 'U'.
283295
}
284296

285297
function f73<T, K extends keyof T>(x: { [P in K]: T[P] }, y: { [P in keyof T]: T[P] }) {
@@ -290,6 +302,7 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(168,5): error TS
290302
!!! error TS2322: Type 'keyof T' is not assignable to type 'K'.
291303
!!! error TS2322: Type 'string | number | symbol' is not assignable to type 'K'.
292304
!!! error TS2322: Type 'string' is not assignable to type 'K'.
305+
!!! error TS2322: Type '{ [P in K]: T[P]; }' is not assignable to type 'T'.
293306
}
294307

295308
function f74<T, U extends T, K extends keyof T>(x: { [P in K]: T[P] }, y: { [P in keyof U]: U[P] }) {
@@ -300,6 +313,7 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(168,5): error TS
300313
!!! error TS2322: Type 'keyof U' is not assignable to type 'K'.
301314
!!! error TS2322: Type 'string | number | symbol' is not assignable to type 'K'.
302315
!!! error TS2322: Type 'string' is not assignable to type 'K'.
316+
!!! error TS2322: Type '{ [P in K]: T[P]; }' is not assignable to type 'U'.
303317
}
304318

305319
function f75<T, U extends T, K extends keyof T>(x: { [P in K]: T[P] }, y: { [P in keyof T]: U[P] }) {
@@ -310,6 +324,7 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(168,5): error TS
310324
!!! error TS2322: Type 'keyof T' is not assignable to type 'K'.
311325
!!! error TS2322: Type 'string | number | symbol' is not assignable to type 'K'.
312326
!!! error TS2322: Type 'string' is not assignable to type 'K'.
327+
!!! error TS2322: Type '{ [P in K]: T[P]; }' is not assignable to type 'U'.
313328
}
314329

315330
function f76<T, U extends T, K extends keyof T>(x: { [P in K]: T[P] }, y: { [P in K]: U[P] }) {
@@ -319,6 +334,7 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(168,5): error TS
319334
!!! error TS2322: Type '{ [P in K]: T[P]; }' is not assignable to type '{ [P in K]: U[P]; }'.
320335
!!! error TS2322: Type 'T[P]' is not assignable to type 'U[P]'.
321336
!!! error TS2322: Type 'T' is not assignable to type 'U'.
337+
!!! error TS2322: Type '{ [P in K]: T[P]; }' is not assignable to type 'U'.
322338
}
323339

324340
function f80<T>(t: T): Partial<T> {

tests/baselines/reference/mappedTypes5.errors.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
tests/cases/conformance/types/mapped/mappedTypes5.ts(6,9): error TS2322: Type 'Partial<T>' is not assignable to type 'Readonly<T>'.
2+
Type 'Partial<T>' is not assignable to type 'T'.
23
tests/cases/conformance/types/mapped/mappedTypes5.ts(8,9): error TS2322: Type 'Partial<Readonly<T>>' is not assignable to type 'Readonly<T>'.
4+
Type 'Partial<Readonly<T>>' is not assignable to type 'T'.
35
tests/cases/conformance/types/mapped/mappedTypes5.ts(9,9): error TS2322: Type 'Readonly<Partial<T>>' is not assignable to type 'Readonly<T>'.
6+
Type 'Readonly<Partial<T>>' is not assignable to type 'T'.
7+
Type 'T[P] | undefined' is not assignable to type 'T[P]'.
8+
Type 'undefined' is not assignable to type 'T[P]'.
49

510

611
==== tests/cases/conformance/types/mapped/mappedTypes5.ts (3 errors) ====
@@ -12,13 +17,18 @@ tests/cases/conformance/types/mapped/mappedTypes5.ts(9,9): error TS2322: Type 'R
1217
let b1: Readonly<T> = p; // Error
1318
~~
1419
!!! error TS2322: Type 'Partial<T>' is not assignable to type 'Readonly<T>'.
20+
!!! error TS2322: Type 'Partial<T>' is not assignable to type 'T'.
1521
let b2: Readonly<T> = r;
1622
let b3: Readonly<T> = pr; // Error
1723
~~
1824
!!! error TS2322: Type 'Partial<Readonly<T>>' is not assignable to type 'Readonly<T>'.
25+
!!! error TS2322: Type 'Partial<Readonly<T>>' is not assignable to type 'T'.
1926
let b4: Readonly<T> = rp; // Error
2027
~~
2128
!!! error TS2322: Type 'Readonly<Partial<T>>' is not assignable to type 'Readonly<T>'.
29+
!!! error TS2322: Type 'Readonly<Partial<T>>' is not assignable to type 'T'.
30+
!!! error TS2322: Type 'T[P] | undefined' is not assignable to type 'T[P]'.
31+
!!! error TS2322: Type 'undefined' is not assignable to type 'T[P]'.
2232
let c1: Partial<Readonly<T>> = p;
2333
let c2: Partial<Readonly<T>> = r;
2434
let c3: Partial<Readonly<T>> = pr;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Repro for #27484
2+
// @target: es6
3+
function makePropsForWrappedComponent<PassthroughProps, ExternalProps, InjectedProps>(
4+
outerProps: Readonly<PassthroughProps & ExternalProps>,
5+
injectedProps: Readonly<InjectedProps>): Readonly<PassthroughProps & InjectedProps> {
6+
return Object.assign({}, injectedProps, outerProps);
7+
}

0 commit comments

Comments
 (0)