Merge pull request #17382 from Microsoft/fixPartialTypeRelations

Fix partial type relations
This commit is contained in:
Anders Hejlsberg 2017-07-27 17:31:55 -07:00 committed by GitHub
commit e7e6475440
8 changed files with 203 additions and 65 deletions

View File

@ -5787,12 +5787,13 @@ namespace ts {
return type.modifiersType;
}
function isPartialMappedType(type: Type) {
return getObjectFlags(type) & ObjectFlags.Mapped && !!(<MappedType>type).declaration.questionToken;
}
function isGenericMappedType(type: Type) {
if (getObjectFlags(type) & ObjectFlags.Mapped) {
const constraintType = getConstraintTypeFromMappedType(<MappedType>type);
return maybeTypeOfKind(constraintType, TypeFlags.TypeVariable | TypeFlags.Index);
}
return false;
return getObjectFlags(type) & ObjectFlags.Mapped &&
maybeTypeOfKind(getConstraintTypeFromMappedType(<MappedType>type), TypeFlags.TypeVariable | TypeFlags.Index);
}
function resolveStructuredTypeMembers(type: StructuredType): ResolvedType {
@ -9280,8 +9281,12 @@ namespace ts {
if (source.flags & (TypeFlags.Object | TypeFlags.Intersection) && target.flags & TypeFlags.Object) {
// Report structural errors only if we haven't reported any errors yet
const reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo && !sourceIsPrimitive;
if (isGenericMappedType(source) || isGenericMappedType(target)) {
result = mappedTypeRelatedTo(source, target, reportStructuralErrors);
// An empty object type is related to any mapped type that includes a '?' modifier.
if (isPartialMappedType(target) && !isGenericMappedType(source) && isEmptyObjectType(source)) {
result = Ternary.True;
}
else if (isGenericMappedType(target)) {
result = isGenericMappedType(source) ? mappedTypeRelatedTo(<MappedType>source, <MappedType>target, reportStructuralErrors) : Ternary.False;
}
else {
result = propertiesRelatedTo(source, target, reportStructuralErrors);
@ -9310,33 +9315,19 @@ namespace ts {
// A type [P in S]: X is related to a type [Q in T]: Y if T is related to S and X' is
// related to Y, where X' is an instantiation of X in which P is replaced with Q. Notice
// that S and T are contra-variant whereas X and Y are co-variant.
function mappedTypeRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary {
if (isGenericMappedType(target)) {
if (isGenericMappedType(source)) {
const sourceReadonly = !!(<MappedType>source).declaration.readonlyToken;
const sourceOptional = !!(<MappedType>source).declaration.questionToken;
const targetReadonly = !!(<MappedType>target).declaration.readonlyToken;
const targetOptional = !!(<MappedType>target).declaration.questionToken;
const modifiersRelated = relation === identityRelation ?
sourceReadonly === targetReadonly && sourceOptional === targetOptional :
relation === comparableRelation || !sourceOptional || targetOptional;
if (modifiersRelated) {
let result: Ternary;
if (result = isRelatedTo(getConstraintTypeFromMappedType(<MappedType>target), getConstraintTypeFromMappedType(<MappedType>source), reportErrors)) {
const mapper = createTypeMapper([getTypeParameterFromMappedType(<MappedType>source)], [getTypeParameterFromMappedType(<MappedType>target)]);
return result & isRelatedTo(instantiateType(getTemplateTypeFromMappedType(<MappedType>source), mapper), getTemplateTypeFromMappedType(<MappedType>target), reportErrors);
}
}
}
else if ((<MappedType>target).declaration.questionToken && isEmptyObjectType(source)) {
return Ternary.True;
}
}
else if (relation !== identityRelation) {
const resolved = resolveStructuredTypeMembers(<ObjectType>target);
if (isEmptyResolvedType(resolved) || resolved.stringIndexInfo && resolved.stringIndexInfo.type.flags & TypeFlags.Any) {
return Ternary.True;
function mappedTypeRelatedTo(source: MappedType, target: MappedType, reportErrors: boolean): Ternary {
const sourceReadonly = !!source.declaration.readonlyToken;
const sourceOptional = !!source.declaration.questionToken;
const targetReadonly = !!target.declaration.readonlyToken;
const targetOptional = !!target.declaration.questionToken;
const modifiersRelated = relation === identityRelation ?
sourceReadonly === targetReadonly && sourceOptional === targetOptional :
relation === comparableRelation || !sourceOptional || targetOptional;
if (modifiersRelated) {
let result: Ternary;
if (result = isRelatedTo(getConstraintTypeFromMappedType(<MappedType>target), getConstraintTypeFromMappedType(<MappedType>source), reportErrors)) {
const mapper = createTypeMapper([getTypeParameterFromMappedType(<MappedType>source)], [getTypeParameterFromMappedType(<MappedType>target)]);
return result & isRelatedTo(instantiateType(getTemplateTypeFromMappedType(<MappedType>source), mapper), getTemplateTypeFromMappedType(<MappedType>target), reportErrors);
}
}
return Ternary.False;

View File

@ -0,0 +1,46 @@
//// [mappedTypePartialConstraints.ts]
// Repro from #16985
interface MyInterface {
something: number;
}
class MyClass<T extends MyInterface> {
doIt(data : Partial<T>) {}
}
class MySubClass extends MyClass<MyInterface> {}
function fn(arg: typeof MyClass) {};
fn(MySubClass);
//// [mappedTypePartialConstraints.js]
// Repro from #16985
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var MyClass = (function () {
function MyClass() {
}
MyClass.prototype.doIt = function (data) { };
return MyClass;
}());
var MySubClass = (function (_super) {
__extends(MySubClass, _super);
function MySubClass() {
return _super !== null && _super.apply(this, arguments) || this;
}
return MySubClass;
}(MyClass));
function fn(arg) { }
;
fn(MySubClass);

View File

@ -0,0 +1,36 @@
=== tests/cases/compiler/mappedTypePartialConstraints.ts ===
// Repro from #16985
interface MyInterface {
>MyInterface : Symbol(MyInterface, Decl(mappedTypePartialConstraints.ts, 0, 0))
something: number;
>something : Symbol(MyInterface.something, Decl(mappedTypePartialConstraints.ts, 2, 23))
}
class MyClass<T extends MyInterface> {
>MyClass : Symbol(MyClass, Decl(mappedTypePartialConstraints.ts, 4, 1))
>T : Symbol(T, Decl(mappedTypePartialConstraints.ts, 6, 14))
>MyInterface : Symbol(MyInterface, Decl(mappedTypePartialConstraints.ts, 0, 0))
doIt(data : Partial<T>) {}
>doIt : Symbol(MyClass.doIt, Decl(mappedTypePartialConstraints.ts, 6, 38))
>data : Symbol(data, Decl(mappedTypePartialConstraints.ts, 7, 7))
>Partial : Symbol(Partial, Decl(lib.d.ts, --, --))
>T : Symbol(T, Decl(mappedTypePartialConstraints.ts, 6, 14))
}
class MySubClass extends MyClass<MyInterface> {}
>MySubClass : Symbol(MySubClass, Decl(mappedTypePartialConstraints.ts, 8, 1))
>MyClass : Symbol(MyClass, Decl(mappedTypePartialConstraints.ts, 4, 1))
>MyInterface : Symbol(MyInterface, Decl(mappedTypePartialConstraints.ts, 0, 0))
function fn(arg: typeof MyClass) {};
>fn : Symbol(fn, Decl(mappedTypePartialConstraints.ts, 10, 48))
>arg : Symbol(arg, Decl(mappedTypePartialConstraints.ts, 12, 12))
>MyClass : Symbol(MyClass, Decl(mappedTypePartialConstraints.ts, 4, 1))
fn(MySubClass);
>fn : Symbol(fn, Decl(mappedTypePartialConstraints.ts, 10, 48))
>MySubClass : Symbol(MySubClass, Decl(mappedTypePartialConstraints.ts, 8, 1))

View File

@ -0,0 +1,37 @@
=== tests/cases/compiler/mappedTypePartialConstraints.ts ===
// Repro from #16985
interface MyInterface {
>MyInterface : MyInterface
something: number;
>something : number
}
class MyClass<T extends MyInterface> {
>MyClass : MyClass<T>
>T : T
>MyInterface : MyInterface
doIt(data : Partial<T>) {}
>doIt : (data: Partial<T>) => void
>data : Partial<T>
>Partial : Partial<T>
>T : T
}
class MySubClass extends MyClass<MyInterface> {}
>MySubClass : MySubClass
>MyClass : MyClass<MyInterface>
>MyInterface : MyInterface
function fn(arg: typeof MyClass) {};
>fn : (arg: typeof MyClass) => void
>arg : typeof MyClass
>MyClass : typeof MyClass
fn(MySubClass);
>fn(MySubClass) : void
>fn : (arg: typeof MyClass) => void
>MySubClass : typeof MySubClass

View File

@ -60,25 +60,26 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(51,5): error TS2
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(56,5): error TS2542: Index signature in type 'Readonly<T>' only permits reading.
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(61,5): error TS2542: Index signature in type 'Readonly<U>' only permits reading.
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(66,5): error TS2542: Index signature in type 'Readonly<U>' only permits reading.
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(70,5): error TS2322: Type 'Partial<T>' is not assignable to type 'T'.
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(75,5): error TS2322: Type 'Partial<T>' is not assignable to type 'T'.
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(125,5): error TS2322: Type 'Partial<U>' is not assignable to type 'Identity<U>'.
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]; }'.
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(72,5): error TS2322: Type 'Partial<T>' is not assignable to type 'T'.
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(78,5): error TS2322: Type 'Partial<Thing>' is not assignable to type 'Partial<T>'.
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(88,5): error TS2322: Type 'Readonly<Thing>' is not assignable to type 'Readonly<T>'.
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(127,5): error TS2322: Type 'Partial<U>' is not assignable to type 'Identity<U>'.
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]; }'.
Type 'T[P]' is not assignable to type 'U[P]'.
Type 'T[string]' is not assignable to type 'U[P]'.
Type 'T[string]' is not assignable to type 'U[string]'.
Type 'T[P]' is not assignable to type 'U[string]'.
Type 'T[string]' is not assignable to type 'U[string]'.
Type 'T' is not assignable to type 'U'.
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]; }'.
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]; }'.
Type 'keyof U' is not assignable to type 'keyof T'.
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]; }'.
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]; }'.
Type 'keyof T' is not assignable to type 'K'.
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]; }'.
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]; }'.
Type 'keyof U' is not assignable to type 'K'.
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]; }'.
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]; }'.
Type 'keyof T' is not assignable to type 'K'.
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]; }'.
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]; }'.
Type 'T[P]' is not assignable to type 'U[P]'.
Type 'T[string]' is not assignable to type 'U[P]'.
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
Type 'T' is not assignable to type 'U'.
==== tests/cases/conformance/types/mapped/mappedTypeRelationships.ts (27 errors) ====
==== tests/cases/conformance/types/mapped/mappedTypeRelationships.ts (28 errors) ====
function f1<T>(x: T, k: keyof T) {
return x[k];
}
@ -236,6 +237,8 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(166,5): error TS
!!! error TS2542: Index signature in type 'Readonly<U>' only permits reading.
}
type Thing = { a: string, b: string };
function f30<T>(x: T, y: Partial<T>) {
x = y; // Error
~
@ -243,11 +246,11 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(166,5): error TS
y = x;
}
function f31<T>(x: T, y: Partial<T>) {
x = y; // Error
function f31<T extends Thing>(x: Partial<Thing>, y: Partial<T>) {
x = y;
y = x; // Error
~
!!! error TS2322: Type 'Partial<T>' is not assignable to type 'T'.
y = x;
!!! error TS2322: Type 'Partial<Thing>' is not assignable to type 'Partial<T>'.
}
function f40<T>(x: T, y: Readonly<T>) {
@ -255,9 +258,11 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(166,5): error TS
y = x;
}
function f41<T>(x: T, y: Readonly<T>) {
function f41<T extends Thing>(x: Readonly<Thing>, y: Readonly<T>) {
x = y;
y = x;
y = x; // Error
~
!!! error TS2322: Type 'Readonly<Thing>' is not assignable to type 'Readonly<T>'.
}
type Item = {

View File

@ -67,14 +67,16 @@ function f23<T, U extends T, K extends keyof T>(x: T, y: Readonly<U>, k: K) {
y[k] = x[k]; // Error
}
type Thing = { a: string, b: string };
function f30<T>(x: T, y: Partial<T>) {
x = y; // Error
y = x;
}
function f31<T>(x: T, y: Partial<T>) {
x = y; // Error
y = x;
function f31<T extends Thing>(x: Partial<Thing>, y: Partial<T>) {
x = y;
y = x; // Error
}
function f40<T>(x: T, y: Readonly<T>) {
@ -82,9 +84,9 @@ function f40<T>(x: T, y: Readonly<T>) {
y = x;
}
function f41<T>(x: T, y: Readonly<T>) {
function f41<T extends Thing>(x: Readonly<Thing>, y: Readonly<T>) {
x = y;
y = x;
y = x; // Error
}
type Item = {
@ -228,8 +230,8 @@ function f30(x, y) {
y = x;
}
function f31(x, y) {
x = y; // Error
y = x;
x = y;
y = x; // Error
}
function f40(x, y) {
x = y;
@ -237,7 +239,7 @@ function f40(x, y) {
}
function f41(x, y) {
x = y;
y = x;
y = x; // Error
}
function f50(obj, key) {
var item = obj[key];
@ -304,10 +306,14 @@ declare function f20<T>(x: T, y: Readonly<T>, k: keyof T): void;
declare function f21<T, K extends keyof T>(x: T, y: Readonly<T>, k: K): void;
declare function f22<T, U extends T>(x: T, y: Readonly<U>, k: keyof T): void;
declare function f23<T, U extends T, K extends keyof T>(x: T, y: Readonly<U>, k: K): void;
declare type Thing = {
a: string;
b: string;
};
declare function f30<T>(x: T, y: Partial<T>): void;
declare function f31<T>(x: T, y: Partial<T>): void;
declare function f31<T extends Thing>(x: Partial<Thing>, y: Partial<T>): void;
declare function f40<T>(x: T, y: Readonly<T>): void;
declare function f41<T>(x: T, y: Readonly<T>): void;
declare function f41<T extends Thing>(x: Readonly<Thing>, y: Readonly<T>): void;
declare type Item = {
name: string;
};

View File

@ -0,0 +1,15 @@
// Repro from #16985
interface MyInterface {
something: number;
}
class MyClass<T extends MyInterface> {
doIt(data : Partial<T>) {}
}
class MySubClass extends MyClass<MyInterface> {}
function fn(arg: typeof MyClass) {};
fn(MySubClass);

View File

@ -69,14 +69,16 @@ function f23<T, U extends T, K extends keyof T>(x: T, y: Readonly<U>, k: K) {
y[k] = x[k]; // Error
}
type Thing = { a: string, b: string };
function f30<T>(x: T, y: Partial<T>) {
x = y; // Error
y = x;
}
function f31<T>(x: T, y: Partial<T>) {
x = y; // Error
y = x;
function f31<T extends Thing>(x: Partial<Thing>, y: Partial<T>) {
x = y;
y = x; // Error
}
function f40<T>(x: T, y: Readonly<T>) {
@ -84,9 +86,9 @@ function f40<T>(x: T, y: Readonly<T>) {
y = x;
}
function f41<T>(x: T, y: Readonly<T>) {
function f41<T extends Thing>(x: Readonly<Thing>, y: Readonly<T>) {
x = y;
y = x;
y = x; // Error
}
type Item = {