Skip to content

Commit 2711303

Browse files
authored
Merge pull request #13448 from Microsoft/fixMappedTypeRelations
Improve generic mapped type relations
2 parents 0f49703 + 1f8b9f8 commit 2711303

File tree

4 files changed

+331
-24
lines changed

4 files changed

+331
-24
lines changed

Diff for: src/compiler/checker.ts

+15-20
Original file line numberDiff line numberDiff line change
@@ -4662,10 +4662,6 @@ namespace ts {
46624662
return type.modifiersType;
46634663
}
46644664

4665-
function getErasedTemplateTypeFromMappedType(type: MappedType) {
4666-
return instantiateType(getTemplateTypeFromMappedType(type), createTypeEraser([getTypeParameterFromMappedType(type)]));
4667-
}
4668-
46694665
function isGenericMappedType(type: Type) {
46704666
if (getObjectFlags(type) & ObjectFlags.Mapped) {
46714667
const constraintType = getConstraintTypeFromMappedType(<MappedType>type);
@@ -7764,25 +7760,24 @@ namespace ts {
77647760
return result;
77657761
}
77667762

7767-
// A type [P in S]: X is related to a type [P in T]: Y if T is related to S and X is related to Y.
7763+
// A type [P in S]: X is related to a type [Q in T]: Y if T is related to S and X' is
7764+
// related to Y, where X' is an instantiation of X in which P is replaced with Q. Notice
7765+
// that S and T are contra-variant whereas X and Y are co-variant.
77687766
function mappedTypeRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary {
77697767
if (isGenericMappedType(target)) {
77707768
if (isGenericMappedType(source)) {
7771-
let result: Ternary;
7772-
if (relation === identityRelation) {
7773-
const readonlyMatches = !(<MappedType>source).declaration.readonlyToken === !(<MappedType>target).declaration.readonlyToken;
7774-
const optionalMatches = !(<MappedType>source).declaration.questionToken === !(<MappedType>target).declaration.questionToken;
7775-
if (readonlyMatches && optionalMatches) {
7776-
if (result = isRelatedTo(getConstraintTypeFromMappedType(<MappedType>target), getConstraintTypeFromMappedType(<MappedType>source), reportErrors)) {
7777-
return result & isRelatedTo(getErasedTemplateTypeFromMappedType(<MappedType>source), getErasedTemplateTypeFromMappedType(<MappedType>target), reportErrors);
7778-
}
7779-
}
7780-
}
7781-
else {
7782-
if (relation === comparableRelation || !(<MappedType>source).declaration.questionToken || (<MappedType>target).declaration.questionToken) {
7783-
if (result = isRelatedTo(getConstraintTypeFromMappedType(<MappedType>target), getConstraintTypeFromMappedType(<MappedType>source), reportErrors)) {
7784-
return result & isRelatedTo(getTemplateTypeFromMappedType(<MappedType>source), getTemplateTypeFromMappedType(<MappedType>target), reportErrors);
7785-
}
7769+
const sourceReadonly = !!(<MappedType>source).declaration.readonlyToken;
7770+
const sourceOptional = !!(<MappedType>source).declaration.questionToken;
7771+
const targetReadonly = !!(<MappedType>target).declaration.readonlyToken;
7772+
const targetOptional = !!(<MappedType>target).declaration.questionToken;
7773+
const modifiersRelated = relation === identityRelation ?
7774+
sourceReadonly === targetReadonly && sourceOptional === targetOptional :
7775+
relation === comparableRelation || !sourceOptional || targetOptional;
7776+
if (modifiersRelated) {
7777+
let result: Ternary;
7778+
if (result = isRelatedTo(getConstraintTypeFromMappedType(<MappedType>target), getConstraintTypeFromMappedType(<MappedType>source), reportErrors)) {
7779+
const mapper = createTypeMapper([getTypeParameterFromMappedType(<MappedType>source)], [getTypeParameterFromMappedType(<MappedType>target)]);
7780+
return result & isRelatedTo(instantiateType(getTemplateTypeFromMappedType(<MappedType>source), mapper), getTemplateTypeFromMappedType(<MappedType>target), reportErrors);
77867781
}
77877782
}
77887783
}

Diff for: tests/baselines/reference/mappedTypeRelationships.errors.txt

+102-2
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,24 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(62,5): error TS2
3030
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(67,5): error TS2542: Index signature in type 'Readonly<U>' only permits reading.
3131
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(71,5): error TS2322: Type 'Partial<T>' is not assignable to type 'T'.
3232
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(76,5): error TS2322: Type 'Partial<T>' is not assignable to type 'T'.
33+
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(126,5): error TS2322: Type 'Partial<U>' is not assignable to type 'Identity<U>'.
34+
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(142,5): error TS2322: Type '{ [P in keyof T]: T[P]; }' is not assignable to type '{ [P in keyof T]: U[P]; }'.
35+
Type 'T[P]' is not assignable to type 'U[P]'.
36+
Type 'T' is not assignable to type 'U'.
37+
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(147,5): error TS2322: Type '{ [P in keyof T]: T[P]; }' is not assignable to type '{ [P in keyof U]: U[P]; }'.
38+
Type 'keyof U' is not assignable to type 'keyof T'.
39+
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(152,5): error TS2322: Type '{ [P in K]: T[P]; }' is not assignable to type '{ [P in keyof T]: T[P]; }'.
40+
Type 'keyof T' is not assignable to type 'K'.
41+
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(157,5): error TS2322: Type '{ [P in K]: T[P]; }' is not assignable to type '{ [P in keyof U]: U[P]; }'.
42+
Type 'keyof U' is not assignable to type 'K'.
43+
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(162,5): error TS2322: Type '{ [P in K]: T[P]; }' is not assignable to type '{ [P in keyof T]: U[P]; }'.
44+
Type 'keyof T' is not assignable to type 'K'.
45+
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(167,5): error TS2322: Type '{ [P in K]: T[P]; }' is not assignable to type '{ [P in K]: U[P]; }'.
46+
Type 'T[P]' is not assignable to type 'U[P]'.
47+
Type 'T' is not assignable to type 'U'.
3348

3449

35-
==== tests/cases/conformance/types/mapped/mappedTypeRelationships.ts (20 errors) ====
50+
==== tests/cases/conformance/types/mapped/mappedTypeRelationships.ts (27 errors) ====
3651

3752
function f1<T>(x: T, k: keyof T) {
3853
return x[k];
@@ -190,4 +205,89 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(76,5): error TS2
190205
function f51<T extends ItemMap, K extends keyof T>(obj: T, key: K) {
191206
let item: Item = obj[key];
192207
return obj[key].name;
193-
}
208+
}
209+
210+
type T1<T> = {
211+
[P in keyof T]: T[P];
212+
}
213+
214+
type T2<T> = {
215+
[P in keyof T]: T[P];
216+
}
217+
218+
function f60<U>(x: T1<U>, y: T2<U>) {
219+
x = y;
220+
y = x;
221+
}
222+
223+
type Identity<T> = {
224+
[P in keyof T]: T[P];
225+
}
226+
227+
function f61<U>(x: Identity<U>, y: Partial<U>) {
228+
x = y; // Error
229+
~
230+
!!! error TS2322: Type 'Partial<U>' is not assignable to type 'Identity<U>'.
231+
y = x;
232+
}
233+
234+
function f62<U>(x: Identity<U>, y: Readonly<U>) {
235+
x = y;
236+
y = x;
237+
}
238+
239+
function f70<T>(x: { [P in keyof T]: T[P] }, y: { [P in keyof T]: T[P] }) {
240+
x = y;
241+
y = x;
242+
}
243+
244+
function f71<T, U extends T>(x: { [P in keyof T]: T[P] }, y: { [P in keyof T]: U[P] }) {
245+
x = y;
246+
y = x; // Error
247+
~
248+
!!! error TS2322: Type '{ [P in keyof T]: T[P]; }' is not assignable to type '{ [P in keyof T]: U[P]; }'.
249+
!!! error TS2322: Type 'T[P]' is not assignable to type 'U[P]'.
250+
!!! error TS2322: Type 'T' is not assignable to type 'U'.
251+
}
252+
253+
function f72<T, U extends T>(x: { [P in keyof T]: T[P] }, y: { [P in keyof U]: U[P] }) {
254+
x = y;
255+
y = x; // Error
256+
~
257+
!!! error TS2322: Type '{ [P in keyof T]: T[P]; }' is not assignable to type '{ [P in keyof U]: U[P]; }'.
258+
!!! error TS2322: Type 'keyof U' is not assignable to type 'keyof T'.
259+
}
260+
261+
function f73<T, K extends keyof T>(x: { [P in K]: T[P] }, y: { [P in keyof T]: T[P] }) {
262+
x = y;
263+
y = x; // Error
264+
~
265+
!!! error TS2322: Type '{ [P in K]: T[P]; }' is not assignable to type '{ [P in keyof T]: T[P]; }'.
266+
!!! error TS2322: Type 'keyof T' is not assignable to type 'K'.
267+
}
268+
269+
function f74<T, U extends T, K extends keyof T>(x: { [P in K]: T[P] }, y: { [P in keyof U]: U[P] }) {
270+
x = y;
271+
y = x; // Error
272+
~
273+
!!! error TS2322: Type '{ [P in K]: T[P]; }' is not assignable to type '{ [P in keyof U]: U[P]; }'.
274+
!!! error TS2322: Type 'keyof U' is not assignable to type 'K'.
275+
}
276+
277+
function f75<T, U extends T, K extends keyof T>(x: { [P in K]: T[P] }, y: { [P in keyof T]: U[P] }) {
278+
x = y;
279+
y = x; // Error
280+
~
281+
!!! error TS2322: Type '{ [P in K]: T[P]; }' is not assignable to type '{ [P in keyof T]: U[P]; }'.
282+
!!! error TS2322: Type 'keyof T' is not assignable to type 'K'.
283+
}
284+
285+
function f76<T, U extends T, K extends keyof T>(x: { [P in K]: T[P] }, y: { [P in K]: U[P] }) {
286+
x = y;
287+
y = x; // Error
288+
~
289+
!!! error TS2322: Type '{ [P in K]: T[P]; }' is not assignable to type '{ [P in K]: U[P]; }'.
290+
!!! error TS2322: Type 'T[P]' is not assignable to type 'U[P]'.
291+
!!! error TS2322: Type 'T' is not assignable to type 'U'.
292+
}
293+

Diff for: tests/baselines/reference/mappedTypeRelationships.js

+151-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,70 @@ function f50<T extends ItemMap>(obj: T, key: keyof T) {
104104
function f51<T extends ItemMap, K extends keyof T>(obj: T, key: K) {
105105
let item: Item = obj[key];
106106
return obj[key].name;
107-
}
107+
}
108+
109+
type T1<T> = {
110+
[P in keyof T]: T[P];
111+
}
112+
113+
type T2<T> = {
114+
[P in keyof T]: T[P];
115+
}
116+
117+
function f60<U>(x: T1<U>, y: T2<U>) {
118+
x = y;
119+
y = x;
120+
}
121+
122+
type Identity<T> = {
123+
[P in keyof T]: T[P];
124+
}
125+
126+
function f61<U>(x: Identity<U>, y: Partial<U>) {
127+
x = y; // Error
128+
y = x;
129+
}
130+
131+
function f62<U>(x: Identity<U>, y: Readonly<U>) {
132+
x = y;
133+
y = x;
134+
}
135+
136+
function f70<T>(x: { [P in keyof T]: T[P] }, y: { [P in keyof T]: T[P] }) {
137+
x = y;
138+
y = x;
139+
}
140+
141+
function f71<T, U extends T>(x: { [P in keyof T]: T[P] }, y: { [P in keyof T]: U[P] }) {
142+
x = y;
143+
y = x; // Error
144+
}
145+
146+
function f72<T, U extends T>(x: { [P in keyof T]: T[P] }, y: { [P in keyof U]: U[P] }) {
147+
x = y;
148+
y = x; // Error
149+
}
150+
151+
function f73<T, K extends keyof T>(x: { [P in K]: T[P] }, y: { [P in keyof T]: T[P] }) {
152+
x = y;
153+
y = x; // Error
154+
}
155+
156+
function f74<T, U extends T, K extends keyof T>(x: { [P in K]: T[P] }, y: { [P in keyof U]: U[P] }) {
157+
x = y;
158+
y = x; // Error
159+
}
160+
161+
function f75<T, U extends T, K extends keyof T>(x: { [P in K]: T[P] }, y: { [P in keyof T]: U[P] }) {
162+
x = y;
163+
y = x; // Error
164+
}
165+
166+
function f76<T, U extends T, K extends keyof T>(x: { [P in K]: T[P] }, y: { [P in K]: U[P] }) {
167+
x = y;
168+
y = x; // Error
169+
}
170+
108171

109172
//// [mappedTypeRelationships.js]
110173
function f1(x, k) {
@@ -185,6 +248,46 @@ function f51(obj, key) {
185248
var item = obj[key];
186249
return obj[key].name;
187250
}
251+
function f60(x, y) {
252+
x = y;
253+
y = x;
254+
}
255+
function f61(x, y) {
256+
x = y; // Error
257+
y = x;
258+
}
259+
function f62(x, y) {
260+
x = y;
261+
y = x;
262+
}
263+
function f70(x, y) {
264+
x = y;
265+
y = x;
266+
}
267+
function f71(x, y) {
268+
x = y;
269+
y = x; // Error
270+
}
271+
function f72(x, y) {
272+
x = y;
273+
y = x; // Error
274+
}
275+
function f73(x, y) {
276+
x = y;
277+
y = x; // Error
278+
}
279+
function f74(x, y) {
280+
x = y;
281+
y = x; // Error
282+
}
283+
function f75(x, y) {
284+
x = y;
285+
y = x; // Error
286+
}
287+
function f76(x, y) {
288+
x = y;
289+
y = x; // Error
290+
}
188291

189292

190293
//// [mappedTypeRelationships.d.ts]
@@ -214,3 +317,50 @@ declare type ItemMap = {
214317
};
215318
declare function f50<T extends ItemMap>(obj: T, key: keyof T): string;
216319
declare function f51<T extends ItemMap, K extends keyof T>(obj: T, key: K): string;
320+
declare type T1<T> = {
321+
[P in keyof T]: T[P];
322+
};
323+
declare type T2<T> = {
324+
[P in keyof T]: T[P];
325+
};
326+
declare function f60<U>(x: T1<U>, y: T2<U>): void;
327+
declare type Identity<T> = {
328+
[P in keyof T]: T[P];
329+
};
330+
declare function f61<U>(x: Identity<U>, y: Partial<U>): void;
331+
declare function f62<U>(x: Identity<U>, y: Readonly<U>): void;
332+
declare function f70<T>(x: {
333+
[P in keyof T]: T[P];
334+
}, y: {
335+
[P in keyof T]: T[P];
336+
}): void;
337+
declare function f71<T, U extends T>(x: {
338+
[P in keyof T]: T[P];
339+
}, y: {
340+
[P in keyof T]: U[P];
341+
}): void;
342+
declare function f72<T, U extends T>(x: {
343+
[P in keyof T]: T[P];
344+
}, y: {
345+
[P in keyof U]: U[P];
346+
}): void;
347+
declare function f73<T, K extends keyof T>(x: {
348+
[P in K]: T[P];
349+
}, y: {
350+
[P in keyof T]: T[P];
351+
}): void;
352+
declare function f74<T, U extends T, K extends keyof T>(x: {
353+
[P in K]: T[P];
354+
}, y: {
355+
[P in keyof U]: U[P];
356+
}): void;
357+
declare function f75<T, U extends T, K extends keyof T>(x: {
358+
[P in K]: T[P];
359+
}, y: {
360+
[P in keyof T]: U[P];
361+
}): void;
362+
declare function f76<T, U extends T, K extends keyof T>(x: {
363+
[P in K]: T[P];
364+
}, y: {
365+
[P in K]: U[P];
366+
}): void;

0 commit comments

Comments
 (0)