anups anups - 3 months ago 22
TypeScript Question

Unable to access parameters from union types (TypeScript)

Why am not able to access attributes in union types like this:

export interface ICondition {
field: string
operator: string
value: string
}

export interface IConditionGroup {
conditions: ICondition[]
group_operator: string
}

function foo(item: ICondition | IConditionGroup) {
if(typeof item.conditions === "undefined") { // does not work
let field = item.field; // does not work
///.. do something
} else {
let conditions = item.conditions; // does not work
/// .. do something else
}
}


I get these errors:

error TS2339: Property 'conditions' does not exist on type 'ICondition | IConditionGroup'.
error TS2339: Property 'conditions' does not exist on type 'ICondition | IConditionGroup'.
error TS2339: Property 'field' does not exist on type 'ICondition | IConditionGroup'.


But I have to cast types to get it to work - like this:

function foo2(inputItem: ICondition | IConditionGroup) {
if(typeof (<IConditionGroup>inputItem).conditions === "undefined") {
let item= (<ICondition>inputItem);
let field = item.field;
///.. do something
} else {
let item= (<IConditionGroup>inputItem);
let conditions = item.conditions;
/// .. do something else
}
}


I understand that the type information is lost in JS, so why do I have to explicitly cast it in TS?

Answer

Typescript handles this with Type Guards, usually it's as simple as:

if (typeof item === "string") { ... } else { ... }

Or

if (item instanceof MyClass) { ... } else { ... }

But in your case as you're using interfaces that's not possible, so you'll need to create your own User-Defined Type Guards:

function isConditionGroup(item: ICondition | IConditionGroup): item is IConditionGroup {
    return (item as IConditionGroup).conditions !== undefined;
}

function foo(item: ICondition | IConditionGroup) {
    if (isConditionGroup(item)) {
        let conditions = item.conditions;
        // do something
    } else {
        let field = item.field;
        // do something else
    }
}

(code in playground)

You can also do it without type guards:

function foo(item: ICondition | IConditionGroup) {
    if ((item as IConditionGroup).conditions !== undefined) {
        let conditions = (item as IConditionGroup).conditions;
        // do something
    } else {
        let field = (item as ICondition).field;
        // do something else
    }
}

But that's way to verbose as you need to type assert item 3 times instead of once.

Comments