SavvyShah
SavvyShah

Reputation: 67

How to type narrow array types passed in functions?

I want to do type narrowing when working with array types but union members aren't getting distinguished. It is weird that they work in the first function print1() which is without arrays but doesn't work in the second function print2().

type Point = {
  x: number;
  y:number;
}
type Pair = [number, number]

type Coords = Point | Pair

function print1(a: Coords): number{
  if("x" in a){
    return a.x;
  } else{
    return a[0];
  }
}

function print2(a: Pair[] | Point[]): void{
  if("x" in a[0]){
    console.log(a[0].x)
  } else{
    console.log(a[0][0])
  }
}

enter image description here enter image description here

Upvotes: 2

Views: 953

Answers (2)

Kelvin Schoofs
Kelvin Schoofs

Reputation: 8718

It's interesting that it successfully narrows a[0] but not a[1] or the whole array, even though narrowing a[0] can also narrow the rest of the array. You could consider reporting it if nobody hasn't already.

Until then, you can "fix" this with a simple type guard/predicate:

const isPairArray = (arr: Pair[] | Point[]): arr is Pair[] => arr[0] && 'x' in arr[0];

function print2(a: Pair[] | Point[]): void {
    if (isPairArray(a)) {
        console.log(a[0][0])
    } else {
        console.log(a[0].x)
    }
}

In that case, a is properly narrowed to either Pair[] or Point[].

Upvotes: 3

Olian04
Olian04

Reputation: 6872

This is a "limitation" with TypeScript (its actually a great feature of typescript, since JavaScript is the real culprit here). According to typescript a[0] in the expression 'x' in a[0] might not reference the same value as a[0] in the log statement console.log(a[0].x).

In this case, storing a[0] in a variable will solve the issue.

function print2(a: Pair[] | Point[]): void{
  const b = a[0];
  if("x" in b){
    console.log(b.x)
  } else{
    console.log(b[0])
  }
}

Upvotes: 2

Related Questions