From bb28444f8c35ab1d7ee8a671564f08d3fc183900 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 6 Feb 2018 12:27:35 -0800 Subject: [PATCH 1/3] Handle all instantiable non-primitive types in homomorphic mapped types --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a93189c81f2..c7cdb922cc6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8781,7 +8781,7 @@ namespace ts { } function isMappableType(type: Type) { - return type.flags & (TypeFlags.Any | TypeFlags.TypeParameter | TypeFlags.Object | TypeFlags.Intersection | TypeFlags.IndexedAccess); + return type.flags & (TypeFlags.Any | TypeFlags.InstantiableNonPrimitive | TypeFlags.Object | TypeFlags.Intersection); } function instantiateAnonymousType(type: AnonymousType, mapper: TypeMapper): AnonymousType { From cac6b5b98506a2c5e64b071fa87804bf03bdec32 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 6 Feb 2018 12:41:04 -0800 Subject: [PATCH 2/3] Add regression test --- .../types/conditional/inferTypes1.ts | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/cases/conformance/types/conditional/inferTypes1.ts b/tests/cases/conformance/types/conditional/inferTypes1.ts index 25552a68acf..6ff7ae67c89 100644 --- a/tests/cases/conformance/types/conditional/inferTypes1.ts +++ b/tests/cases/conformance/types/conditional/inferTypes1.ts @@ -73,3 +73,35 @@ type T54 = X3<{ a: (x: number) => void, b: () => void }>; // number type T60 = infer U; // Error type T61 = infer A extends infer B ? infer C : infer D; // Error type T62 = U extends (infer U)[] ? U : U; // Error + +// Example from #21496 + +type JsonifiedObject = { [K in keyof T]: Jsonified }; + +type Jsonified = + T extends string | number | boolean | null ? T + : T extends undefined | Function ? never // undefined and functions are removed + : T extends { toJSON(): infer R } ? R // toJSON is called if it exists (e.g. Date) + : T extends object ? JsonifiedObject + : "what is this"; + +type Example = { + str: "literalstring", + fn: () => void, + date: Date, + customClass: MyClass, + obj: { + prop: "property", + clz: MyClass, + nested: { attr: Date } + }, +} + +declare class MyClass { + toJSON(): "correct"; +} + +type JsonifiedExample = Jsonified; +declare let ex: JsonifiedExample; +const z1: "correct" = ex.customClass; +const z2: string = ex.obj.nested.attr; From efea19f9972ed3bce636e74d0af7f2ecf9332456 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 6 Feb 2018 12:41:12 -0800 Subject: [PATCH 3/3] Accept new baselines --- .../reference/inferTypes1.errors.txt | 32 ++++++ tests/baselines/reference/inferTypes1.js | 34 ++++++ tests/baselines/reference/inferTypes1.symbols | 103 +++++++++++++++++ tests/baselines/reference/inferTypes1.types | 104 ++++++++++++++++++ 4 files changed, 273 insertions(+) diff --git a/tests/baselines/reference/inferTypes1.errors.txt b/tests/baselines/reference/inferTypes1.errors.txt index 79a0e430cbd..ff93491dd1d 100644 --- a/tests/baselines/reference/inferTypes1.errors.txt +++ b/tests/baselines/reference/inferTypes1.errors.txt @@ -108,4 +108,36 @@ tests/cases/conformance/types/conditional/inferTypes1.ts(72,43): error TS4081: E !!! error TS2304: Cannot find name 'U'. ~ !!! error TS4081: Exported type alias 'T62' has or is using private name 'U'. + + // Example from #21496 + + type JsonifiedObject = { [K in keyof T]: Jsonified }; + + type Jsonified = + T extends string | number | boolean | null ? T + : T extends undefined | Function ? never // undefined and functions are removed + : T extends { toJSON(): infer R } ? R // toJSON is called if it exists (e.g. Date) + : T extends object ? JsonifiedObject + : "what is this"; + + type Example = { + str: "literalstring", + fn: () => void, + date: Date, + customClass: MyClass, + obj: { + prop: "property", + clz: MyClass, + nested: { attr: Date } + }, + } + + declare class MyClass { + toJSON(): "correct"; + } + + type JsonifiedExample = Jsonified; + declare let ex: JsonifiedExample; + const z1: "correct" = ex.customClass; + const z2: string = ex.obj.nested.attr; \ No newline at end of file diff --git a/tests/baselines/reference/inferTypes1.js b/tests/baselines/reference/inferTypes1.js index 231c39e8952..5a692fe8d05 100644 --- a/tests/baselines/reference/inferTypes1.js +++ b/tests/baselines/reference/inferTypes1.js @@ -71,6 +71,38 @@ type T54 = X3<{ a: (x: number) => void, b: () => void }>; // number type T60 = infer U; // Error type T61 = infer A extends infer B ? infer C : infer D; // Error type T62 = U extends (infer U)[] ? U : U; // Error + +// Example from #21496 + +type JsonifiedObject = { [K in keyof T]: Jsonified }; + +type Jsonified = + T extends string | number | boolean | null ? T + : T extends undefined | Function ? never // undefined and functions are removed + : T extends { toJSON(): infer R } ? R // toJSON is called if it exists (e.g. Date) + : T extends object ? JsonifiedObject + : "what is this"; + +type Example = { + str: "literalstring", + fn: () => void, + date: Date, + customClass: MyClass, + obj: { + prop: "property", + clz: MyClass, + nested: { attr: Date } + }, +} + +declare class MyClass { + toJSON(): "correct"; +} + +type JsonifiedExample = Jsonified; +declare let ex: JsonifiedExample; +const z1: "correct" = ex.customClass; +const z2: string = ex.obj.nested.attr; //// [inferTypes1.js] @@ -85,3 +117,5 @@ var C = /** @class */ (function () { } return C; }()); +var z1 = ex.customClass; +var z2 = ex.obj.nested.attr; diff --git a/tests/baselines/reference/inferTypes1.symbols b/tests/baselines/reference/inferTypes1.symbols index a85f124dede..18674d957a8 100644 --- a/tests/baselines/reference/inferTypes1.symbols +++ b/tests/baselines/reference/inferTypes1.symbols @@ -315,3 +315,106 @@ type T62 = U extends (infer U)[] ? U : U; // Error >U : Symbol(U, Decl(inferTypes1.ts, 71, 30)) >U : Symbol(U, Decl(inferTypes1.ts, 71, 30)) +// Example from #21496 + +type JsonifiedObject = { [K in keyof T]: Jsonified }; +>JsonifiedObject : Symbol(JsonifiedObject, Decl(inferTypes1.ts, 71, 44)) +>T : Symbol(T, Decl(inferTypes1.ts, 75, 21)) +>K : Symbol(K, Decl(inferTypes1.ts, 75, 44)) +>T : Symbol(T, Decl(inferTypes1.ts, 75, 21)) +>Jsonified : Symbol(Jsonified, Decl(inferTypes1.ts, 75, 77)) +>T : Symbol(T, Decl(inferTypes1.ts, 75, 21)) +>K : Symbol(K, Decl(inferTypes1.ts, 75, 44)) + +type Jsonified = +>Jsonified : Symbol(Jsonified, Decl(inferTypes1.ts, 75, 77)) +>T : Symbol(T, Decl(inferTypes1.ts, 77, 15)) + + T extends string | number | boolean | null ? T +>T : Symbol(T, Decl(inferTypes1.ts, 77, 15)) +>T : Symbol(T, Decl(inferTypes1.ts, 77, 15)) + + : T extends undefined | Function ? never // undefined and functions are removed +>T : Symbol(T, Decl(inferTypes1.ts, 77, 15)) +>Function : Symbol(Function, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + : T extends { toJSON(): infer R } ? R // toJSON is called if it exists (e.g. Date) +>T : Symbol(T, Decl(inferTypes1.ts, 77, 15)) +>toJSON : Symbol(toJSON, Decl(inferTypes1.ts, 80, 17)) +>R : Symbol(R, Decl(inferTypes1.ts, 80, 33)) +>R : Symbol(R, Decl(inferTypes1.ts, 80, 33)) + + : T extends object ? JsonifiedObject +>T : Symbol(T, Decl(inferTypes1.ts, 77, 15)) +>JsonifiedObject : Symbol(JsonifiedObject, Decl(inferTypes1.ts, 71, 44)) +>T : Symbol(T, Decl(inferTypes1.ts, 77, 15)) + + : "what is this"; + +type Example = { +>Example : Symbol(Example, Decl(inferTypes1.ts, 82, 21)) + + str: "literalstring", +>str : Symbol(str, Decl(inferTypes1.ts, 84, 16)) + + fn: () => void, +>fn : Symbol(fn, Decl(inferTypes1.ts, 85, 25)) + + date: Date, +>date : Symbol(date, Decl(inferTypes1.ts, 86, 19)) +>Date : Symbol(Date, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + customClass: MyClass, +>customClass : Symbol(customClass, Decl(inferTypes1.ts, 87, 15)) +>MyClass : Symbol(MyClass, Decl(inferTypes1.ts, 94, 1)) + + obj: { +>obj : Symbol(obj, Decl(inferTypes1.ts, 88, 25)) + + prop: "property", +>prop : Symbol(prop, Decl(inferTypes1.ts, 89, 10)) + + clz: MyClass, +>clz : Symbol(clz, Decl(inferTypes1.ts, 90, 25)) +>MyClass : Symbol(MyClass, Decl(inferTypes1.ts, 94, 1)) + + nested: { attr: Date } +>nested : Symbol(nested, Decl(inferTypes1.ts, 91, 21)) +>attr : Symbol(attr, Decl(inferTypes1.ts, 92, 17)) +>Date : Symbol(Date, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + }, +} + +declare class MyClass { +>MyClass : Symbol(MyClass, Decl(inferTypes1.ts, 94, 1)) + + toJSON(): "correct"; +>toJSON : Symbol(MyClass.toJSON, Decl(inferTypes1.ts, 96, 23)) +} + +type JsonifiedExample = Jsonified; +>JsonifiedExample : Symbol(JsonifiedExample, Decl(inferTypes1.ts, 98, 1)) +>Jsonified : Symbol(Jsonified, Decl(inferTypes1.ts, 75, 77)) +>Example : Symbol(Example, Decl(inferTypes1.ts, 82, 21)) + +declare let ex: JsonifiedExample; +>ex : Symbol(ex, Decl(inferTypes1.ts, 101, 11)) +>JsonifiedExample : Symbol(JsonifiedExample, Decl(inferTypes1.ts, 98, 1)) + +const z1: "correct" = ex.customClass; +>z1 : Symbol(z1, Decl(inferTypes1.ts, 102, 5)) +>ex.customClass : Symbol(customClass, Decl(inferTypes1.ts, 87, 15)) +>ex : Symbol(ex, Decl(inferTypes1.ts, 101, 11)) +>customClass : Symbol(customClass, Decl(inferTypes1.ts, 87, 15)) + +const z2: string = ex.obj.nested.attr; +>z2 : Symbol(z2, Decl(inferTypes1.ts, 103, 5)) +>ex.obj.nested.attr : Symbol(attr, Decl(inferTypes1.ts, 92, 17)) +>ex.obj.nested : Symbol(nested, Decl(inferTypes1.ts, 91, 21)) +>ex.obj : Symbol(obj, Decl(inferTypes1.ts, 88, 25)) +>ex : Symbol(ex, Decl(inferTypes1.ts, 101, 11)) +>obj : Symbol(obj, Decl(inferTypes1.ts, 88, 25)) +>nested : Symbol(nested, Decl(inferTypes1.ts, 91, 21)) +>attr : Symbol(attr, Decl(inferTypes1.ts, 92, 17)) + diff --git a/tests/baselines/reference/inferTypes1.types b/tests/baselines/reference/inferTypes1.types index be974f8661a..0b753c30f49 100644 --- a/tests/baselines/reference/inferTypes1.types +++ b/tests/baselines/reference/inferTypes1.types @@ -321,3 +321,107 @@ type T62 = U extends (infer U)[] ? U : U; // Error >U : U >U : No type information available! +// Example from #21496 + +type JsonifiedObject = { [K in keyof T]: Jsonified }; +>JsonifiedObject : JsonifiedObject +>T : T +>K : K +>T : T +>Jsonified : Jsonified +>T : T +>K : K + +type Jsonified = +>Jsonified : Jsonified +>T : T + + T extends string | number | boolean | null ? T +>T : T +>null : null +>T : T + + : T extends undefined | Function ? never // undefined and functions are removed +>T : T +>Function : Function + + : T extends { toJSON(): infer R } ? R // toJSON is called if it exists (e.g. Date) +>T : T +>toJSON : () => R +>R : R +>R : R + + : T extends object ? JsonifiedObject +>T : T +>JsonifiedObject : JsonifiedObject +>T : T + + : "what is this"; + +type Example = { +>Example : Example + + str: "literalstring", +>str : "literalstring" + + fn: () => void, +>fn : () => void + + date: Date, +>date : Date +>Date : Date + + customClass: MyClass, +>customClass : MyClass +>MyClass : MyClass + + obj: { +>obj : { prop: "property"; clz: MyClass; nested: { attr: Date; }; } + + prop: "property", +>prop : "property" + + clz: MyClass, +>clz : MyClass +>MyClass : MyClass + + nested: { attr: Date } +>nested : { attr: Date; } +>attr : Date +>Date : Date + + }, +} + +declare class MyClass { +>MyClass : MyClass + + toJSON(): "correct"; +>toJSON : () => "correct" +} + +type JsonifiedExample = Jsonified; +>JsonifiedExample : JsonifiedObject +>Jsonified : Jsonified +>Example : Example + +declare let ex: JsonifiedExample; +>ex : JsonifiedObject +>JsonifiedExample : JsonifiedObject + +const z1: "correct" = ex.customClass; +>z1 : "correct" +>ex.customClass : "correct" +>ex : JsonifiedObject +>customClass : "correct" + +const z2: string = ex.obj.nested.attr; +>z2 : string +>ex.obj.nested.attr : string +>ex.obj.nested : JsonifiedObject<{ attr: Date; }> +>ex.obj : JsonifiedObject<{ prop: "property"; clz: MyClass; nested: { attr: Date; }; }> +>ex : JsonifiedObject +>obj : JsonifiedObject<{ prop: "property"; clz: MyClass; nested: { attr: Date; }; }> +>nested : JsonifiedObject<{ attr: Date; }> +>attr : string +