Skip to content

Commit e7e6475

Browse files
authored
Merge pull request #17382 from Microsoft/fixPartialTypeRelations
Fix partial type relations
2 parents d9172dc + 1d9c3e1 commit e7e6475

File tree

8 files changed

+203
-65
lines changed

8 files changed

+203
-65
lines changed

src/compiler/checker.ts

Lines changed: 25 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5787,12 +5787,13 @@ namespace ts {
57875787
return type.modifiersType;
57885788
}
57895789

5790+
function isPartialMappedType(type: Type) {
5791+
return getObjectFlags(type) & ObjectFlags.Mapped && !!(<MappedType>type).declaration.questionToken;
5792+
}
5793+
57905794
function isGenericMappedType(type: Type) {
5791-
if (getObjectFlags(type) & ObjectFlags.Mapped) {
5792-
const constraintType = getConstraintTypeFromMappedType(<MappedType>type);
5793-
return maybeTypeOfKind(constraintType, TypeFlags.TypeVariable | TypeFlags.Index);
5794-
}
5795-
return false;
5795+
return getObjectFlags(type) & ObjectFlags.Mapped &&
5796+
maybeTypeOfKind(getConstraintTypeFromMappedType(<MappedType>type), TypeFlags.TypeVariable | TypeFlags.Index);
57965797
}
57975798

57985799
function resolveStructuredTypeMembers(type: StructuredType): ResolvedType {
@@ -9280,8 +9281,12 @@ namespace ts {
92809281
if (source.flags & (TypeFlags.Object | TypeFlags.Intersection) && target.flags & TypeFlags.Object) {
92819282
// Report structural errors only if we haven't reported any errors yet
92829283
const reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo && !sourceIsPrimitive;
9283-
if (isGenericMappedType(source) || isGenericMappedType(target)) {
9284-
result = mappedTypeRelatedTo(source, target, reportStructuralErrors);
9284+
// An empty object type is related to any mapped type that includes a '?' modifier.
9285+
if (isPartialMappedType(target) && !isGenericMappedType(source) && isEmptyObjectType(source)) {
9286+
result = Ternary.True;
9287+
}
9288+
else if (isGenericMappedType(target)) {
9289+
result = isGenericMappedType(source) ? mappedTypeRelatedTo(<MappedType>source, <MappedType>target, reportStructuralErrors) : Ternary.False;
92859290
}
92869291
else {
92879292
result = propertiesRelatedTo(source, target, reportStructuralErrors);
@@ -9310,33 +9315,19 @@ namespace ts {
93109315
// A type [P in S]: X is related to a type [Q in T]: Y if T is related to S and X' is
93119316
// related to Y, where X' is an instantiation of X in which P is replaced with Q. Notice
93129317
// that S and T are contra-variant whereas X and Y are co-variant.
9313-
function mappedTypeRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary {
9314-
if (isGenericMappedType(target)) {
9315-
if (isGenericMappedType(source)) {
9316-
const sourceReadonly = !!(<MappedType>source).declaration.readonlyToken;
9317-
const sourceOptional = !!(<MappedType>source).declaration.questionToken;
9318-
const targetReadonly = !!(<MappedType>target).declaration.readonlyToken;
9319-
const targetOptional = !!(<MappedType>target).declaration.questionToken;
9320-
const modifiersRelated = relation === identityRelation ?
9321-
sourceReadonly === targetReadonly && sourceOptional === targetOptional :
9322-
relation === comparableRelation || !sourceOptional || targetOptional;
9323-
if (modifiersRelated) {
9324-
let result: Ternary;
9325-
if (result = isRelatedTo(getConstraintTypeFromMappedType(<MappedType>target), getConstraintTypeFromMappedType(<MappedType>source), reportErrors)) {
9326-
const mapper = createTypeMapper([getTypeParameterFromMappedType(<MappedType>source)], [getTypeParameterFromMappedType(<MappedType>target)]);
9327-
return result & isRelatedTo(instantiateType(getTemplateTypeFromMappedType(<MappedType>source), mapper), getTemplateTypeFromMappedType(<MappedType>target), reportErrors);
9328-
}
9329-
}
9330-
}
9331-
else if ((<MappedType>target).declaration.questionToken && isEmptyObjectType(source)) {
9332-
return Ternary.True;
9333-
9334-
}
9335-
}
9336-
else if (relation !== identityRelation) {
9337-
const resolved = resolveStructuredTypeMembers(<ObjectType>target);
9338-
if (isEmptyResolvedType(resolved) || resolved.stringIndexInfo && resolved.stringIndexInfo.type.flags & TypeFlags.Any) {
9339-
return Ternary.True;
9318+
function mappedTypeRelatedTo(source: MappedType, target: MappedType, reportErrors: boolean): Ternary {
9319+
const sourceReadonly = !!source.declaration.readonlyToken;
9320+
const sourceOptional = !!source.declaration.questionToken;
9321+
const targetReadonly = !!target.declaration.readonlyToken;
9322+
const targetOptional = !!target.declaration.questionToken;
9323+
const modifiersRelated = relation === identityRelation ?
9324+
sourceReadonly === targetReadonly && sourceOptional === targetOptional :
9325+
relation === comparableRelation || !sourceOptional || targetOptional;
9326+
if (modifiersRelated) {
9327+
let result: Ternary;
9328+
if (result = isRelatedTo(getConstraintTypeFromMappedType(<MappedType>target), getConstraintTypeFromMappedType(<MappedType>source), reportErrors)) {
9329+
const mapper = createTypeMapper([getTypeParameterFromMappedType(<MappedType>source)], [getTypeParameterFromMappedType(<MappedType>target)]);
9330+
return result & isRelatedTo(instantiateType(getTemplateTypeFromMappedType(<MappedType>source), mapper), getTemplateTypeFromMappedType(<MappedType>target), reportErrors);
93409331
}
93419332
}
93429333
return Ternary.False;
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//// [mappedTypePartialConstraints.ts]
2+
// Repro from #16985
3+
4+
interface MyInterface {
5+
something: number;
6+
}
7+
8+
class MyClass<T extends MyInterface> {
9+
doIt(data : Partial<T>) {}
10+
}
11+
12+
class MySubClass extends MyClass<MyInterface> {}
13+
14+
function fn(arg: typeof MyClass) {};
15+
16+
fn(MySubClass);
17+
18+
19+
//// [mappedTypePartialConstraints.js]
20+
// Repro from #16985
21+
var __extends = (this && this.__extends) || (function () {
22+
var extendStatics = Object.setPrototypeOf ||
23+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
24+
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
25+
return function (d, b) {
26+
extendStatics(d, b);
27+
function __() { this.constructor = d; }
28+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
29+
};
30+
})();
31+
var MyClass = (function () {
32+
function MyClass() {
33+
}
34+
MyClass.prototype.doIt = function (data) { };
35+
return MyClass;
36+
}());
37+
var MySubClass = (function (_super) {
38+
__extends(MySubClass, _super);
39+
function MySubClass() {
40+
return _super !== null && _super.apply(this, arguments) || this;
41+
}
42+
return MySubClass;
43+
}(MyClass));
44+
function fn(arg) { }
45+
;
46+
fn(MySubClass);
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
=== tests/cases/compiler/mappedTypePartialConstraints.ts ===
2+
// Repro from #16985
3+
4+
interface MyInterface {
5+
>MyInterface : Symbol(MyInterface, Decl(mappedTypePartialConstraints.ts, 0, 0))
6+
7+
something: number;
8+
>something : Symbol(MyInterface.something, Decl(mappedTypePartialConstraints.ts, 2, 23))
9+
}
10+
11+
class MyClass<T extends MyInterface> {
12+
>MyClass : Symbol(MyClass, Decl(mappedTypePartialConstraints.ts, 4, 1))
13+
>T : Symbol(T, Decl(mappedTypePartialConstraints.ts, 6, 14))
14+
>MyInterface : Symbol(MyInterface, Decl(mappedTypePartialConstraints.ts, 0, 0))
15+
16+
doIt(data : Partial<T>) {}
17+
>doIt : Symbol(MyClass.doIt, Decl(mappedTypePartialConstraints.ts, 6, 38))
18+
>data : Symbol(data, Decl(mappedTypePartialConstraints.ts, 7, 7))
19+
>Partial : Symbol(Partial, Decl(lib.d.ts, --, --))
20+
>T : Symbol(T, Decl(mappedTypePartialConstraints.ts, 6, 14))
21+
}
22+
23+
class MySubClass extends MyClass<MyInterface> {}
24+
>MySubClass : Symbol(MySubClass, Decl(mappedTypePartialConstraints.ts, 8, 1))
25+
>MyClass : Symbol(MyClass, Decl(mappedTypePartialConstraints.ts, 4, 1))
26+
>MyInterface : Symbol(MyInterface, Decl(mappedTypePartialConstraints.ts, 0, 0))
27+
28+
function fn(arg: typeof MyClass) {};
29+
>fn : Symbol(fn, Decl(mappedTypePartialConstraints.ts, 10, 48))
30+
>arg : Symbol(arg, Decl(mappedTypePartialConstraints.ts, 12, 12))
31+
>MyClass : Symbol(MyClass, Decl(mappedTypePartialConstraints.ts, 4, 1))
32+
33+
fn(MySubClass);
34+
>fn : Symbol(fn, Decl(mappedTypePartialConstraints.ts, 10, 48))
35+
>MySubClass : Symbol(MySubClass, Decl(mappedTypePartialConstraints.ts, 8, 1))
36+
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
=== tests/cases/compiler/mappedTypePartialConstraints.ts ===
2+
// Repro from #16985
3+
4+
interface MyInterface {
5+
>MyInterface : MyInterface
6+
7+
something: number;
8+
>something : number
9+
}
10+
11+
class MyClass<T extends MyInterface> {
12+
>MyClass : MyClass<T>
13+
>T : T
14+
>MyInterface : MyInterface
15+
16+
doIt(data : Partial<T>) {}
17+
>doIt : (data: Partial<T>) => void
18+
>data : Partial<T>
19+
>Partial : Partial<T>
20+
>T : T
21+
}
22+
23+
class MySubClass extends MyClass<MyInterface> {}
24+
>MySubClass : MySubClass
25+
>MyClass : MyClass<MyInterface>
26+
>MyInterface : MyInterface
27+
28+
function fn(arg: typeof MyClass) {};
29+
>fn : (arg: typeof MyClass) => void
30+
>arg : typeof MyClass
31+
>MyClass : typeof MyClass
32+
33+
fn(MySubClass);
34+
>fn(MySubClass) : void
35+
>fn : (arg: typeof MyClass) => void
36+
>MySubClass : typeof MySubClass
37+

tests/baselines/reference/mappedTypeRelationships.errors.txt

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -60,25 +60,26 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(51,5): error TS2
6060
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(56,5): error TS2542: Index signature in type 'Readonly<T>' only permits reading.
6161
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(61,5): error TS2542: Index signature in type 'Readonly<U>' only permits reading.
6262
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(66,5): error TS2542: Index signature in type 'Readonly<U>' only permits reading.
63-
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(70,5): error TS2322: Type 'Partial<T>' is not assignable to type 'T'.
64-
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(75,5): error TS2322: Type 'Partial<T>' is not assignable to type 'T'.
65-
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(125,5): error TS2322: Type 'Partial<U>' is not assignable to type 'Identity<U>'.
66-
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(141,5): error TS2322: Type '{ [P in keyof T]: T[P]; }' is not assignable to type '{ [P in keyof T]: U[P]; }'.
63+
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(72,5): error TS2322: Type 'Partial<T>' is not assignable to type 'T'.
64+
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(78,5): error TS2322: Type 'Partial<Thing>' is not assignable to type 'Partial<T>'.
65+
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(88,5): error TS2322: Type 'Readonly<Thing>' is not assignable to type 'Readonly<T>'.
66+
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(127,5): error TS2322: Type 'Partial<U>' is not assignable to type 'Identity<U>'.
67+
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]; }'.
6768
Type 'T[P]' is not assignable to type 'U[P]'.
6869
Type 'T[string]' is not assignable to type 'U[P]'.
6970
Type 'T[string]' is not assignable to type 'U[string]'.
7071
Type 'T[P]' is not assignable to type 'U[string]'.
7172
Type 'T[string]' is not assignable to type 'U[string]'.
7273
Type 'T' is not assignable to type 'U'.
73-
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(146,5): error TS2322: Type '{ [P in keyof T]: T[P]; }' is not assignable to type '{ [P in keyof U]: U[P]; }'.
74+
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]; }'.
7475
Type 'keyof U' is not assignable to type 'keyof T'.
75-
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(151,5): error TS2322: Type '{ [P in K]: T[P]; }' is not assignable to type '{ [P in keyof T]: T[P]; }'.
76+
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]; }'.
7677
Type 'keyof T' is not assignable to type 'K'.
77-
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(156,5): error TS2322: Type '{ [P in K]: T[P]; }' is not assignable to type '{ [P in keyof U]: U[P]; }'.
78+
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]; }'.
7879
Type 'keyof U' is not assignable to type 'K'.
79-
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(161,5): error TS2322: Type '{ [P in K]: T[P]; }' is not assignable to type '{ [P in keyof T]: U[P]; }'.
80+
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]; }'.
8081
Type 'keyof T' is not assignable to type 'K'.
81-
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(166,5): error TS2322: Type '{ [P in K]: T[P]; }' is not assignable to type '{ [P in K]: U[P]; }'.
82+
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]; }'.
8283
Type 'T[P]' is not assignable to type 'U[P]'.
8384
Type 'T[string]' is not assignable to type 'U[P]'.
8485
Type 'T[string]' is not assignable to type 'U[string]'.
@@ -87,7 +88,7 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(166,5): error TS
8788
Type 'T' is not assignable to type 'U'.
8889

8990

90-
==== tests/cases/conformance/types/mapped/mappedTypeRelationships.ts (27 errors) ====
91+
==== tests/cases/conformance/types/mapped/mappedTypeRelationships.ts (28 errors) ====
9192
function f1<T>(x: T, k: keyof T) {
9293
return x[k];
9394
}
@@ -236,28 +237,32 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(166,5): error TS
236237
!!! error TS2542: Index signature in type 'Readonly<U>' only permits reading.
237238
}
238239

240+
type Thing = { a: string, b: string };
241+
239242
function f30<T>(x: T, y: Partial<T>) {
240243
x = y; // Error
241244
~
242245
!!! error TS2322: Type 'Partial<T>' is not assignable to type 'T'.
243246
y = x;
244247
}
245248

246-
function f31<T>(x: T, y: Partial<T>) {
247-
x = y; // Error
249+
function f31<T extends Thing>(x: Partial<Thing>, y: Partial<T>) {
250+
x = y;
251+
y = x; // Error
248252
~
249-
!!! error TS2322: Type 'Partial<T>' is not assignable to type 'T'.
250-
y = x;
253+
!!! error TS2322: Type 'Partial<Thing>' is not assignable to type 'Partial<T>'.
251254
}
252255

253256
function f40<T>(x: T, y: Readonly<T>) {
254257
x = y;
255258
y = x;
256259
}
257260

258-
function f41<T>(x: T, y: Readonly<T>) {
261+
function f41<T extends Thing>(x: Readonly<Thing>, y: Readonly<T>) {
259262
x = y;
260-
y = x;
263+
y = x; // Error
264+
~
265+
!!! error TS2322: Type 'Readonly<Thing>' is not assignable to type 'Readonly<T>'.
261266
}
262267

263268
type Item = {

tests/baselines/reference/mappedTypeRelationships.js

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -67,24 +67,26 @@ function f23<T, U extends T, K extends keyof T>(x: T, y: Readonly<U>, k: K) {
6767
y[k] = x[k]; // Error
6868
}
6969

70+
type Thing = { a: string, b: string };
71+
7072
function f30<T>(x: T, y: Partial<T>) {
7173
x = y; // Error
7274
y = x;
7375
}
7476

75-
function f31<T>(x: T, y: Partial<T>) {
76-
x = y; // Error
77-
y = x;
77+
function f31<T extends Thing>(x: Partial<Thing>, y: Partial<T>) {
78+
x = y;
79+
y = x; // Error
7880
}
7981

8082
function f40<T>(x: T, y: Readonly<T>) {
8183
x = y;
8284
y = x;
8385
}
8486

85-
function f41<T>(x: T, y: Readonly<T>) {
87+
function f41<T extends Thing>(x: Readonly<Thing>, y: Readonly<T>) {
8688
x = y;
87-
y = x;
89+
y = x; // Error
8890
}
8991

9092
type Item = {
@@ -228,16 +230,16 @@ function f30(x, y) {
228230
y = x;
229231
}
230232
function f31(x, y) {
231-
x = y; // Error
232-
y = x;
233+
x = y;
234+
y = x; // Error
233235
}
234236
function f40(x, y) {
235237
x = y;
236238
y = x;
237239
}
238240
function f41(x, y) {
239241
x = y;
240-
y = x;
242+
y = x; // Error
241243
}
242244
function f50(obj, key) {
243245
var item = obj[key];
@@ -304,10 +306,14 @@ declare function f20<T>(x: T, y: Readonly<T>, k: keyof T): void;
304306
declare function f21<T, K extends keyof T>(x: T, y: Readonly<T>, k: K): void;
305307
declare function f22<T, U extends T>(x: T, y: Readonly<U>, k: keyof T): void;
306308
declare function f23<T, U extends T, K extends keyof T>(x: T, y: Readonly<U>, k: K): void;
309+
declare type Thing = {
310+
a: string;
311+
b: string;
312+
};
307313
declare function f30<T>(x: T, y: Partial<T>): void;
308-
declare function f31<T>(x: T, y: Partial<T>): void;
314+
declare function f31<T extends Thing>(x: Partial<Thing>, y: Partial<T>): void;
309315
declare function f40<T>(x: T, y: Readonly<T>): void;
310-
declare function f41<T>(x: T, y: Readonly<T>): void;
316+
declare function f41<T extends Thing>(x: Readonly<Thing>, y: Readonly<T>): void;
311317
declare type Item = {
312318
name: string;
313319
};
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Repro from #16985
2+
3+
interface MyInterface {
4+
something: number;
5+
}
6+
7+
class MyClass<T extends MyInterface> {
8+
doIt(data : Partial<T>) {}
9+
}
10+
11+
class MySubClass extends MyClass<MyInterface> {}
12+
13+
function fn(arg: typeof MyClass) {};
14+
15+
fn(MySubClass);

0 commit comments

Comments
 (0)