Typescript: typescript can't infer destructuring of array properly

Created on 18 Jul 2019  路  7Comments  路  Source: microsoft/TypeScript


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

Bug Fix Available

Most helpful comment

+1 to this - it's especially annoying in my common use case of passing down the return value from useState in React.

All 7 comments

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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

wmaurer picture wmaurer  路  3Comments

blendsdk picture blendsdk  路  3Comments

bgrieder picture bgrieder  路  3Comments

DanielRosenwasser picture DanielRosenwasser  路  3Comments

uber5001 picture uber5001  路  3Comments