From 0b381cb8c91489378fc971742fdcbb3f8913f738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 7 Apr 2022 18:16:18 +0200 Subject: [PATCH 1/2] Add tests showcasing the current behavior of prop type when mapping over a tuple type --- .../mappedTypeTupleTypePropType.errors.txt | 46 ++++++++++ .../reference/mappedTypeTupleTypePropType.js | 40 ++++++++ .../mappedTypeTupleTypePropType.symbols | 91 +++++++++++++++++++ .../mappedTypeTupleTypePropType.types | 70 ++++++++++++++ .../compiler/mappedTypeTupleTypePropType.ts | 32 +++++++ 5 files changed, 279 insertions(+) create mode 100644 tests/baselines/reference/mappedTypeTupleTypePropType.errors.txt create mode 100644 tests/baselines/reference/mappedTypeTupleTypePropType.js create mode 100644 tests/baselines/reference/mappedTypeTupleTypePropType.symbols create mode 100644 tests/baselines/reference/mappedTypeTupleTypePropType.types create mode 100644 tests/cases/compiler/mappedTypeTupleTypePropType.ts diff --git a/tests/baselines/reference/mappedTypeTupleTypePropType.errors.txt b/tests/baselines/reference/mappedTypeTupleTypePropType.errors.txt new file mode 100644 index 0000000000000..21d4d64ed3dea --- /dev/null +++ b/tests/baselines/reference/mappedTypeTupleTypePropType.errors.txt @@ -0,0 +1,46 @@ +tests/cases/compiler/mappedTypeTupleTypePropType.ts(29,9): error TS2322: Type 'string | number' is not assignable to type 'string'. + Type 'number' is not assignable to type 'string'. +tests/cases/compiler/mappedTypeTupleTypePropType.ts(30,9): error TS2322: Type 'string | number' is not assignable to type 'number'. + Type 'string' is not assignable to type 'number'. + + +==== tests/cases/compiler/mappedTypeTupleTypePropType.ts (2 errors) ==== + type Indices> = { + [K in keyof T]: K + }; + + // should contain numbers + type MyIndices = Indices<[string, number]>; + // union of indices should be preserved + type StillMyIndices = MyIndices[number] & number + + // simplified repro from https://twitter.com/oleg008/status/1508422774401949704 + + type Container = { + value: V; + }; + + type UnwrapContainers[]> = { + [K in keyof T]: T[K & number]["value"]; + }; + + declare function combine[]>( + containers: [...T], + callback: (...values: UnwrapContainers) => void + ): void; + + declare const container1: Container; + declare const container2: Container; + + combine([container1, container2], (value1, value2) => { + const val1: string = value1; + ~~~~ +!!! error TS2322: Type 'string | number' is not assignable to type 'string'. +!!! error TS2322: Type 'number' is not assignable to type 'string'. + const val2: number = value2; + ~~~~ +!!! error TS2322: Type 'string | number' is not assignable to type 'number'. +!!! error TS2322: Type 'string' is not assignable to type 'number'. + }); + + \ No newline at end of file diff --git a/tests/baselines/reference/mappedTypeTupleTypePropType.js b/tests/baselines/reference/mappedTypeTupleTypePropType.js new file mode 100644 index 0000000000000..d0cb6685c84c8 --- /dev/null +++ b/tests/baselines/reference/mappedTypeTupleTypePropType.js @@ -0,0 +1,40 @@ +//// [mappedTypeTupleTypePropType.ts] +type Indices> = { + [K in keyof T]: K +}; + +// should contain numbers +type MyIndices = Indices<[string, number]>; +// union of indices should be preserved +type StillMyIndices = MyIndices[number] & number + +// simplified repro from https://twitter.com/oleg008/status/1508422774401949704 + +type Container = { + value: V; +}; + +type UnwrapContainers[]> = { + [K in keyof T]: T[K & number]["value"]; +}; + +declare function combine[]>( + containers: [...T], + callback: (...values: UnwrapContainers) => void +): void; + +declare const container1: Container; +declare const container2: Container; + +combine([container1, container2], (value1, value2) => { + const val1: string = value1; + const val2: number = value2; +}); + + + +//// [mappedTypeTupleTypePropType.js] +combine([container1, container2], function (value1, value2) { + var val1 = value1; + var val2 = value2; +}); diff --git a/tests/baselines/reference/mappedTypeTupleTypePropType.symbols b/tests/baselines/reference/mappedTypeTupleTypePropType.symbols new file mode 100644 index 0000000000000..3971a27f71450 --- /dev/null +++ b/tests/baselines/reference/mappedTypeTupleTypePropType.symbols @@ -0,0 +1,91 @@ +=== tests/cases/compiler/mappedTypeTupleTypePropType.ts === +type Indices> = { +>Indices : Symbol(Indices, Decl(mappedTypeTupleTypePropType.ts, 0, 0)) +>T : Symbol(T, Decl(mappedTypeTupleTypePropType.ts, 0, 13)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + [K in keyof T]: K +>K : Symbol(K, Decl(mappedTypeTupleTypePropType.ts, 1, 3)) +>T : Symbol(T, Decl(mappedTypeTupleTypePropType.ts, 0, 13)) +>K : Symbol(K, Decl(mappedTypeTupleTypePropType.ts, 1, 3)) + +}; + +// should contain numbers +type MyIndices = Indices<[string, number]>; +>MyIndices : Symbol(MyIndices, Decl(mappedTypeTupleTypePropType.ts, 2, 2)) +>Indices : Symbol(Indices, Decl(mappedTypeTupleTypePropType.ts, 0, 0)) + +// union of indices should be preserved +type StillMyIndices = MyIndices[number] & number +>StillMyIndices : Symbol(StillMyIndices, Decl(mappedTypeTupleTypePropType.ts, 5, 43)) +>MyIndices : Symbol(MyIndices, Decl(mappedTypeTupleTypePropType.ts, 2, 2)) + +// simplified repro from https://twitter.com/oleg008/status/1508422774401949704 + +type Container = { +>Container : Symbol(Container, Decl(mappedTypeTupleTypePropType.ts, 7, 48)) +>V : Symbol(V, Decl(mappedTypeTupleTypePropType.ts, 11, 15)) + + value: V; +>value : Symbol(value, Decl(mappedTypeTupleTypePropType.ts, 11, 21)) +>V : Symbol(V, Decl(mappedTypeTupleTypePropType.ts, 11, 15)) + +}; + +type UnwrapContainers[]> = { +>UnwrapContainers : Symbol(UnwrapContainers, Decl(mappedTypeTupleTypePropType.ts, 13, 2)) +>T : Symbol(T, Decl(mappedTypeTupleTypePropType.ts, 15, 22)) +>Container : Symbol(Container, Decl(mappedTypeTupleTypePropType.ts, 7, 48)) + + [K in keyof T]: T[K & number]["value"]; +>K : Symbol(K, Decl(mappedTypeTupleTypePropType.ts, 16, 3)) +>T : Symbol(T, Decl(mappedTypeTupleTypePropType.ts, 15, 22)) +>T : Symbol(T, Decl(mappedTypeTupleTypePropType.ts, 15, 22)) +>K : Symbol(K, Decl(mappedTypeTupleTypePropType.ts, 16, 3)) + +}; + +declare function combine[]>( +>combine : Symbol(combine, Decl(mappedTypeTupleTypePropType.ts, 17, 2)) +>T : Symbol(T, Decl(mappedTypeTupleTypePropType.ts, 19, 25)) +>Container : Symbol(Container, Decl(mappedTypeTupleTypePropType.ts, 7, 48)) + + containers: [...T], +>containers : Symbol(containers, Decl(mappedTypeTupleTypePropType.ts, 19, 57)) +>T : Symbol(T, Decl(mappedTypeTupleTypePropType.ts, 19, 25)) + + callback: (...values: UnwrapContainers) => void +>callback : Symbol(callback, Decl(mappedTypeTupleTypePropType.ts, 20, 21)) +>values : Symbol(values, Decl(mappedTypeTupleTypePropType.ts, 21, 13)) +>UnwrapContainers : Symbol(UnwrapContainers, Decl(mappedTypeTupleTypePropType.ts, 13, 2)) +>T : Symbol(T, Decl(mappedTypeTupleTypePropType.ts, 19, 25)) + +): void; + +declare const container1: Container; +>container1 : Symbol(container1, Decl(mappedTypeTupleTypePropType.ts, 24, 13)) +>Container : Symbol(Container, Decl(mappedTypeTupleTypePropType.ts, 7, 48)) + +declare const container2: Container; +>container2 : Symbol(container2, Decl(mappedTypeTupleTypePropType.ts, 25, 13)) +>Container : Symbol(Container, Decl(mappedTypeTupleTypePropType.ts, 7, 48)) + +combine([container1, container2], (value1, value2) => { +>combine : Symbol(combine, Decl(mappedTypeTupleTypePropType.ts, 17, 2)) +>container1 : Symbol(container1, Decl(mappedTypeTupleTypePropType.ts, 24, 13)) +>container2 : Symbol(container2, Decl(mappedTypeTupleTypePropType.ts, 25, 13)) +>value1 : Symbol(value1, Decl(mappedTypeTupleTypePropType.ts, 27, 35)) +>value2 : Symbol(value2, Decl(mappedTypeTupleTypePropType.ts, 27, 42)) + + const val1: string = value1; +>val1 : Symbol(val1, Decl(mappedTypeTupleTypePropType.ts, 28, 7)) +>value1 : Symbol(value1, Decl(mappedTypeTupleTypePropType.ts, 27, 35)) + + const val2: number = value2; +>val2 : Symbol(val2, Decl(mappedTypeTupleTypePropType.ts, 29, 7)) +>value2 : Symbol(value2, Decl(mappedTypeTupleTypePropType.ts, 27, 42)) + +}); + + diff --git a/tests/baselines/reference/mappedTypeTupleTypePropType.types b/tests/baselines/reference/mappedTypeTupleTypePropType.types new file mode 100644 index 0000000000000..8e2f183f120dc --- /dev/null +++ b/tests/baselines/reference/mappedTypeTupleTypePropType.types @@ -0,0 +1,70 @@ +=== tests/cases/compiler/mappedTypeTupleTypePropType.ts === +type Indices> = { +>Indices : Indices + + [K in keyof T]: K +}; + +// should contain numbers +type MyIndices = Indices<[string, number]>; +>MyIndices : ["0", "1"] + +// union of indices should be preserved +type StillMyIndices = MyIndices[number] & number +>StillMyIndices : never + +// simplified repro from https://twitter.com/oleg008/status/1508422774401949704 + +type Container = { +>Container : Container + + value: V; +>value : V + +}; + +type UnwrapContainers[]> = { +>UnwrapContainers : UnwrapContainers + + [K in keyof T]: T[K & number]["value"]; +}; + +declare function combine[]>( +>combine : []>(containers: [...T], callback: (...values: UnwrapContainers) => void) => void + + containers: [...T], +>containers : [...T] + + callback: (...values: UnwrapContainers) => void +>callback : (...values: UnwrapContainers) => void +>values : UnwrapContainers + +): void; + +declare const container1: Container; +>container1 : Container + +declare const container2: Container; +>container2 : Container + +combine([container1, container2], (value1, value2) => { +>combine([container1, container2], (value1, value2) => { const val1: string = value1; const val2: number = value2;}) : void +>combine : []>(containers: [...T], callback: (...values: UnwrapContainers) => void) => void +>[container1, container2] : [Container, Container] +>container1 : Container +>container2 : Container +>(value1, value2) => { const val1: string = value1; const val2: number = value2;} : (value1: string | number, value2: string | number) => void +>value1 : string | number +>value2 : string | number + + const val1: string = value1; +>val1 : string +>value1 : string | number + + const val2: number = value2; +>val2 : number +>value2 : string | number + +}); + + diff --git a/tests/cases/compiler/mappedTypeTupleTypePropType.ts b/tests/cases/compiler/mappedTypeTupleTypePropType.ts new file mode 100644 index 0000000000000..9dea1d773fcba --- /dev/null +++ b/tests/cases/compiler/mappedTypeTupleTypePropType.ts @@ -0,0 +1,32 @@ +type Indices> = { + [K in keyof T]: K +}; + +// should contain numbers +type MyIndices = Indices<[string, number]>; +// union of indices should be preserved +type StillMyIndices = MyIndices[number] & number + +// simplified repro from https://twitter.com/oleg008/status/1508422774401949704 + +type Container = { + value: V; +}; + +type UnwrapContainers[]> = { + [K in keyof T]: T[K & number]["value"]; +}; + +declare function combine[]>( + containers: [...T], + callback: (...values: UnwrapContainers) => void +): void; + +declare const container1: Container; +declare const container2: Container; + +combine([container1, container2], (value1, value2) => { + const val1: string = value1; + const val2: number = value2; +}); + From b25dacf528db825de13fc3ca6041a24360c2a7e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 7 Apr 2022 18:17:48 +0200 Subject: [PATCH 2/2] Improve prop type when mapping over a tuple type --- src/compiler/checker.ts | 2 +- .../mappedTypeTupleTypePropType.errors.txt | 46 ------------------- .../mappedTypeTupleTypePropType.types | 14 +++--- 3 files changed, 8 insertions(+), 54 deletions(-) delete mode 100644 tests/baselines/reference/mappedTypeTupleTypePropType.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0f2b62bf4937c..474f062c9da6e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17034,7 +17034,7 @@ namespace ts { function instantiateMappedTupleType(tupleType: TupleTypeReference, mappedType: MappedType, mapper: TypeMapper) { const elementFlags = tupleType.target.elementFlags; const elementTypes = map(getTypeArguments(tupleType), (_, i) => - instantiateMappedTypeTemplate(mappedType, getStringLiteralType("" + i), !!(elementFlags[i] & ElementFlags.Optional), mapper)); + instantiateMappedTypeTemplate(mappedType, getNumberLiteralType(i), !!(elementFlags[i] & ElementFlags.Optional), mapper)); const modifiers = getMappedTypeModifiers(mappedType); const newTupleModifiers = modifiers & MappedTypeModifiers.IncludeOptional ? map(elementFlags, f => f & ElementFlags.Required ? ElementFlags.Optional : f) : modifiers & MappedTypeModifiers.ExcludeOptional ? map(elementFlags, f => f & ElementFlags.Optional ? ElementFlags.Required : f) : diff --git a/tests/baselines/reference/mappedTypeTupleTypePropType.errors.txt b/tests/baselines/reference/mappedTypeTupleTypePropType.errors.txt deleted file mode 100644 index 21d4d64ed3dea..0000000000000 --- a/tests/baselines/reference/mappedTypeTupleTypePropType.errors.txt +++ /dev/null @@ -1,46 +0,0 @@ -tests/cases/compiler/mappedTypeTupleTypePropType.ts(29,9): error TS2322: Type 'string | number' is not assignable to type 'string'. - Type 'number' is not assignable to type 'string'. -tests/cases/compiler/mappedTypeTupleTypePropType.ts(30,9): error TS2322: Type 'string | number' is not assignable to type 'number'. - Type 'string' is not assignable to type 'number'. - - -==== tests/cases/compiler/mappedTypeTupleTypePropType.ts (2 errors) ==== - type Indices> = { - [K in keyof T]: K - }; - - // should contain numbers - type MyIndices = Indices<[string, number]>; - // union of indices should be preserved - type StillMyIndices = MyIndices[number] & number - - // simplified repro from https://twitter.com/oleg008/status/1508422774401949704 - - type Container = { - value: V; - }; - - type UnwrapContainers[]> = { - [K in keyof T]: T[K & number]["value"]; - }; - - declare function combine[]>( - containers: [...T], - callback: (...values: UnwrapContainers) => void - ): void; - - declare const container1: Container; - declare const container2: Container; - - combine([container1, container2], (value1, value2) => { - const val1: string = value1; - ~~~~ -!!! error TS2322: Type 'string | number' is not assignable to type 'string'. -!!! error TS2322: Type 'number' is not assignable to type 'string'. - const val2: number = value2; - ~~~~ -!!! error TS2322: Type 'string | number' is not assignable to type 'number'. -!!! error TS2322: Type 'string' is not assignable to type 'number'. - }); - - \ No newline at end of file diff --git a/tests/baselines/reference/mappedTypeTupleTypePropType.types b/tests/baselines/reference/mappedTypeTupleTypePropType.types index 8e2f183f120dc..ebd0a26a6833b 100644 --- a/tests/baselines/reference/mappedTypeTupleTypePropType.types +++ b/tests/baselines/reference/mappedTypeTupleTypePropType.types @@ -7,11 +7,11 @@ type Indices> = { // should contain numbers type MyIndices = Indices<[string, number]>; ->MyIndices : ["0", "1"] +>MyIndices : [0, 1] // union of indices should be preserved type StillMyIndices = MyIndices[number] & number ->StillMyIndices : never +>StillMyIndices : StillMyIndices // simplified repro from https://twitter.com/oleg008/status/1508422774401949704 @@ -53,17 +53,17 @@ combine([container1, container2], (value1, value2) => { >[container1, container2] : [Container, Container] >container1 : Container >container2 : Container ->(value1, value2) => { const val1: string = value1; const val2: number = value2;} : (value1: string | number, value2: string | number) => void ->value1 : string | number ->value2 : string | number +>(value1, value2) => { const val1: string = value1; const val2: number = value2;} : (value1: string, value2: number) => void +>value1 : string +>value2 : number const val1: string = value1; >val1 : string ->value1 : string | number +>value1 : string const val2: number = value2; >val2 : number ->value2 : string | number +>value2 : number });