Skip to content

Array concat has wrong types #47351

Open
Open

Description

@ljharb

Bug Report

A not-uncommon JS idiom is [].concat(x || [], y || [], z || []) as a faster, simpler, more backwards-compatible [x, y, z].flatMap(x => x || []). However, when I try, I get type errors.

🔎 Search Terms

array concat
concat overload

🕗 Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about array concat

⏯ Playground Link

Playground link with relevant code

💻 Code

const a = [].concat(
    true ? 'a' : [],
    false ? 'b' : [],
);

const x = [].concat(
    false ? 'b' : [],
    true ? 'a' : [],
);

🙁 Actual behavior

No overload matches this call.
  Overload 1 of 2, '(...items: ConcatArray<never>[]): never[]', gave the following error.
    Argument of type 'never[] | "a"' is not assignable to parameter of type 'ConcatArray<never>'.
      Type 'string' is not assignable to type 'ConcatArray<never>'.
  Overload 2 of 2, '(...items: ConcatArray<never>[]): never[]', gave the following error.
    Argument of type 'never[] | "a"' is not assignable to parameter of type 'ConcatArray<never>'.
      Type 'string' is not assignable to type 'ConcatArray<never>'.(2769)

🙂 Expected behavior

No type errors (except perhaps that the variable may be any until i provide an explicit type for the resulting array), because this is valid JavaScript since at least ES3.

Activity

  1. ajafff commented on Jan 8, 2022

    @ajafff
    Contributor

    As a workaround you can use new Array<string>().concat( true ? 'a' : [], false ? 'b' : [])

  2. ljharb commented on Jan 8, 2022

    @ljharb
    ContributorAuthor

    That would create a sparse array, which is deoptimized in engines, so that would never be a good workaround (new Array should never, ever be used).

  3. MartinJohns commented on Jan 8, 2022

    @MartinJohns
    Contributor

    ([] as string[]) works as well to help the type-checker.

  4. MartinJohns commented on Jan 8, 2022

    @MartinJohns
    Contributor

    Duplicate of #36554 (comment). Used search terms: never array concat

  5. ljharb commented on Jan 8, 2022

    @ljharb
    ContributorAuthor

    Thanks for the workaround suggestion! Unfortunately that doesn’t help me in JS, using JSDoc.

  6. fatcerberus commented on Jan 8, 2022

    @fatcerberus

    Your examples work as-is for me in JS, even with checkJs on; that said, the equivalent JSDoc-based workaround would be

    const a = /** @type {string[]} */ ([]).concat(
        true ? 'a' : [],
        false ? 'b' : [],
    );

    But this won't work in a .ts file (or your playground) because JSDoc isn't used for typing there.

  7. MartinJohns commented on Jan 8, 2022

    @MartinJohns
    Contributor

    Bruce Pascoe (@fatcerberus) The examples don't work with strictNullChecks enabled. The playground strangely turns this flag off when you switch to JS mode, I fell for it the first time as well.

  8. SancheZz commented on Jan 9, 2022

    @SancheZz

    Jordan Harband (@ljharb) you can use spread operator instead of the concat method as a workaround.

  9. MartinJohns commented on Jan 9, 2022

    @MartinJohns
    Contributor

    SancheZz He's intentionally not using the spread operator. See his first sentence, where he mentions:

    more backwards-compatible

  10. ljharb commented on Jan 9, 2022

    @ljharb
    ContributorAuthor

    Also, spread has different semantics - a string is spread into code units, a Map into entries, a Set into values, but the precise semantic I want here is IsConcatSpreadable, which only concat, flat, and flatMap have.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugA bug in TypeScriptDomain: lib.d.tsThe issue relates to the different libraries shipped with TypeScriptHelp WantedYou can do this

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions