From bf7258742eccd1691c139417715e3eb5e7d18d54 Mon Sep 17 00:00:00 2001
From: Anders Hejlsberg <andersh@microsoft.com>
Date: Thu, 12 Jan 2017 10:49:44 -0800
Subject: [PATCH 1/3] Improve type relationships for generic mapped types

---
 src/compiler/checker.ts | 35 +++++++++++++++--------------------
 1 file changed, 15 insertions(+), 20 deletions(-)

diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index cc4afc8aa3eca..d8456b8074ca4 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(<MappedType>type);
@@ -7765,25 +7761,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 = !(<MappedType>source).declaration.readonlyToken === !(<MappedType>target).declaration.readonlyToken;
-                            const optionalMatches = !(<MappedType>source).declaration.questionToken === !(<MappedType>target).declaration.questionToken;
-                            if (readonlyMatches && optionalMatches) {
-                                if (result = isRelatedTo(getConstraintTypeFromMappedType(<MappedType>target), getConstraintTypeFromMappedType(<MappedType>source), reportErrors)) {
-                                    return result & isRelatedTo(getErasedTemplateTypeFromMappedType(<MappedType>source), getErasedTemplateTypeFromMappedType(<MappedType>target), reportErrors);
-                                }
-                            }
-                        }
-                        else {
-                            if (relation === comparableRelation || !(<MappedType>source).declaration.questionToken || (<MappedType>target).declaration.questionToken) {
-                                if (result = isRelatedTo(getConstraintTypeFromMappedType(<MappedType>target), getConstraintTypeFromMappedType(<MappedType>source), reportErrors)) {
-                                    return result & isRelatedTo(getTemplateTypeFromMappedType(<MappedType>source), getTemplateTypeFromMappedType(<MappedType>target), reportErrors);
-                                }
+                        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);
                             }
                         }
                     }

From dafea7f54d46cf7715cffa466649e48d51684c54 Mon Sep 17 00:00:00 2001
From: Anders Hejlsberg <andersh@microsoft.com>
Date: Thu, 12 Jan 2017 10:49:58 -0800
Subject: [PATCH 2/3] Add tests

---
 .../types/mapped/mappedTypeRelationships.ts   | 64 ++++++++++++++++++-
 1 file changed, 63 insertions(+), 1 deletion(-)

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<T extends ItemMap>(obj: T, key: keyof T) {
 function f51<T extends ItemMap, K extends keyof T>(obj: T, key: K) {
     let item: Item = obj[key];
     return obj[key].name;
-}
\ No newline at end of file
+}
+
+type T1<T> = {
+    [P in keyof T]: T[P];
+}
+
+type T2<T> = {
+    [P in keyof T]: T[P];
+}
+
+function f60<U>(x: T1<U>, y: T2<U>) {
+    x = y;
+    y = x;
+}
+
+type Identity<T> = {
+    [P in keyof T]: T[P];
+}
+
+function f61<U>(x: Identity<U>, y: Partial<U>) {
+    x = y;  // Error
+    y = x;
+}
+
+function f62<U>(x: Identity<U>, y: Readonly<U>) {
+    x = y;
+    y = x;
+}
+
+function f70<T>(x: { [P in keyof T]: T[P] }, y: { [P in keyof T]: T[P] }) {
+    x = y;
+    y = x;
+}
+
+function f71<T, U extends T>(x: { [P in keyof T]: T[P] }, y: { [P in keyof T]: U[P] }) {
+    x = y;
+    y = x;  // Error
+}
+
+function f72<T, U extends T>(x: { [P in keyof T]: T[P] }, y: { [P in keyof U]: U[P] }) {
+    x = y;
+    y = x;  // Error
+}
+
+function f73<T, K extends keyof T>(x: { [P in K]: T[P] }, y: { [P in keyof T]: T[P] }) {
+    x = y;
+    y = x;  // Error
+}
+
+function f74<T, U extends T, K extends keyof T>(x: { [P in K]: T[P] }, y: { [P in keyof U]: U[P] }) {
+    x = y;
+    y = x;  // Error
+}
+
+function f75<T, U extends T, K extends keyof T>(x: { [P in K]: T[P] }, y: { [P in keyof T]: U[P] }) {
+    x = y;
+    y = x;  // Error
+}
+
+function f76<T, U extends T, K extends keyof T>(x: { [P in K]: T[P] }, y: { [P in K]: U[P] }) {
+    x = y;
+    y = x;  // Error
+}

From 1f8b9f8bbeab6a244bfcb5fa292bf4261a196b20 Mon Sep 17 00:00:00 2001
From: Anders Hejlsberg <andersh@microsoft.com>
Date: Thu, 12 Jan 2017 10:50:08 -0800
Subject: [PATCH 3/3] Accept new baselines

---
 .../mappedTypeRelationships.errors.txt        | 104 +++++++++++-
 .../reference/mappedTypeRelationships.js      | 152 +++++++++++++++++-
 2 files changed, 253 insertions(+), 3 deletions(-)

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<U>' only permits reading.
 tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(71,5): error TS2322: Type 'Partial<T>' is not assignable to type 'T'.
 tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(76,5): error TS2322: Type 'Partial<T>' is not assignable to type 'T'.
+tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(126,5): error TS2322: Type 'Partial<U>' is not assignable to type 'Identity<U>'.
+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<T>(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<T extends ItemMap, K extends keyof T>(obj: T, key: K) {
         let item: Item = obj[key];
         return obj[key].name;
-    }
\ No newline at end of file
+    }
+    
+    type T1<T> = {
+        [P in keyof T]: T[P];
+    }
+    
+    type T2<T> = {
+        [P in keyof T]: T[P];
+    }
+    
+    function f60<U>(x: T1<U>, y: T2<U>) {
+        x = y;
+        y = x;
+    }
+    
+    type Identity<T> = {
+        [P in keyof T]: T[P];
+    }
+    
+    function f61<U>(x: Identity<U>, y: Partial<U>) {
+        x = y;  // Error
+        ~
+!!! error TS2322: Type 'Partial<U>' is not assignable to type 'Identity<U>'.
+        y = x;
+    }
+    
+    function f62<U>(x: Identity<U>, y: Readonly<U>) {
+        x = y;
+        y = x;
+    }
+    
+    function f70<T>(x: { [P in keyof T]: T[P] }, y: { [P in keyof T]: T[P] }) {
+        x = y;
+        y = x;
+    }
+    
+    function f71<T, U extends T>(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<T, U extends T>(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<T, K extends keyof T>(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<T, U extends T, K extends keyof T>(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<T, U extends T, K extends keyof T>(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<T, U extends T, K extends keyof T>(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<T extends ItemMap>(obj: T, key: keyof T) {
 function f51<T extends ItemMap, K extends keyof T>(obj: T, key: K) {
     let item: Item = obj[key];
     return obj[key].name;
-}
+}
+
+type T1<T> = {
+    [P in keyof T]: T[P];
+}
+
+type T2<T> = {
+    [P in keyof T]: T[P];
+}
+
+function f60<U>(x: T1<U>, y: T2<U>) {
+    x = y;
+    y = x;
+}
+
+type Identity<T> = {
+    [P in keyof T]: T[P];
+}
+
+function f61<U>(x: Identity<U>, y: Partial<U>) {
+    x = y;  // Error
+    y = x;
+}
+
+function f62<U>(x: Identity<U>, y: Readonly<U>) {
+    x = y;
+    y = x;
+}
+
+function f70<T>(x: { [P in keyof T]: T[P] }, y: { [P in keyof T]: T[P] }) {
+    x = y;
+    y = x;
+}
+
+function f71<T, U extends T>(x: { [P in keyof T]: T[P] }, y: { [P in keyof T]: U[P] }) {
+    x = y;
+    y = x;  // Error
+}
+
+function f72<T, U extends T>(x: { [P in keyof T]: T[P] }, y: { [P in keyof U]: U[P] }) {
+    x = y;
+    y = x;  // Error
+}
+
+function f73<T, K extends keyof T>(x: { [P in K]: T[P] }, y: { [P in keyof T]: T[P] }) {
+    x = y;
+    y = x;  // Error
+}
+
+function f74<T, U extends T, K extends keyof T>(x: { [P in K]: T[P] }, y: { [P in keyof U]: U[P] }) {
+    x = y;
+    y = x;  // Error
+}
+
+function f75<T, U extends T, K extends keyof T>(x: { [P in K]: T[P] }, y: { [P in keyof T]: U[P] }) {
+    x = y;
+    y = x;  // Error
+}
+
+function f76<T, U extends T, K extends keyof T>(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<T extends ItemMap>(obj: T, key: keyof T): string;
 declare function f51<T extends ItemMap, K extends keyof T>(obj: T, key: K): string;
+declare type T1<T> = {
+    [P in keyof T]: T[P];
+};
+declare type T2<T> = {
+    [P in keyof T]: T[P];
+};
+declare function f60<U>(x: T1<U>, y: T2<U>): void;
+declare type Identity<T> = {
+    [P in keyof T]: T[P];
+};
+declare function f61<U>(x: Identity<U>, y: Partial<U>): void;
+declare function f62<U>(x: Identity<U>, y: Readonly<U>): void;
+declare function f70<T>(x: {
+    [P in keyof T]: T[P];
+}, y: {
+    [P in keyof T]: T[P];
+}): void;
+declare function f71<T, U extends T>(x: {
+    [P in keyof T]: T[P];
+}, y: {
+    [P in keyof T]: U[P];
+}): void;
+declare function f72<T, U extends T>(x: {
+    [P in keyof T]: T[P];
+}, y: {
+    [P in keyof U]: U[P];
+}): void;
+declare function f73<T, K extends keyof T>(x: {
+    [P in K]: T[P];
+}, y: {
+    [P in keyof T]: T[P];
+}): void;
+declare function f74<T, U extends T, K extends keyof T>(x: {
+    [P in K]: T[P];
+}, y: {
+    [P in keyof U]: U[P];
+}): void;
+declare function f75<T, U extends T, K extends keyof T>(x: {
+    [P in K]: T[P];
+}, y: {
+    [P in keyof T]: U[P];
+}): void;
+declare function f76<T, U extends T, K extends keyof T>(x: {
+    [P in K]: T[P];
+}, y: {
+    [P in K]: U[P];
+}): void;