Typescript: Confusing 'never' return type when appending an empty array

Created on 27 Jul 2016  ·  12Comments  ·  Source: microsoft/TypeScript

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.

Working as Intended

Most helpful comment

@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.

All 12 comments

8944

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

@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.

8944 covers why this is done

@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.

My question: why never in this:

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

but not in this:

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

@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());

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. 😕

@TAGC I tried your code in TypeScript 2.9.1 and don't hit the never error:

https://agentcooper.github.io/typescript-play/?noImplicitAny=false&target=5#code/JYOwLgpgTgZghgYwgAgOJWAEwCrADYQDCA9uHKNAN4C+AUDAK4gJjCnIC2DerADgQGcAkiABKcEAHMIAClYEAysABeEAFzIQDDgCNoAGmRwNW3QeQ6T2vVACUVs1ADaAXWSVkUCGAZQQyVwBuZDpaCAAPXmIoMGRGZlZ2aTBcQREFCBY2EBlaZGRwhxt9POQATyKDUoFMxJAAdSwwAAtKqBL8mqzSAAkIYElmsDaO5HkIJVU22ns0DBx8IlIwchBoV3dShFIBWOa4PBgM7oam5uQAXmQuusbMFuQAemQAJkCtnb2Do9rsvoGhpdrr9ev1BrFnm8PiBdsh9odUhMVCgruNJihIe98tsYbFwgBGIHhZAAWjh32OtzOWOQONh4ReROQAGpyYdKdk7i0aXTYmVCVcyqS2T8Tv9wTzPuVGYKWSKOaCAWB3tD6QBZLBRUBgARArg8YD8CDCMQSaRyRbowwE60vWyS3HlDWYLXgXVXfV8NKmqSyNHIwz8wN2lX5AixHErChQAQadBYREkMjRjZXIK0UowaLIGS8grIYgwArO1062ybfL5LNQHN5oWFp2a4jagTlyilSu05araACAB0vAYAmaMnbnc7uzKBA0Y-HnaiAmAdQ0ACI4DoBMQ8AxICvRnP8oV82T4TBEVaOweKuVhafzwHL3PmmChhp-ap9weAO5nN+W5GPpWdBztQ9qXsBIQZvkXg+H4XbJmsMbvNQQA

@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.

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

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

Was this page helpful?
0 / 5 - 0 ratings

Related issues

dlaberge picture dlaberge  ·  3Comments

bgrieder picture bgrieder  ·  3Comments

jbondc picture jbondc  ·  3Comments

fwanicka picture fwanicka  ·  3Comments

CyrusNajmabadi picture CyrusNajmabadi  ·  3Comments