diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e92ea973f8f18..9e4e81e3da918 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4662,10 +4662,6 @@ namespace ts { return type.modifiersType; } - function getErasedTemplateTypeFromMappedType(type: MappedType) { - return instantiateType(getTemplateTypeFromMappedType(type), createTypeEraser([getTypeParameterFromMappedType(type)])); - } - function isGenericMappedType(type: Type) { if (getObjectFlags(type) & ObjectFlags.Mapped) { const constraintType = getConstraintTypeFromMappedType(type); @@ -7764,25 +7760,24 @@ namespace ts { return result; } - // 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. + // 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)) { - let result: Ternary; - if (relation === identityRelation) { - const readonlyMatches = !(source).declaration.readonlyToken === !(target).declaration.readonlyToken; - const optionalMatches = !(source).declaration.questionToken === !(target).declaration.questionToken; - if (readonlyMatches && optionalMatches) { - if (result = isRelatedTo(getConstraintTypeFromMappedType(target), getConstraintTypeFromMappedType(source), reportErrors)) { - return result & isRelatedTo(getErasedTemplateTypeFromMappedType(source), getErasedTemplateTypeFromMappedType(target), reportErrors); - } - } - } - else { - if (relation === comparableRelation || !(source).declaration.questionToken || (target).declaration.questionToken) { - if (result = isRelatedTo(getConstraintTypeFromMappedType(target), getConstraintTypeFromMappedType(source), reportErrors)) { - return result & isRelatedTo(getTemplateTypeFromMappedType(source), getTemplateTypeFromMappedType(target), reportErrors); - } + 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(target), getConstraintTypeFromMappedType(source), reportErrors)) { + const mapper = createTypeMapper([getTypeParameterFromMappedType(source)], [getTypeParameterFromMappedType(target)]); + return result & isRelatedTo(instantiateType(getTemplateTypeFromMappedType(source), mapper), getTemplateTypeFromMappedType(target), reportErrors); } } } diff --git a/tests/baselines/reference/mappedTypeRelationships.errors.txt b/tests/baselines/reference/mappedTypeRelationships.errors.txt index ce6ecaa61fab8..22f56dacaf669 100644 --- a/tests/baselines/reference/mappedTypeRelationships.errors.txt +++ b/tests/baselines/reference/mappedTypeRelationships.errors.txt @@ -30,9 +30,24 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(62,5): error TS2 tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(67,5): error TS2542: Index signature in type 'Readonly' only permits reading. tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(71,5): error TS2322: Type 'Partial' is not assignable to type 'T'. tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(76,5): error TS2322: Type 'Partial' is not assignable to type 'T'. +tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(126,5): error TS2322: Type 'Partial' is not assignable to type 'Identity'. +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]; }'. + Type 'T[P]' is not assignable to type 'U[P]'. + Type 'T' is not assignable to type 'U'. +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]; }'. + Type 'keyof U' is not assignable to type 'keyof T'. +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]; }'. + Type 'keyof T' is not assignable to type 'K'. +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]; }'. + Type 'keyof U' is not assignable to type 'K'. +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]; }'. + Type 'keyof T' is not assignable to type 'K'. +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]; }'. + Type 'T[P]' is not assignable to type 'U[P]'. + Type 'T' is not assignable to type 'U'. -==== tests/cases/conformance/types/mapped/mappedTypeRelationships.ts (20 errors) ==== +==== tests/cases/conformance/types/mapped/mappedTypeRelationships.ts (27 errors) ==== function f1(x: T, k: keyof T) { return x[k]; @@ -190,4 +205,89 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(76,5): error TS2 function f51(obj: T, key: K) { let item: Item = obj[key]; return obj[key].name; - } \ No newline at end of file + } + + type T1 = { + [P in keyof T]: T[P]; + } + + type T2 = { + [P in keyof T]: T[P]; + } + + function f60(x: T1, y: T2) { + x = y; + y = x; + } + + type Identity = { + [P in keyof T]: T[P]; + } + + function f61(x: Identity, y: Partial) { + x = y; // Error + ~ +!!! error TS2322: Type 'Partial' is not assignable to type 'Identity'. + y = x; + } + + function f62(x: Identity, y: Readonly) { + x = y; + y = x; + } + + function f70(x: { [P in keyof T]: T[P] }, y: { [P in keyof T]: T[P] }) { + x = y; + y = x; + } + + function f71(x: { [P in keyof T]: T[P] }, y: { [P in keyof T]: U[P] }) { + x = y; + y = x; // Error + ~ +!!! error TS2322: Type '{ [P in keyof T]: T[P]; }' is not assignable to type '{ [P in keyof T]: U[P]; }'. +!!! error TS2322: Type 'T[P]' is not assignable to type 'U[P]'. +!!! error TS2322: Type 'T' is not assignable to type 'U'. + } + + function f72(x: { [P in keyof T]: T[P] }, y: { [P in keyof U]: U[P] }) { + x = y; + y = x; // Error + ~ +!!! error TS2322: Type '{ [P in keyof T]: T[P]; }' is not assignable to type '{ [P in keyof U]: U[P]; }'. +!!! error TS2322: Type 'keyof U' is not assignable to type 'keyof T'. + } + + function f73(x: { [P in K]: T[P] }, y: { [P in keyof T]: T[P] }) { + x = y; + y = x; // Error + ~ +!!! error TS2322: Type '{ [P in K]: T[P]; }' is not assignable to type '{ [P in keyof T]: T[P]; }'. +!!! error TS2322: Type 'keyof T' is not assignable to type 'K'. + } + + function f74(x: { [P in K]: T[P] }, y: { [P in keyof U]: U[P] }) { + x = y; + y = x; // Error + ~ +!!! error TS2322: Type '{ [P in K]: T[P]; }' is not assignable to type '{ [P in keyof U]: U[P]; }'. +!!! error TS2322: Type 'keyof U' is not assignable to type 'K'. + } + + function f75(x: { [P in K]: T[P] }, y: { [P in keyof T]: U[P] }) { + x = y; + y = x; // Error + ~ +!!! error TS2322: Type '{ [P in K]: T[P]; }' is not assignable to type '{ [P in keyof T]: U[P]; }'. +!!! error TS2322: Type 'keyof T' is not assignable to type 'K'. + } + + function f76(x: { [P in K]: T[P] }, y: { [P in K]: U[P] }) { + x = y; + y = x; // Error + ~ +!!! error TS2322: Type '{ [P in K]: T[P]; }' is not assignable to type '{ [P in K]: U[P]; }'. +!!! error TS2322: Type 'T[P]' is not assignable to type 'U[P]'. +!!! error TS2322: Type 'T' is not assignable to type 'U'. + } + \ No newline at end of file diff --git a/tests/baselines/reference/mappedTypeRelationships.js b/tests/baselines/reference/mappedTypeRelationships.js index a81ff973da874..2476b72cef659 100644 --- a/tests/baselines/reference/mappedTypeRelationships.js +++ b/tests/baselines/reference/mappedTypeRelationships.js @@ -104,7 +104,70 @@ function f50(obj: T, key: keyof T) { function f51(obj: T, key: K) { let item: Item = obj[key]; return obj[key].name; -} +} + +type T1 = { + [P in keyof T]: T[P]; +} + +type T2 = { + [P in keyof T]: T[P]; +} + +function f60(x: T1, y: T2) { + x = y; + y = x; +} + +type Identity = { + [P in keyof T]: T[P]; +} + +function f61(x: Identity, y: Partial) { + x = y; // Error + y = x; +} + +function f62(x: Identity, y: Readonly) { + x = y; + y = x; +} + +function f70(x: { [P in keyof T]: T[P] }, y: { [P in keyof T]: T[P] }) { + x = y; + y = x; +} + +function f71(x: { [P in keyof T]: T[P] }, y: { [P in keyof T]: U[P] }) { + x = y; + y = x; // Error +} + +function f72(x: { [P in keyof T]: T[P] }, y: { [P in keyof U]: U[P] }) { + x = y; + y = x; // Error +} + +function f73(x: { [P in K]: T[P] }, y: { [P in keyof T]: T[P] }) { + x = y; + y = x; // Error +} + +function f74(x: { [P in K]: T[P] }, y: { [P in keyof U]: U[P] }) { + x = y; + y = x; // Error +} + +function f75(x: { [P in K]: T[P] }, y: { [P in keyof T]: U[P] }) { + x = y; + y = x; // Error +} + +function f76(x: { [P in K]: T[P] }, y: { [P in K]: U[P] }) { + x = y; + y = x; // Error +} + //// [mappedTypeRelationships.js] function f1(x, k) { @@ -185,6 +248,46 @@ function f51(obj, key) { var item = obj[key]; return obj[key].name; } +function f60(x, y) { + x = y; + y = x; +} +function f61(x, y) { + x = y; // Error + y = x; +} +function f62(x, y) { + x = y; + y = x; +} +function f70(x, y) { + x = y; + y = x; +} +function f71(x, y) { + x = y; + y = x; // Error +} +function f72(x, y) { + x = y; + y = x; // Error +} +function f73(x, y) { + x = y; + y = x; // Error +} +function f74(x, y) { + x = y; + y = x; // Error +} +function f75(x, y) { + x = y; + y = x; // Error +} +function f76(x, y) { + x = y; + y = x; // Error +} //// [mappedTypeRelationships.d.ts] @@ -214,3 +317,50 @@ declare type ItemMap = { }; declare function f50(obj: T, key: keyof T): string; declare function f51(obj: T, key: K): string; +declare type T1 = { + [P in keyof T]: T[P]; +}; +declare type T2 = { + [P in keyof T]: T[P]; +}; +declare function f60(x: T1, y: T2): void; +declare type Identity = { + [P in keyof T]: T[P]; +}; +declare function f61(x: Identity, y: Partial): void; +declare function f62(x: Identity, y: Readonly): void; +declare function f70(x: { + [P in keyof T]: T[P]; +}, y: { + [P in keyof T]: T[P]; +}): void; +declare function f71(x: { + [P in keyof T]: T[P]; +}, y: { + [P in keyof T]: U[P]; +}): void; +declare function f72(x: { + [P in keyof T]: T[P]; +}, y: { + [P in keyof U]: U[P]; +}): void; +declare function f73(x: { + [P in K]: T[P]; +}, y: { + [P in keyof T]: T[P]; +}): void; +declare function f74(x: { + [P in K]: T[P]; +}, y: { + [P in keyof U]: U[P]; +}): void; +declare function f75(x: { + [P in K]: T[P]; +}, y: { + [P in keyof T]: U[P]; +}): void; +declare function f76(x: { + [P in K]: T[P]; +}, y: { + [P in K]: U[P]; +}): void; diff --git a/tests/cases/conformance/types/mapped/mappedTypeRelationships.ts b/tests/cases/conformance/types/mapped/mappedTypeRelationships.ts index 4af71fbea0804..587ec1c9d7e61 100644 --- a/tests/cases/conformance/types/mapped/mappedTypeRelationships.ts +++ b/tests/cases/conformance/types/mapped/mappedTypeRelationships.ts @@ -105,4 +105,66 @@ function f50(obj: T, key: keyof T) { function f51(obj: T, key: K) { let item: Item = obj[key]; return obj[key].name; -} \ No newline at end of file +} + +type T1 = { + [P in keyof T]: T[P]; +} + +type T2 = { + [P in keyof T]: T[P]; +} + +function f60(x: T1, y: T2) { + x = y; + y = x; +} + +type Identity = { + [P in keyof T]: T[P]; +} + +function f61(x: Identity, y: Partial) { + x = y; // Error + y = x; +} + +function f62(x: Identity, y: Readonly) { + x = y; + y = x; +} + +function f70(x: { [P in keyof T]: T[P] }, y: { [P in keyof T]: T[P] }) { + x = y; + y = x; +} + +function f71(x: { [P in keyof T]: T[P] }, y: { [P in keyof T]: U[P] }) { + x = y; + y = x; // Error +} + +function f72(x: { [P in keyof T]: T[P] }, y: { [P in keyof U]: U[P] }) { + x = y; + y = x; // Error +} + +function f73(x: { [P in K]: T[P] }, y: { [P in keyof T]: T[P] }) { + x = y; + y = x; // Error +} + +function f74(x: { [P in K]: T[P] }, y: { [P in keyof U]: U[P] }) { + x = y; + y = x; // Error +} + +function f75(x: { [P in K]: T[P] }, y: { [P in keyof T]: U[P] }) { + x = y; + y = x; // Error +} + +function f76(x: { [P in K]: T[P] }, y: { [P in K]: U[P] }) { + x = y; + y = x; // Error +}