2

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.

2 Answers 2

1

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)

Sign up to request clarification or add additional context in comments.

3 Comments

When narrowing, the expected type should normally be RandomType not UnifiedTypes & I did mention this behaviour only happens within a loop. Appreciate the effort & and thanks for the lesson bro I get it.
The real problem with your question is it is narrowing the type of newIns[i][j] to 0. And it's not understood the RandomType. I mean complier. Try this (newIns[i][j] as RandomType).x = 30 include the RandomType
Worked! Don't know how but still this solved the issue. But I don't get why this only happens inside loops. Thanks mate!
0

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
        }
    }
}

1 Comment

Yes, indeed it works now but it doesn't actually address the core problem. Thanks though.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.