Can't narrow simple TypeScript union type with

2019-06-25 13:00发布

问题:

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!

回答1:

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.



回答2:

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