Search code examples
typescripttypescript1.6

TypeScript type guard Oddity


I'm using a TypeScript type guard in a ternary operator within a loop and seeing a behavior I don't understand.

My interfaces

interface INamed {
    name: string;
}

interface IOtherNamed extends INamed {
    otherName: string;
}

My type guard

function isOther(obj: any): obj is IOtherNamed {
    ... // some check that returns boolean
}

General Usage Sample

var list: Array<{named: INamed}> = [];

for(let item of list) {
    var other: IOtherNamed = ...
}

Inside of my for .. of loop I am using my type guard to assign either my current item or null to a variable of IOtherNamed.

This doesn't work

// Compiler Error: INamed is not assignable to IOtherNamed
for(let item of list) {
    var other: IOtherNamed = isOther(item.named) ? item.named : null;
}

This does

for(let item of list) {
    var named: INamed = item.named;
    var other2: IOtherNamed = isOther(named) ? named : null;
}

My Questions

  1. Is this by design that one of these works while the other doesn't?
  2. If by design, what is the nuance here that determines when it works or not? In particular why does assigning my object to a new variable (without any type change) get rid of the compiler error?

Solution

  • Yes, this is by design for TypeScript < 2.0:

    Note that type guards affect types of variables and parameters only and have no effect on members of objects such as properties.

    — 4.20 from the language specification (PDF, page 83)

    So the reason it works in the second scenario is because you have assigned the property to a variable and then type guarded that variable.

    Update: As Alex pointed out, TypeScript 2.0 will support type guards on properties.