Невозможно сузить простой тип объединения TypeScript с неопределенным свойством

У меня есть два объединенных типа, у одного есть свойство, а у другого нет. Я предположил, что проверка существования этого свойства позволит мне сузить его, но это не работает.

Я создал репродукция этой игровой площадки. Этот другой очень похожая вещь работает нормально. Я неправильно использую профсоюзы?

Вот код для полноты:

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

Спасибо!


person Martín Coll    schedule 16.06.2017    source источник


Ответы (2)


Вы можете использовать защиту типа, чтобы ограничить тип параметра prof.

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

Вы можете узнать больше о защите типов в машинописном тексте в разделе «Расширенные типы» в руководство.

person toskv    schedule 16.06.2017
comment
Ваше решение выглядит хорошо. У меня только один вопрос: если свойство userId является частью обоих типов, и каждый тип может принимать только непересекающийся набор значений, то почему TypeScript не может вывести тип после моей первоначальной проверки? - person Martín Coll; 16.06.2017
comment
для меня вызов prof.userId отлично работает без каких-либо охранников. - person toskv; 16.06.2017
comment
На самом деле это известная проблема. github.com/Microsoft/TypeScript/issues/12600. Не знаю, почему вы не видите ошибку, она прямо там, в репродукции детской площадки, которую я опубликовал - person Martín Coll; 17.06.2017
comment
в этой детской ссылке доступ к prof.userId работает нормально. доступ к prof.name дает ошибку. и это нормально, потому что это не в обоих типах. - person toskv; 17.06.2017
comment
@ MartínColl, если это был правильный ответ, отметьте его как правильный, чтобы другие люди, видящие этот вопрос, знали. :) Если нет, дайте мне знать, чего не хватает. - person toskv; 28.06.2017

Вы можете сделать следующее:

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
    }
}
person Remo H. Jansen    schedule 16.06.2017
comment
Хорошо знать эту альтернативу, но я буду придерживаться другого решения, которое не меняет мои значения или типы. Но у меня есть один вопрос: если свойство userId является частью обоих типов, и каждый тип может принимать только непересекающийся набор значений, то почему TypeScript не может вывести тип после моей первоначальной проверки? - person Martín Coll; 16.06.2017