From 6d1422b270dc2620b0829fcaad875bcf833610e3 Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Sun, 31 Dec 2023 22:28:18 +0100 Subject: [PATCH 1/8] better support for nameType skipping properties logic --- src/compiler/checker.ts | 3 +- ...TypeInferFromFilteringNameType2.errors.txt | 27 +++++++- ...pedTypeInferFromFilteringNameType2.symbols | 61 +++++++++++++++++++ ...appedTypeInferFromFilteringNameType2.types | 61 +++++++++++++++++++ ...seMappedTypeInferFromFilteringNameType2.ts | 21 +++++++ 5 files changed, 170 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2bce201e7e35b..2a55f3539c42a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13705,7 +13705,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } if (nameType) { const nameMapper = appendTypeMapping(type.mappedType.mapper, getTypeParameterFromMappedType(type.mappedType), propertyNameType); - const instantiatedNameType = instantiateType(nameType, nameMapper); + const typeParameterMapper = appendTypeMapping(nameMapper, type.constraintType.type, type.source); + const instantiatedNameType = instantiateType(nameType, typeParameterMapper); if (instantiatedNameType.flags & TypeFlags.Never) { continue; } diff --git a/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.errors.txt b/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.errors.txt index 0cab58fe12be9..2c99993f7ef7d 100644 --- a/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.errors.txt +++ b/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.errors.txt @@ -14,9 +14,10 @@ reverseMappedTypeInferFromFilteringNameType2.ts(85,36): error TS2353: Object lit reverseMappedTypeInferFromFilteringNameType2.ts(96,68): error TS2353: Object literal may only specify known properties, and 'extra' does not exist in type '{ prop: "foo"; nested: { prop: string; }; }'. reverseMappedTypeInferFromFilteringNameType2.ts(139,3): error TS2353: Object literal may only specify known properties, and 'extra' does not exist in type '{ readonly types: { actors: { src: "str"; logic: () => Promise; }; }; readonly invoke: { readonly src: "str"; }; }'. reverseMappedTypeInferFromFilteringNameType2.ts(145,3): error TS2353: Object literal may only specify known properties, and 'extra' does not exist in type '{ readonly invoke: { readonly src: "whatever"; }; }'. +reverseMappedTypeInferFromFilteringNameType2.ts(164,11): error TS2353: Object literal may only specify known properties, and 'a' does not exist in type 'Bombolo<{ enabled: false; }>'. -==== reverseMappedTypeInferFromFilteringNameType2.ts (13 errors) ==== +==== reverseMappedTypeInferFromFilteringNameType2.ts (14 errors) ==== type StateConfig = { entry?: TAction; states?: Record>; @@ -193,4 +194,26 @@ reverseMappedTypeInferFromFilteringNameType2.ts(145,3): error TS2353: Object lit ~~~~~ !!! error TS2353: Object literal may only specify known properties, and 'extra' does not exist in type '{ readonly invoke: { readonly src: "whatever"; }; }'. }); - \ No newline at end of file + + + type Bombolo = { + [ + K in keyof T as K extends 'enabled' + ? K + : 'enabled' extends keyof T + ? T["enabled"] extends true + ? K + : never + : never + ]: T[K] + } + + + declare function bombolo(a: Bombolo): T + + bombolo({ a: "a", b: "b", c: "c", enabled: false as const}) + ~ +!!! error TS2353: Object literal may only specify known properties, and 'a' does not exist in type 'Bombolo<{ enabled: false; }>'. + bombolo({ a: "a", b: "b", c: "c", enabled: true as const}) + + bombolo({ a: "a", b: "b", c: "c"}) // no epc? \ No newline at end of file diff --git a/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.symbols b/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.symbols index 944179ae40452..af1e616fe5d37 100644 --- a/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.symbols +++ b/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.symbols @@ -489,3 +489,64 @@ const config2 = createXMachine({ }); + +type Bombolo = { +>Bombolo : Symbol(Bombolo, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 145, 3)) +>T : Symbol(T, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 148, 13)) + + [ + K in keyof T as K extends 'enabled' +>K : Symbol(K, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 149, 5)) +>T : Symbol(T, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 148, 13)) +>K : Symbol(K, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 149, 5)) + + ? K +>K : Symbol(K, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 149, 5)) + + : 'enabled' extends keyof T +>T : Symbol(T, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 148, 13)) + + ? T["enabled"] extends true +>T : Symbol(T, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 148, 13)) + + ? K +>K : Symbol(K, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 149, 5)) + + : never + : never + ]: T[K] +>T : Symbol(T, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 148, 13)) +>K : Symbol(K, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 149, 5)) +} + + +declare function bombolo(a: Bombolo): T +>bombolo : Symbol(bombolo, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 158, 1)) +>T : Symbol(T, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 161, 25)) +>a : Symbol(a, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 161, 28)) +>Bombolo : Symbol(Bombolo, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 145, 3)) +>T : Symbol(T, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 161, 25)) +>T : Symbol(T, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 161, 25)) + +bombolo({ a: "a", b: "b", c: "c", enabled: false as const}) +>bombolo : Symbol(bombolo, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 158, 1)) +>a : Symbol(a, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 163, 9)) +>b : Symbol(b, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 163, 17)) +>c : Symbol(c, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 163, 25)) +>enabled : Symbol(enabled, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 163, 33)) +>const : Symbol(const) + +bombolo({ a: "a", b: "b", c: "c", enabled: true as const}) +>bombolo : Symbol(bombolo, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 158, 1)) +>a : Symbol(a, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 164, 9)) +>b : Symbol(b, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 164, 17)) +>c : Symbol(c, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 164, 25)) +>enabled : Symbol(enabled, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 164, 33)) +>const : Symbol(const) + +bombolo({ a: "a", b: "b", c: "c"}) // no epc? +>bombolo : Symbol(bombolo, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 158, 1)) +>a : Symbol(a, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 166, 9)) +>b : Symbol(b, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 166, 17)) +>c : Symbol(c, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 166, 25)) + diff --git a/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.types b/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.types index 6cd5ffdd64f9e..07bf71a5c240d 100644 --- a/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.types +++ b/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.types @@ -447,3 +447,64 @@ const config2 = createXMachine({ }); + +type Bombolo = { +>Bombolo : Bombolo + + [ + K in keyof T as K extends 'enabled' + ? K + : 'enabled' extends keyof T + ? T["enabled"] extends true +>true : true + + ? K + : never + : never + ]: T[K] +} + + +declare function bombolo(a: Bombolo): T +>bombolo : (a: Bombolo) => T +>a : Bombolo + +bombolo({ a: "a", b: "b", c: "c", enabled: false as const}) +>bombolo({ a: "a", b: "b", c: "c", enabled: false as const}) : { enabled: false; } +>bombolo : (a: Bombolo) => T +>{ a: "a", b: "b", c: "c", enabled: false as const} : { a: string; b: string; c: string; enabled: false; } +>a : string +>"a" : "a" +>b : string +>"b" : "b" +>c : string +>"c" : "c" +>enabled : false +>false as const : false +>false : false + +bombolo({ a: "a", b: "b", c: "c", enabled: true as const}) +>bombolo({ a: "a", b: "b", c: "c", enabled: true as const}) : { a: string; b: string; c: string; enabled: true; } +>bombolo : (a: Bombolo) => T +>{ a: "a", b: "b", c: "c", enabled: true as const} : { a: string; b: string; c: string; enabled: true; } +>a : string +>"a" : "a" +>b : string +>"b" : "b" +>c : string +>"c" : "c" +>enabled : true +>true as const : true +>true : true + +bombolo({ a: "a", b: "b", c: "c"}) // no epc? +>bombolo({ a: "a", b: "b", c: "c"}) : {} +>bombolo : (a: Bombolo) => T +>{ a: "a", b: "b", c: "c"} : { a: string; b: string; c: string; } +>a : string +>"a" : "a" +>b : string +>"b" : "b" +>c : string +>"c" : "c" + diff --git a/tests/cases/compiler/reverseMappedTypeInferFromFilteringNameType2.ts b/tests/cases/compiler/reverseMappedTypeInferFromFilteringNameType2.ts index 737050c533a26..5bba4e702da70 100644 --- a/tests/cases/compiler/reverseMappedTypeInferFromFilteringNameType2.ts +++ b/tests/cases/compiler/reverseMappedTypeInferFromFilteringNameType2.ts @@ -148,3 +148,24 @@ const config2 = createXMachine({ }, extra: 10, }); + + +type Bombolo = { + [ + K in keyof T as K extends 'enabled' + ? K + : 'enabled' extends keyof T + ? T["enabled"] extends true + ? K + : never + : never + ]: T[K] +} + + +declare function bombolo(a: Bombolo): T + +bombolo({ a: "a", b: "b", c: "c", enabled: false as const}) +bombolo({ a: "a", b: "b", c: "c", enabled: true as const}) + +bombolo({ a: "a", b: "b", c: "c"}) // no epc? \ No newline at end of file From 7040c6ff6d7681fa6f724efbc4f029a9b27003b2 Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Sun, 31 Dec 2023 22:52:56 +0100 Subject: [PATCH 2/8] that's why no epc --- ...verseMappedTypeInferFromFilteringNameType2.errors.txt | 3 ++- .../reverseMappedTypeInferFromFilteringNameType2.symbols | 9 +++++---- .../reverseMappedTypeInferFromFilteringNameType2.types | 3 ++- .../reverseMappedTypeInferFromFilteringNameType2.ts | 3 ++- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.errors.txt b/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.errors.txt index 2c99993f7ef7d..19602798dbda6 100644 --- a/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.errors.txt +++ b/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.errors.txt @@ -216,4 +216,5 @@ reverseMappedTypeInferFromFilteringNameType2.ts(164,11): error TS2353: Object li !!! error TS2353: Object literal may only specify known properties, and 'a' does not exist in type 'Bombolo<{ enabled: false; }>'. bombolo({ a: "a", b: "b", c: "c", enabled: true as const}) - bombolo({ a: "a", b: "b", c: "c"}) // no epc? \ No newline at end of file + // no excess property check because the parameter type turns out to be the empty object type {} + bombolo({ a: "a", b: "b", c: "c"}) \ No newline at end of file diff --git a/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.symbols b/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.symbols index af1e616fe5d37..81912a4384118 100644 --- a/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.symbols +++ b/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.symbols @@ -544,9 +544,10 @@ bombolo({ a: "a", b: "b", c: "c", enabled: true as const}) >enabled : Symbol(enabled, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 164, 33)) >const : Symbol(const) -bombolo({ a: "a", b: "b", c: "c"}) // no epc? +// no excess property check because the parameter type turns out to be the empty object type {} +bombolo({ a: "a", b: "b", c: "c"}) >bombolo : Symbol(bombolo, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 158, 1)) ->a : Symbol(a, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 166, 9)) ->b : Symbol(b, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 166, 17)) ->c : Symbol(c, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 166, 25)) +>a : Symbol(a, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 167, 9)) +>b : Symbol(b, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 167, 17)) +>c : Symbol(c, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 167, 25)) diff --git a/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.types b/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.types index 07bf71a5c240d..e1fa9079da343 100644 --- a/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.types +++ b/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.types @@ -497,7 +497,8 @@ bombolo({ a: "a", b: "b", c: "c", enabled: true as const}) >true as const : true >true : true -bombolo({ a: "a", b: "b", c: "c"}) // no epc? +// no excess property check because the parameter type turns out to be the empty object type {} +bombolo({ a: "a", b: "b", c: "c"}) >bombolo({ a: "a", b: "b", c: "c"}) : {} >bombolo : (a: Bombolo) => T >{ a: "a", b: "b", c: "c"} : { a: string; b: string; c: string; } diff --git a/tests/cases/compiler/reverseMappedTypeInferFromFilteringNameType2.ts b/tests/cases/compiler/reverseMappedTypeInferFromFilteringNameType2.ts index 5bba4e702da70..cd858204e7695 100644 --- a/tests/cases/compiler/reverseMappedTypeInferFromFilteringNameType2.ts +++ b/tests/cases/compiler/reverseMappedTypeInferFromFilteringNameType2.ts @@ -168,4 +168,5 @@ declare function bombolo(a: Bombolo): T bombolo({ a: "a", b: "b", c: "c", enabled: false as const}) bombolo({ a: "a", b: "b", c: "c", enabled: true as const}) -bombolo({ a: "a", b: "b", c: "c"}) // no epc? \ No newline at end of file +// no excess property check because the parameter type turns out to be the empty object type {} +bombolo({ a: "a", b: "b", c: "c"}) \ No newline at end of file From 0ab9e19afdf7376f5c89d648c4818b752cd1e1ba Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Mon, 1 Jan 2024 02:16:13 +0100 Subject: [PATCH 3/8] found bad example --- ...TypeInferFromFilteringNameType2.errors.txt | 20 ++++++++- ...pedTypeInferFromFilteringNameType2.symbols | 45 +++++++++++++++++++ ...appedTypeInferFromFilteringNameType2.types | 39 ++++++++++++++++ ...seMappedTypeInferFromFilteringNameType2.ts | 20 ++++++++- 4 files changed, 122 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.errors.txt b/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.errors.txt index 19602798dbda6..c91bb07be9cf0 100644 --- a/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.errors.txt +++ b/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.errors.txt @@ -217,4 +217,22 @@ reverseMappedTypeInferFromFilteringNameType2.ts(164,11): error TS2353: Object li bombolo({ a: "a", b: "b", c: "c", enabled: true as const}) // no excess property check because the parameter type turns out to be the empty object type {} - bombolo({ a: "a", b: "b", c: "c"}) \ No newline at end of file + bombolo({ a: "a", b: "b", c: "c"}) + + + // this is wrong: res should be { ok: "it's a string" } instead of {} + // it's a bad idea using the source + + type Test = { + [K in keyof T as T[K] extends string ? K : never ]: { a: T[K] } + } + + declare function tst(a: Test): T + + const res = tst({ + ok: { a: "it's a string" }, + not_ok: { a: 123 }, + }) + + res + // ^? \ No newline at end of file diff --git a/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.symbols b/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.symbols index 81912a4384118..190b21334fb51 100644 --- a/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.symbols +++ b/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.symbols @@ -551,3 +551,48 @@ bombolo({ a: "a", b: "b", c: "c"}) >b : Symbol(b, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 167, 17)) >c : Symbol(c, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 167, 25)) + +// this is wrong: res should be { ok: "it's a string" } instead of {} +// it's a bad idea using the source + +type Test = { +>Test : Symbol(Test, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 167, 34)) +>T : Symbol(T, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 173, 10)) + + [K in keyof T as T[K] extends string ? K : never ]: { a: T[K] } +>K : Symbol(K, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 174, 5)) +>T : Symbol(T, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 173, 10)) +>T : Symbol(T, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 173, 10)) +>K : Symbol(K, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 174, 5)) +>K : Symbol(K, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 174, 5)) +>a : Symbol(a, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 174, 57)) +>T : Symbol(T, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 173, 10)) +>K : Symbol(K, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 174, 5)) +} + +declare function tst(a: Test): T +>tst : Symbol(tst, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 175, 1)) +>T : Symbol(T, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 177, 21)) +>a : Symbol(a, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 177, 24)) +>Test : Symbol(Test, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 167, 34)) +>T : Symbol(T, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 177, 21)) +>T : Symbol(T, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 177, 21)) + +const res = tst({ +>res : Symbol(res, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 179, 5)) +>tst : Symbol(tst, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 175, 1)) + + ok: { a: "it's a string" }, +>ok : Symbol(ok, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 179, 17)) +>a : Symbol(a, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 180, 7)) + + not_ok: { a: 123 }, +>not_ok : Symbol(not_ok, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 180, 29)) +>a : Symbol(a, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 181, 11)) + +}) + + res +>res : Symbol(res, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 179, 5)) + +// ^? diff --git a/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.types b/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.types index e1fa9079da343..623906642a325 100644 --- a/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.types +++ b/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.types @@ -509,3 +509,42 @@ bombolo({ a: "a", b: "b", c: "c"}) >c : string >"c" : "c" + +// this is wrong: res should be { ok: "it's a string" } instead of {} +// it's a bad idea using the source + +type Test = { +>Test : Test + + [K in keyof T as T[K] extends string ? K : never ]: { a: T[K] } +>a : T[K] +} + +declare function tst(a: Test): T +>tst : (a: Test) => T +>a : Test + +const res = tst({ +>res : {} +>tst({ ok: { a: "it's a string" }, not_ok: { a: 123 },}) : {} +>tst : (a: Test) => T +>{ ok: { a: "it's a string" }, not_ok: { a: 123 },} : { ok: { a: string; }; not_ok: { a: number; }; } + + ok: { a: "it's a string" }, +>ok : { a: string; } +>{ a: "it's a string" } : { a: string; } +>a : string +>"it's a string" : "it's a string" + + not_ok: { a: 123 }, +>not_ok : { a: number; } +>{ a: 123 } : { a: number; } +>a : number +>123 : 123 + +}) + + res +>res : {} + +// ^? diff --git a/tests/cases/compiler/reverseMappedTypeInferFromFilteringNameType2.ts b/tests/cases/compiler/reverseMappedTypeInferFromFilteringNameType2.ts index cd858204e7695..c02b789a5ff95 100644 --- a/tests/cases/compiler/reverseMappedTypeInferFromFilteringNameType2.ts +++ b/tests/cases/compiler/reverseMappedTypeInferFromFilteringNameType2.ts @@ -169,4 +169,22 @@ bombolo({ a: "a", b: "b", c: "c", enabled: false as const}) bombolo({ a: "a", b: "b", c: "c", enabled: true as const}) // no excess property check because the parameter type turns out to be the empty object type {} -bombolo({ a: "a", b: "b", c: "c"}) \ No newline at end of file +bombolo({ a: "a", b: "b", c: "c"}) + + +// this is wrong: res should be { ok: "it's a string" } instead of {} +// it's a bad idea using the source + +type Test = { + [K in keyof T as T[K] extends string ? K : never ]: { a: T[K] } +} + +declare function tst(a: Test): T + +const res = tst({ + ok: { a: "it's a string" }, + not_ok: { a: 123 }, +}) + + res +// ^? \ No newline at end of file From 01a8f9863d3e6fccfa8b01d1400f647a9c6ddf74 Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Mon, 1 Jan 2024 02:38:32 +0100 Subject: [PATCH 4/8] proof of concept --- src/compiler/checker.ts | 48 ++++++++++++------- ...TypeInferFromFilteringNameType2.errors.txt | 5 +- ...appedTypeInferFromFilteringNameType2.types | 6 +-- 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2a55f3539c42a..00ac3f42772b4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13695,23 +13695,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const nameType = getNameTypeFromMappedType(type.mappedType); for (const prop of getPropertiesOfType(type.source)) { - // In case of a reverse mapped type with an intersection constraint or a name type - // we skip those properties that are not assignable to them - // because the extra properties wouldn't get through the application of the mapped type anyway - if (limitedConstraint || nameType) { - const propertyNameType = getLiteralTypeFromProperty(prop, TypeFlags.StringOrNumberLiteralOrUnique); - if (limitedConstraint && !isTypeAssignableTo(propertyNameType, limitedConstraint)) { - continue; - } - if (nameType) { - const nameMapper = appendTypeMapping(type.mappedType.mapper, getTypeParameterFromMappedType(type.mappedType), propertyNameType); - const typeParameterMapper = appendTypeMapping(nameMapper, type.constraintType.type, type.source); - const instantiatedNameType = instantiateType(nameType, typeParameterMapper); - if (instantiatedNameType.flags & TypeFlags.Never) { - continue; - } - } - } const checkFlags = CheckFlags.ReverseMapped | (readonlyMask && isReadonlySymbol(prop) ? CheckFlags.Readonly : 0); const inferredProp = createSymbol(SymbolFlags.Property | prop.flags & optionalMask, prop.escapedName, checkFlags) as ReverseMappedSymbol; inferredProp.declarations = prop.declarations; @@ -13736,6 +13719,37 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } members.set(prop.escapedName, inferredProp); } + + setStructuredTypeMembers(type, members, emptyArray, emptyArray, indexInfos); + + const propsToBeStrippedAway: __String[] = []; + + for (const prop of getPropertiesOfType(type.source)) { + // In case of a reverse mapped type with an intersection constraint or a name type + // we skip those properties that are not assignable to them + // because the extra properties wouldn't get through the application of the mapped type anyway + // + // We do this after because we need the full inferred T (this comment sucks, it's a TODO) + if (limitedConstraint || nameType) { + const propertyNameType = getLiteralTypeFromProperty(prop, TypeFlags.StringOrNumberLiteralOrUnique); + if (limitedConstraint && !isTypeAssignableTo(propertyNameType, limitedConstraint)) { + propsToBeStrippedAway.push(prop.escapedName); + } + if (nameType) { + const nameMapper = appendTypeMapping(type.mappedType.mapper, getTypeParameterFromMappedType(type.mappedType), propertyNameType); + const typeParameterMapper = appendTypeMapping(nameMapper, type.constraintType.type, type); + const instantiatedNameType = instantiateType(nameType, typeParameterMapper); + if (instantiatedNameType.flags & TypeFlags.Never) { + propsToBeStrippedAway.push(prop.escapedName); + } + } + } + } + + for (const propToStripAway of propsToBeStrippedAway) { + members.delete(propToStripAway); + } + setStructuredTypeMembers(type, members, emptyArray, emptyArray, indexInfos); } diff --git a/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.errors.txt b/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.errors.txt index c91bb07be9cf0..8c8c6bdc42a1e 100644 --- a/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.errors.txt +++ b/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.errors.txt @@ -15,9 +15,10 @@ reverseMappedTypeInferFromFilteringNameType2.ts(96,68): error TS2353: Object lit reverseMappedTypeInferFromFilteringNameType2.ts(139,3): error TS2353: Object literal may only specify known properties, and 'extra' does not exist in type '{ readonly types: { actors: { src: "str"; logic: () => Promise; }; }; readonly invoke: { readonly src: "str"; }; }'. reverseMappedTypeInferFromFilteringNameType2.ts(145,3): error TS2353: Object literal may only specify known properties, and 'extra' does not exist in type '{ readonly invoke: { readonly src: "whatever"; }; }'. reverseMappedTypeInferFromFilteringNameType2.ts(164,11): error TS2353: Object literal may only specify known properties, and 'a' does not exist in type 'Bombolo<{ enabled: false; }>'. +reverseMappedTypeInferFromFilteringNameType2.ts(182,3): error TS2353: Object literal may only specify known properties, and 'not_ok' does not exist in type 'Test<{ ok: string; }>'. -==== reverseMappedTypeInferFromFilteringNameType2.ts (14 errors) ==== +==== reverseMappedTypeInferFromFilteringNameType2.ts (15 errors) ==== type StateConfig = { entry?: TAction; states?: Record>; @@ -232,6 +233,8 @@ reverseMappedTypeInferFromFilteringNameType2.ts(164,11): error TS2353: Object li const res = tst({ ok: { a: "it's a string" }, not_ok: { a: 123 }, + ~~~~~~ +!!! error TS2353: Object literal may only specify known properties, and 'not_ok' does not exist in type 'Test<{ ok: string; }>'. }) res diff --git a/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.types b/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.types index 623906642a325..ae339604c9c6f 100644 --- a/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.types +++ b/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.types @@ -525,8 +525,8 @@ declare function tst(a: Test): T >a : Test const res = tst({ ->res : {} ->tst({ ok: { a: "it's a string" }, not_ok: { a: 123 },}) : {} +>res : { ok: string; } +>tst({ ok: { a: "it's a string" }, not_ok: { a: 123 },}) : { ok: string; } >tst : (a: Test) => T >{ ok: { a: "it's a string" }, not_ok: { a: 123 },} : { ok: { a: string; }; not_ok: { a: number; }; } @@ -545,6 +545,6 @@ const res = tst({ }) res ->res : {} +>res : { ok: string; } // ^? From a23fd7aff6518996f87db96d2975f5db8121acbf Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Mon, 1 Jan 2024 02:40:56 +0100 Subject: [PATCH 5/8] better names in tests --- ...TypeInferFromFilteringNameType2.errors.txt | 16 ++--- ...pedTypeInferFromFilteringNameType2.symbols | 63 +++++++++---------- ...appedTypeInferFromFilteringNameType2.types | 39 ++++++------ ...seMappedTypeInferFromFilteringNameType2.ts | 12 ++-- 4 files changed, 58 insertions(+), 72 deletions(-) diff --git a/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.errors.txt b/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.errors.txt index 8c8c6bdc42a1e..d065bde64b6d3 100644 --- a/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.errors.txt +++ b/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.errors.txt @@ -15,7 +15,7 @@ reverseMappedTypeInferFromFilteringNameType2.ts(96,68): error TS2353: Object lit reverseMappedTypeInferFromFilteringNameType2.ts(139,3): error TS2353: Object literal may only specify known properties, and 'extra' does not exist in type '{ readonly types: { actors: { src: "str"; logic: () => Promise; }; }; readonly invoke: { readonly src: "str"; }; }'. reverseMappedTypeInferFromFilteringNameType2.ts(145,3): error TS2353: Object literal may only specify known properties, and 'extra' does not exist in type '{ readonly invoke: { readonly src: "whatever"; }; }'. reverseMappedTypeInferFromFilteringNameType2.ts(164,11): error TS2353: Object literal may only specify known properties, and 'a' does not exist in type 'Bombolo<{ enabled: false; }>'. -reverseMappedTypeInferFromFilteringNameType2.ts(182,3): error TS2353: Object literal may only specify known properties, and 'not_ok' does not exist in type 'Test<{ ok: string; }>'. +reverseMappedTypeInferFromFilteringNameType2.ts(178,3): error TS2353: Object literal may only specify known properties, and 'not_ok' does not exist in type 'TestRecursiveReferenceToT<{ ok: "it's a string"; }>'. ==== reverseMappedTypeInferFromFilteringNameType2.ts (15 errors) ==== @@ -220,21 +220,17 @@ reverseMappedTypeInferFromFilteringNameType2.ts(182,3): error TS2353: Object lit // no excess property check because the parameter type turns out to be the empty object type {} bombolo({ a: "a", b: "b", c: "c"}) - - // this is wrong: res should be { ok: "it's a string" } instead of {} - // it's a bad idea using the source - - type Test = { + type TestRecursiveReferenceToT = { [K in keyof T as T[K] extends string ? K : never ]: { a: T[K] } } - declare function tst(a: Test): T + declare function tstrcv(a: TestRecursiveReferenceToT): T - const res = tst({ - ok: { a: "it's a string" }, + const res = tstrcv({ + ok: { a: "it's a string" } as const, not_ok: { a: 123 }, ~~~~~~ -!!! error TS2353: Object literal may only specify known properties, and 'not_ok' does not exist in type 'Test<{ ok: string; }>'. +!!! error TS2353: Object literal may only specify known properties, and 'not_ok' does not exist in type 'TestRecursiveReferenceToT<{ ok: "it's a string"; }>'. }) res diff --git a/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.symbols b/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.symbols index 190b21334fb51..bec6f3717d6f6 100644 --- a/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.symbols +++ b/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.symbols @@ -551,48 +551,45 @@ bombolo({ a: "a", b: "b", c: "c"}) >b : Symbol(b, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 167, 17)) >c : Symbol(c, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 167, 25)) - -// this is wrong: res should be { ok: "it's a string" } instead of {} -// it's a bad idea using the source - -type Test = { ->Test : Symbol(Test, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 167, 34)) ->T : Symbol(T, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 173, 10)) +type TestRecursiveReferenceToT = { +>TestRecursiveReferenceToT : Symbol(TestRecursiveReferenceToT, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 167, 34)) +>T : Symbol(T, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 169, 31)) [K in keyof T as T[K] extends string ? K : never ]: { a: T[K] } ->K : Symbol(K, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 174, 5)) ->T : Symbol(T, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 173, 10)) ->T : Symbol(T, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 173, 10)) ->K : Symbol(K, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 174, 5)) ->K : Symbol(K, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 174, 5)) ->a : Symbol(a, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 174, 57)) ->T : Symbol(T, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 173, 10)) ->K : Symbol(K, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 174, 5)) +>K : Symbol(K, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 170, 5)) +>T : Symbol(T, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 169, 31)) +>T : Symbol(T, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 169, 31)) +>K : Symbol(K, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 170, 5)) +>K : Symbol(K, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 170, 5)) +>a : Symbol(a, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 170, 57)) +>T : Symbol(T, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 169, 31)) +>K : Symbol(K, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 170, 5)) } -declare function tst(a: Test): T ->tst : Symbol(tst, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 175, 1)) ->T : Symbol(T, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 177, 21)) ->a : Symbol(a, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 177, 24)) ->Test : Symbol(Test, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 167, 34)) ->T : Symbol(T, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 177, 21)) ->T : Symbol(T, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 177, 21)) - -const res = tst({ ->res : Symbol(res, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 179, 5)) ->tst : Symbol(tst, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 175, 1)) - - ok: { a: "it's a string" }, ->ok : Symbol(ok, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 179, 17)) ->a : Symbol(a, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 180, 7)) +declare function tstrcv(a: TestRecursiveReferenceToT): T +>tstrcv : Symbol(tstrcv, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 171, 1)) +>T : Symbol(T, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 173, 24)) +>a : Symbol(a, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 173, 27)) +>TestRecursiveReferenceToT : Symbol(TestRecursiveReferenceToT, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 167, 34)) +>T : Symbol(T, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 173, 24)) +>T : Symbol(T, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 173, 24)) + +const res = tstrcv({ +>res : Symbol(res, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 175, 5)) +>tstrcv : Symbol(tstrcv, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 171, 1)) + + ok: { a: "it's a string" } as const, +>ok : Symbol(ok, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 175, 20)) +>a : Symbol(a, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 176, 7)) +>const : Symbol(const) not_ok: { a: 123 }, ->not_ok : Symbol(not_ok, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 180, 29)) ->a : Symbol(a, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 181, 11)) +>not_ok : Symbol(not_ok, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 176, 38)) +>a : Symbol(a, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 177, 11)) }) res ->res : Symbol(res, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 179, 5)) +>res : Symbol(res, Decl(reverseMappedTypeInferFromFilteringNameType2.ts, 175, 5)) // ^? diff --git a/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.types b/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.types index ae339604c9c6f..341d331d0633f 100644 --- a/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.types +++ b/tests/baselines/reference/reverseMappedTypeInferFromFilteringNameType2.types @@ -509,31 +509,28 @@ bombolo({ a: "a", b: "b", c: "c"}) >c : string >"c" : "c" - -// this is wrong: res should be { ok: "it's a string" } instead of {} -// it's a bad idea using the source - -type Test = { ->Test : Test +type TestRecursiveReferenceToT = { +>TestRecursiveReferenceToT : TestRecursiveReferenceToT [K in keyof T as T[K] extends string ? K : never ]: { a: T[K] } >a : T[K] } -declare function tst(a: Test): T ->tst : (a: Test) => T ->a : Test - -const res = tst({ ->res : { ok: string; } ->tst({ ok: { a: "it's a string" }, not_ok: { a: 123 },}) : { ok: string; } ->tst : (a: Test) => T ->{ ok: { a: "it's a string" }, not_ok: { a: 123 },} : { ok: { a: string; }; not_ok: { a: number; }; } - - ok: { a: "it's a string" }, ->ok : { a: string; } ->{ a: "it's a string" } : { a: string; } ->a : string +declare function tstrcv(a: TestRecursiveReferenceToT): T +>tstrcv : (a: TestRecursiveReferenceToT) => T +>a : TestRecursiveReferenceToT + +const res = tstrcv({ +>res : { ok: "it's a string"; } +>tstrcv({ ok: { a: "it's a string" } as const, not_ok: { a: 123 },}) : { ok: "it's a string"; } +>tstrcv : (a: TestRecursiveReferenceToT) => T +>{ ok: { a: "it's a string" } as const, not_ok: { a: 123 },} : { ok: { readonly a: "it's a string"; }; not_ok: { a: number; }; } + + ok: { a: "it's a string" } as const, +>ok : { readonly a: "it's a string"; } +>{ a: "it's a string" } as const : { readonly a: "it's a string"; } +>{ a: "it's a string" } : { readonly a: "it's a string"; } +>a : "it's a string" >"it's a string" : "it's a string" not_ok: { a: 123 }, @@ -545,6 +542,6 @@ const res = tst({ }) res ->res : { ok: string; } +>res : { ok: "it's a string"; } // ^? diff --git a/tests/cases/compiler/reverseMappedTypeInferFromFilteringNameType2.ts b/tests/cases/compiler/reverseMappedTypeInferFromFilteringNameType2.ts index c02b789a5ff95..aa5a9d15596e8 100644 --- a/tests/cases/compiler/reverseMappedTypeInferFromFilteringNameType2.ts +++ b/tests/cases/compiler/reverseMappedTypeInferFromFilteringNameType2.ts @@ -171,18 +171,14 @@ bombolo({ a: "a", b: "b", c: "c", enabled: true as const}) // no excess property check because the parameter type turns out to be the empty object type {} bombolo({ a: "a", b: "b", c: "c"}) - -// this is wrong: res should be { ok: "it's a string" } instead of {} -// it's a bad idea using the source - -type Test = { +type TestRecursiveReferenceToT = { [K in keyof T as T[K] extends string ? K : never ]: { a: T[K] } } -declare function tst(a: Test): T +declare function tstrcv(a: TestRecursiveReferenceToT): T -const res = tst({ - ok: { a: "it's a string" }, +const res = tstrcv({ + ok: { a: "it's a string" } as const, not_ok: { a: 123 }, }) From f4b72e536f5fedfd535eb4a8ed7bb43431c7bbd6 Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Mon, 1 Jan 2024 11:06:42 +0100 Subject: [PATCH 6/8] little bit better --- src/compiler/checker.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 00ac3f42772b4..4965a6266901c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13693,8 +13693,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const members = createSymbolTable(); const limitedConstraint = getLimitedConstraint(type); const nameType = getNameTypeFromMappedType(type.mappedType); + const sourceProperties = getPropertiesOfType(type.source); - for (const prop of getPropertiesOfType(type.source)) { + for (const prop of sourceProperties) { const checkFlags = CheckFlags.ReverseMapped | (readonlyMask && isReadonlySymbol(prop) ? CheckFlags.Readonly : 0); const inferredProp = createSymbol(SymbolFlags.Property | prop.flags & optionalMask, prop.escapedName, checkFlags) as ReverseMappedSymbol; inferredProp.declarations = prop.declarations; @@ -13722,9 +13723,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { setStructuredTypeMembers(type, members, emptyArray, emptyArray, indexInfos); - const propsToBeStrippedAway: __String[] = []; + const propsToBeStrippedAway: Set<__String> = new Set(); - for (const prop of getPropertiesOfType(type.source)) { + for (const prop of sourceProperties) { // In case of a reverse mapped type with an intersection constraint or a name type // we skip those properties that are not assignable to them // because the extra properties wouldn't get through the application of the mapped type anyway @@ -13733,14 +13734,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (limitedConstraint || nameType) { const propertyNameType = getLiteralTypeFromProperty(prop, TypeFlags.StringOrNumberLiteralOrUnique); if (limitedConstraint && !isTypeAssignableTo(propertyNameType, limitedConstraint)) { - propsToBeStrippedAway.push(prop.escapedName); + propsToBeStrippedAway.add(prop.escapedName); } if (nameType) { const nameMapper = appendTypeMapping(type.mappedType.mapper, getTypeParameterFromMappedType(type.mappedType), propertyNameType); const typeParameterMapper = appendTypeMapping(nameMapper, type.constraintType.type, type); const instantiatedNameType = instantiateType(nameType, typeParameterMapper); if (instantiatedNameType.flags & TypeFlags.Never) { - propsToBeStrippedAway.push(prop.escapedName); + propsToBeStrippedAway.add(prop.escapedName); } } } From 576a29aee67401665f9175af5b21fd591f2651f1 Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Mon, 1 Jan 2024 11:12:02 +0100 Subject: [PATCH 7/8] better comment --- src/compiler/checker.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4965a6266901c..99486f086d7df 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13730,7 +13730,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // we skip those properties that are not assignable to them // because the extra properties wouldn't get through the application of the mapped type anyway // - // We do this after because we need the full inferred T (this comment sucks, it's a TODO) + // We do this after having set all the properties because we may need the full reverse-inferred type + // while checking its properties, sort of a circular dependency + // e.g. we need T[K] in { [K in keyof T as T[K] extends string ? K : never ]: { value: T[K] } } if (limitedConstraint || nameType) { const propertyNameType = getLiteralTypeFromProperty(prop, TypeFlags.StringOrNumberLiteralOrUnique); if (limitedConstraint && !isTypeAssignableTo(propertyNameType, limitedConstraint)) { From 49eb6dd69804b69b3e3d1a1b54322ce298900b23 Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Mon, 1 Jan 2024 11:19:09 +0100 Subject: [PATCH 8/8] better comment --- 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 99486f086d7df..71ce6bdbea5c0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13731,7 +13731,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // because the extra properties wouldn't get through the application of the mapped type anyway // // We do this after having set all the properties because we may need the full reverse-inferred type - // while checking its properties, sort of a circular dependency + // while stripping away some of its properties, it's sort of a circular dependency // e.g. we need T[K] in { [K in keyof T as T[K] extends string ? K : never ]: { value: T[K] } } if (limitedConstraint || nameType) { const propertyNameType = getLiteralTypeFromProperty(prop, TypeFlags.StringOrNumberLiteralOrUnique);