Skip to content

typescript can't infer destructuring of array properly  #32465

Closed

Description

@deepkolos

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: #5296

Activity

  1. russelldavis commented on Jul 26, 2019

    @russelldavis
    Contributor

    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 (@deepkolos) it might help to update your title and description.)

  2. RyanCavanaugh commented on Jul 26, 2019

    @RyanCavanaugh
    Member

    Simpler repro:

    declare const sb: [string, boolean];
    // Shouldn't error
    const k: [number, string, boolean] = [1, ...sb];
  3. changed the title [-]typescript can't infer destruction of array properly [/-] [+]typescript can't infer destructuring of array properly [/+] on Jul 29, 2019
  4. micheleangioni commented on Dec 9, 2019

    @micheleangioni

    I ran into this problem as well, it would be nice to have it fixed :)

  5. dianafa commented on Dec 18, 2019

    @dianafa

    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 :)

  6. tylervick commented on Dec 30, 2019

    @tylervick

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

  7. added 3 commits that reference this issue on Feb 18, 2020
    8472084
    672b0de
    ebea675
  8. added a commit that references this issue on Feb 27, 2020
    d0702f8
  9. added a commit that references this issue on Feb 27, 2020
    db1c440
  10. added a commit that references this issue on Feb 27, 2020
    e71614a
  11. keithbro commented on Apr 21, 2020

    @keithbro

    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)
    
  12. elibarzilay commented on Apr 22, 2020

    @elibarzilay
    Contributor

    Keith Broughton (@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.

  13. locked as resolved and limited conversation to collaborators on Oct 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

BugA bug in TypeScriptFix AvailableA PR has been opened for this issue

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions