Could someone please explain to me TypeScript behavior here?
JavaScript
const test1 = [ ['a', 'b'], ['c', 'd'], ]; const test2 = [ ...[ ['a', 'b'], ['c', 'd'], ], ]; console.log(JSON.stringify(test1) === JSON.stringify(test2));
Logs
true
as expected (arrays are identical).TypeScript
const test1: [string, string][] = [ ['a', 'b'], ['c', 'd'], ]; const test2: [string, string][] = [ ...[ ['a', 'b'], ['c', 'd'], ], ];
This, however, leads to the following error on
test2
:Type 'string[][]' is not assignable to type '[string, string][]'. Type 'string[]' is not assignable to type '[string, string]'. Target requires 2 element(s) but source may have fewer.
Here is a reproducer on TypeScript Playground.
Am I doing something wrong here, or is there any way to get this to work without resorting to some ugly type casting?
UPDATE
Here is a more concrete example of what I'm trying to achieve:
const someValues = [1, 2, 3];
const test3: [string, string][] = [
...someValues.map(value => ['some string', 'another string']),
];
UPDATE #2
Following Алексей Мартинкевич advice, I can confirm that this works:
const someValues = [1, 2, 3];
const test3: (readonly [string, string])[] = [
...someValues.map(i => ['a', 'a'] as const),
];
Would there be a more readable/less complex solution though?
(thinking of my coworkers and 2-weeks-future-me wondering what I did there :D)
CodePudding user response:
This construction has type string[][]
, so it cannot be assigned to [string, string][]
.
...[
['a', 'b'],
['c', 'd'],
]
The following should work:
const test1: [string, string][] = [
['a', 'b'],
['c', 'd'],
];
const test2: [string, string][] = [
...test1,
];
You can use as const
, but you need to keep everything readonly:
const test2: (readonly [string, string])[] = [
...[
['a', 'b'],
['c', 'd'],
] as const
]
Typescript assumes that array can be changed, so it uses more wide type by default.
CodePudding user response:
For your specific concrete case, you can indicate the return type on the arrow function given to map
:
const someValues = [1, 2, 3];
const test3: [string, string][] = [
...someValues.map((value): [string, string] => ['some string', 'another string']),
];
You could also remove the type on the variable if you think it's explicit enough:
const test4 = [
...someValues.map((value): [string, string] => ['some string', 'another string']),
];