Skip to content

Confusing 'never' return type when appending an empty array #9976

Closed

Description

TypeScript Version: 2.0.0

Code

        var x = [];
        x.push(1);

Expected behavior:
Accept this, and have the type inferencer postulate x:number[]. Alternatively, an error message along the lines of "cannot assign number to parameter {}" (because type inferencer could also assume x:undefined[]).

Actual behavior:
(with strictNullCheck)

error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'.

This might be because of how never is documented as the "bottom" type (for functions that never return), but since the code isn't using the result expression, this is pretty confusing. Is this because the assumed type of x is never[]?

I tried looking for related issues, I feel like it's slightly related to empty tuple discussions, but not 100% sure.

Activity

  1. Arnavion commented on Jul 27, 2016

    @Arnavion
  2. basarat commented on Aug 5, 2016

    @basarat
    Contributor

    Maybe there Is there something like noImplicitAny that will make sense for any[] instead? 🌹

  3. kitsonk commented on Aug 5, 2016

    @kitsonk
    Contributor

    Basarat Ali Syed (@basarat) it is called not setting --strictNullCheck 😉. I would say it is desirable behaviour that if you have said that you want to make sure you don't have null values, that the type system becomes more strict. You can of course be explicit about it (x: any[] = []) and it works.

  4. RyanCavanaugh commented on Aug 5, 2016

    @RyanCavanaugh
    Member

    #8944 covers why this is done

  5. robertpenner commented on May 25, 2017

    @robertpenner

    Ryan Cavanaugh (@RyanCavanaugh) I've read through the related discussions and now I get the logic of why let a = [] is inferred as never[]. But prior to that, the compiler error message is not very helpful; it's quite difficult to understand what is going on and how to resolve the error.

  6. 108adams commented on Aug 10, 2017

    @108adams

    My question: why never in this:

    let rows: Element[] = [].concat(generateElements());

    but not in this:

    let rows: Element[] = [];
    rows.concat(generateElements());
  7. EliRobinson commented on Mar 28, 2018

    @EliRobinson

    Adam Kucharczyk (@108adams) - Your code example doesn't actually concat the rows. As concat returns a new array. By doing rows.concat(generateElements()); you aren't actually updating rows to contain generateElements(). Might be why this doesn't throw the error?

    You'd need to say:

    let rows: Element[] = [];
    
    rows = rows.concat(generateElements());
    
  8. TAGC commented on May 18, 2018

    @TAGC

    I love how Typescript is still giving me an error:

    "TS2345: Argument of type '{ style: { position: string; x: number; y: number; height: any; width: any; }; }' is not assignable to parameter of type 'never'."

    When I've clearly annotated the type:

    export function getTilesInSection(
      x: number,
      y: number,
      sectionWidth: number,
      sectionHeight: number,
      tileSize: number
    ): GridTileContainer[] {
      const halfSectionWidth = sectionWidth / 2;
      const halfSectionHeight = sectionHeight / 2;
      const halfTileSize = tileSize / 2;
      const x1 = x - halfSectionWidth;
      const x2 = x + halfSectionWidth;
      const y1 = y - halfSectionHeight;
      const y2 = y + halfSectionHeight;
    
      const xMidpoints = multiplesInRange(tileSize, x1, x2);
      const yMidpoints = multiplesInRange(tileSize, y1, y2);
    
      let containers: GridTileContainer[] = [];
    
      for (const x of xMidpoints) {
        for (const y of yMidpoints) {
          containers.push({
            style: {
              position: "absolute",
              x: x - halfTileSize,
              y: y - halfTileSize,
              height: tileSize,
              width: tileSize
            }
          });
        }
      }
    
      return containers;
    }

    I don't see anything wrong with that, and no errors display in VS Code, yet it won't compile. 😕

  9. TAGC commented on Jun 20, 2018

    @TAGC

    Robert Penner (@robertpenner) I think it's because that was in a Vue file. I've found out that there's a few issues in integrating Vue with TypeScript.

    Edit: scratch that, it's unlikely that was in a Vue component file, since I don't need to export functions in those. Although I was probably consuming that function within a Vue component and maybe that's what caused the issue.

  10. leoyli commented on Apr 4, 2019

    @leoyli

    I still have question on this:

    [].concat(a, b);  // <- not good
    Array().concat(a, b) // <- okay

    I have a use case that a, b are in the same type but one of them are also possible to be undefined. I want to merge them into an array without mutation... The firs expressional approach failed but the second functional approach not give me any error?!

    I'm using this workaround since .concat is not happy to be undefined which is fine (it won't extend the array but just return a copy).

  11. s-ong-c commented on Aug 6, 2019

    @s-ong-c
    const getAverage = (numbers: number[]) => {
        console.log('getAverage');
        if (numbers.length === 0) return 0;
        const sum = numbers.reduce((a,b) => a + b);
        return sum / numbers.length;
    }
    interface AverageProps{}
    
    const Average: React.SFC<AverageProps> = props => {
        const [list, setList] = useState([]);
        const [number, setNumber] = useState('');
        const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
            setNumber(e.target.value);
        }
        const onInsert = (e: React.MouseEvent<HTMLButtonElement>) => {
            console.log(number);
            const nextList = list.concat(parseInt(number));
            setNumber('');
    
        }
    

    why type error const nextList = list.concat(parseInt(number));

  12. danielmorell commented on Mar 24, 2021

    @danielmorell

    Here is a short list of every way I can think of writing this...

    let foo: string[] = ['one', 'two', 'three'];
    
    // Bad - this will throw a compile error.
    let one: string[] = [].concat(...foo);
    
    // Good
    let two: string[] = Array().concat(...foo);
    
    // Good
    let three: string[] = [];
    three.concat(...foo);
    
    // Good
    let four: string[] = ([] as string[]).concat(...foo); 
    
    // Good - but throws an error in the playground because it thinks this the type annotation is JSX.
    // You must set JSX to "none" to remove the error.
    let four: string[] = (<string[]>[]).concat(...foo);
  13. locked as resolved and limited conversation to collaborators on Oct 22, 2025
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

    Working as IntendedThe behavior described is the intended behavior; this is not a bug

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions