Home > OS >  Odd error thrown by typescript compiler when looping over union typed array
Odd error thrown by typescript compiler when looping over union typed array

Time:01-29

Here's an example (not a good one :p):

type RandomType = {x: number, y: number}
type UnifiedTypes =  RandomType | 0

type ArrayOfTypes = Array<(RandomType | UnifiedTypes)[]>

const newIns: ArrayOfTypes = [[0, {x: 10, y: 201}], [0, {x: 10, y: 201}]]

for(let i=0; i < newIns.length;   i){
    for(let j=0; j < newIns[i].length;   j){
        if(newIns[i][j] !== 0){
            newIns[i][j].x = 30 // Property 'x' does not exist on type 'UnifiedTypes'. Property 'x' does not exist on type '0'
        }
    }
}

// Hoewever, when outside of loop compiler runs fine
if(newIns[0][0] !== 0) {
   newIns[0][0].x = 33; // no error thrown
}

Narrowing doesn't seem to work when looping through a union typed array so I'm bit lost. Did I miss smt?

By narrowing down which type the indexed element is going to hold, the typescript compiler should be able to figure out the type of the element array in the specified index and hence assignement occurs safely.

CodePudding user response:

In my opinion when you type of newIns[i][j] is understood as RandomType | UnifiedTypes at the point. But when you check newIns[i][j] !== 0 in the loop, the type of newIns[i][j] is narrowed to UnifiedTypes which is RandomType | 0 . But, when you check newIns[0][0] !== 0, the type of newIns[0][0] is narrowed to RandomType, so the compiler knows that newIns[0][0] has a property x. In other words see below down example.

type Shape = 'circle' | 'square'
type Color = 'red' | 'blue'
type Item = Shape | Color

const items: Item[] = ['circle', 'blue', 'red']

for (let i = 0; i < items.length; i  ) {
  if (items[i] !== 'circle') {
    console.log(items[i].length) // Property 'length' does not exist on type 'Item'.
  }
}

As you're well aware items refers to Item type and items[i] is inferred as Item, which is Shape | Color. But, when you check items[i] !== 'circle', you narrow the type of items[i] to Color, because you are excluding the possibility that it is 'circle' from the union.

You could fix this like this.

console.log((items[i] as Color).length)

CodePudding user response:

This will work if you create a constant for the newIns[i][j] element. For her, TS will be able to correctly process the type.

type RandomType = {x: number, y: number}
type UnifiedTypes =  RandomType | 0

type ArrayOfTypes = Array<(RandomType | UnifiedTypes)[]>

const newIns: ArrayOfTypes = [[0, {x: 10, y: 201}], [0, {x: 10, y: 201}]]

for(let i=0; i < newIns.length;   i){
    for(let j=0; j < newIns[i].length;   j){
      const element = newIns[i][j]
        if(element !== 0){
            element.x = 30
        }
    }
}

  • Related