Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't narrow simple TypeScript union type with undefined property

I have two unioned types, one has a property and the other one hasn't. I assumed that checking for the existence of that property would allow me to narrow it down, but it isn't working.

I've created this Playground repro. This other very similar thing seems to work just fine. Am I using unions the wrong way?

Here's the code for the sake of completeness:

export interface AuthenticatedProfile {
    readonly userId: string;
    readonly name: string;
}
export interface AnonymousProfile {
    readonly userId: undefined;
    readonly otherProp: string;
}
export type Profile = AnonymousProfile | AuthenticatedProfile;

function handleProfile(prof: Profile) {
    if (prof.userId) {
        console.log(prof.name);
    }
}

Thanks!

like image 239
Martín Coll Avatar asked Jun 16 '17 07:06

Martín Coll


People also ask

How do I create a Union in typescript?

A common technique for working with unions is to have a single field which uses literal types which you can use to let TypeScript narrow down the possible current type. For example, we’re going to create a union of three types which have a single shared field. ts typeNetworkLoadingState= {

How to narrow the Union of networkloadingstate in typescript?

Property 'code' does not exist on type 'NetworkLoadingState'. // By switching on state, TypeScript can narrow the union // down in code flow analysis switch(state.state) {

What is narrowing in typescript?

TypeScript follows possible paths of execution that our programs can take to analyze the most specific possible type of a value at a given position. It looks at these special checks (called type guards) and assignments, and the process of refining types to more specific types than declared is called narrowing .

What is the difference between typescript and JavaScript?

JavaScript has an operator for determining if an object has a property with a name: the in operator. TypeScript takes this into account as a way to narrow down potential types. For example, with the code: "value" in x. where "value" is a string literal and x is a union type.


2 Answers

You can use type guards to restrict the type of the prof parameter.

export interface AuthenticatedProfile {
    readonly userId: string;
    readonly name: string;
}
export interface AnonymousProfile {
    readonly userId: undefined;
    readonly otherProp: string;
}
export type Profile = AnonymousProfile | AuthenticatedProfile;

function isAuthenticatedProfile(prof: Profile): prof is AuthenticatedProfile {
    return (<AuthenticatedProfile>prof).name !== undefined;
}

function isAnonymousProfile(prof: Profile): prof is AnonymousProfile {
    return (<AnonymousProfile>prof).otherProp !== undefined;
}

function handleProfile(prof: Profile) {
    if (isAuthenticatedProfile(prof)) {
        console.log(prof.name);
    } else if (isAnonymousProfile(prof)) {
        console.log(prof.otherProp);
    }
}

You can read more about type guards in typescript in the Advanced Types section in the handbook.

like image 189
toskv Avatar answered Oct 16 '22 09:10

toskv


You can do the following:

export interface AuthenticatedProfile {
    readonly type: "AuthenticatedProfile";
    readonly userId: string;
    readonly name: string;
}

export interface AnonymousProfile {
    readonly type: "AnonymousProfile";
    readonly userId: undefined;
    readonly otherProp: string;
}

export type Profile = AnonymousProfile | AuthenticatedProfile;

function handleProfile(prof: Profile) {
    if (prof.type === "AnonymousProfile") {
        console.log(prof.name); // Error
        console.log(prof.otherProp); // OK
        console.log(prof.userId); // OK
    }
    if (prof.type === "AuthenticatedProfile") {
        console.log(prof.name); // OK 
        console.log(prof.otherProp); // Error
        console.log(prof.userId); // OK
    }
}
like image 3
Remo H. Jansen Avatar answered Oct 16 '22 09:10

Remo H. Jansen