Home > Net >  TS array map needs type assertion - why?
TS array map needs type assertion - why?

Time:09-30

This code

function myFilter(debts:Map<string, number>) : Map<string, number>{
  return new Map([...debts]
    .map(d => [d[0], Math.round(d[1] * 10) / 10]) // error
    .filter(d => d[1] != 0)
  )
}

gives an error because the map() ostensibly can yield [string | number][][].

But this does work:

function myFilter(debts:Map<string, number>) : Map<string, number>{
  return new Map([...debts]
    .map(d => [d[0], Math.round(d[1] * 10) / 10] as [string, number])
    .filter(d => d[1] != 0)
  )
}

I don't understand why this assertion is necessary.

CodePudding user response:

Why?

Because TypeScript does not infer a tuple return type in your callback:

.map(d => [d[0], Math.round(d[1] * 10) / 10])

It is inferred to be a union of all of the possible value types at each element: If you rewrite it using an explicit return statement, you can see the inferred type:

TS Playground

.map(d => {
  const result = [d[0], Math.round(d[1] * 10) / 10];
      //^? const result: (string | number)[]
  return result;
})

Alternate solution:

You can also supply a generic type parameter when using Array.prototype.map<T>() instead of using a type assertion:

TS Playground

.map<[string, number]>(d => [d[0], Math.round(d[1] * 10) / 10])

This will satisfy the compiler and help ensure that the value returned by your callback is assignable to the supplied generic type.

CodePudding user response:

I don't understand why this assertion is necessary.

Because [string, number] is only one of several possible combinations/types that (string | number)[] may cover.

But it could also contain any of these [number] or [number, string] or [string, string, string] and plenty more which are all not compatible with the Map<string, number>.

  • Related