TypeScript Version: 3.5.1
Search Terms: infer array destruction
Code
// A *self-contained* demonstration of the problem follows...
// Test this by running `tsc` on the command-line, rather than through another build tool such as Gulp, Webpack, etc.
function formatValue(value: string): [string, boolean] {
const hasExpandString = /\%[^\%]*\%/i;
return [value.replace(/^[\'\"].*[\'\"]$/, ''), hasExpandString.test(value)];
}
function formatOptions(options: string[]): [string, string, string, boolean] {
let key, value, operator;
switch (options.length) {
case 2:
[key, value] = options;
return [key, '=', ...formatValue(value)];
// ^
// Type 'string | boolean' is not assignable to type 'string'.
// Type 'false' is not assignable to type 'string'.
default:
throw new Error('argument error');
}
}
Expected behavior: after destruction of array ts can infer [key, '=', ...formatValue(value)] to be [string, string, string, boolean] with formatValue(string): [string, boolean]
Actual behavior: got error
Type 'string | boolean' is not assignable to type 'string'.
Type 'false' is not assignable to type 'string'.
Playground Link: https://www.typescriptlang.org/play/index.html#code/GYVwdgxgLglg9mABMOAnAtgQygNUwGxAFMAKANwOIC5EBnKVGMAcwEoaBtexlgGkQBGcOPiKYwAXUQBvAFCJEEBPUQALTLQCiADwAO4gCYBlBk2aIAvIgD0AHQCkHAHoOJAKgfWYAbnmJURFAgqEgcFIREAHQBuviYEKTWThy2AOS2AEQSkW4p6VkAJNb8qams-OpaeoYmPMyRUET05JRErBK+AL6ysqCQsAjIaFhQAPK6A2C0JHAT8FM03GYcEuyIXKZ8dJvM-EtbQiJikjJ+olCIANZEAJ784cT8s0So2Gi+CrQA7jBQEKqIGZzZSRUQsKCqVinBQKCAaIiIABMVD8MPW1zuiAeRCkVlmk1oHzR-kCwVCGJKFlS-EitJQGGweAiLQi7SJsPhiAAzCjiejbk9dC83qh7q1cYh8fNCaiYTBgIDnq8oGhLBYrKkANSpRAAH11kqFytV6o1VNYsrRASCIX5mKVIppdOGjNaLOIbMtiCI+FoCIhqDgX0QYCIwc0qEDqBIqQdKtQgCvlQC-AYBrDUAmYqWfja1jsxAGIjATAgfBQXnEgNBkNhxARqMxwBDyoAHU3TgExUwD30WUid1OkA
Related Issues: https://github.com/microsoft/TypeScript/issues/5296
To provide some clarification here, the term is "destructuring" (not "destruction"). And, to be even more precise, it's actually a spread operation. But I agree this is an issue. (@deepkolos it might help to update your title and description.)
Simpler repro:
declare const sb: [string, boolean];
// Shouldn't error
const k: [number, string, boolean] = [1, ...sb];
I ran into this problem as well, it would be nice to have it fixed :)
Same issue with the code:
const testCases = [
[['1', '2'], 3],
];
testCases.forEach(([input, expected] :[string[], number]) =>
test('works', () => {
expect(computeSum(input)).toEqual(expected);
}),
);
Gives error:
TS2345: Argument of type '([input, expected]: [string[], number]) => void' is not assignable to parameter of type '(value: (number | string[])[], index: number, array: (number | string[])[][]) => void'.
聽聽Types of parameters '__0' and 'value' are incompatible.
聽聽聽聽Type '(number | string[])[]' is missing the following properties from type '[string[], number]': 0, 1
The workaround was to use objects instead. Object destructuring works good 馃憤
const testCases = [
{input: ['1', '2'], expected: 3},
];
testCases.forEach(({input, expected} : { input: string[], expected: number }) =>
test('works', () => {
expect(computeSum(input)).toEqual(expected);
}),
);
but sometimes the code is not that flexible to change :)
+1 to this - it's especially annoying in my common use case of passing down the return value from useState in React.
I think this is still an issue. With the following code:
const f = () => [[1], 'word'];
const [arr, str] = f();
console.log({ arr, str })
arr.map((i) => i);
I get an issue when testing against the nightly build on https://www.typescriptlang.org/play
Property 'map' does not exist on type 'string | number[]'.
Property 'map' does not exist on type 'string'.(2339)
@keithbro, the problem here is the inferred type of f, which TS assumes to be an array and therefore results in the union that you see.
Most helpful comment
+1 to this - it's especially annoying in my common use case of passing down the return value from
useStatein React.