Typescript: Не удается получить доступ к свойству в объединении типов объектов для свойств, определенных не для всех членов объединения

Созданный на 10 дек. 2016  ·  12Комментарии  ·  Источник: microsoft/TypeScript

Версия TypeScript: 2.1.1

Код

type A = { a: string; } 
type B = { b: string; }
type AorB = A | B;

declare const AorB: AorB;

if (AorB.a) {
    // use AorB.a
}

Ожидаемое поведение:
Код компилируется без ошибок. Несмотря на то, что a не (обязательно) существует на всех составляющих союза, тот факт, что он существует на некоторых, должен позволить мне проверить наличие .a и использовать его, если он присутствует.

Фактическое поведение:
Typescript жалуется: «Свойство a не существует для типа AorB ... Свойство a не существует для типа B.»

Question

Самый полезный комментарий

я нахожу решение в книге:

interface A { x:  number;} 
interface B { y:  string;}
function doStuff ( q: A | B ) {
  if ( 'x'  in q) {
    //  if type A...
  }
  else {
    // if type B...
  }
}

Выдержка из: Басарат Али Сайед. «Глубокое погружение в TypeScript». Apple Книги.

свойство объекта и может использоваться в качестве защиты типа, а TypeScript может знать, какой тип вы использовали.

Все 12 Комментарий

В документе говорится:

Чтобы тот же код работал, нам нужно использовать утверждение типа:

let pet = getSmallPet();

if ((<Fish>pet).swim) {
    (<Fish>pet).swim();
}
else {
    (<Bird>pet).fly();
}

http://www.typescriptlang.org/docs/handbook/advanced-types.html

Тогда в вашем образце:

if ((<A>AorB).a) {
    // use AorB.a
}

Проблема здесь в том, что поскольку B не объявляет свойство a , во время выполнения оно может иметь свойство a любого возможного типа (поскольку вы можете назначить объект с любым набором свойств в B если он имеет свойство b типа string). Вы можете заставить его работать, явно объявив свойство a: undefined в B (таким образом гарантируя, что B не будет иметь случайного свойства a ):

type A = { a: string; } 
type B = { b: string; a: undefined }
type AorB = A | B;

declare const AorB: AorB;

if (AorB.a) {
    // Ok
}

Имеет смысл. Мозг пердит.

Если вы определяете a: undefined для типа B, вам нужно будет установить его в undefined при создании / передаче переменной.
Чтобы обойти это, вы можете объявить a как a?: undefined , и машинописный текст будет счастлив, если вы его опустите.

Если я правильно понимаю комментарии, это должно сработать (но бросает; проверено на игровой площадке): это ошибка или нет? 🤔

type LinkProps = {
    to: string;
    onClick?: undefined;
    // Link specific props:
    target: string;
}

type ButtonProps = {
    to?: undefined;
    onClick: Function;
    // Button specific props:
    disabled: boolean;
}

type ActionProps = LinkProps | ButtonProps;

const Action = (props: ActionProps) =>
    props.to ?
        'Link with target: ' + props.target // Error not on ButtonProps
    :
        'Button with disabled: ' + props.disabled; // Error: not on LinkProps

Action({
  to: 'dssd',
  target: '_blank'
});

Я вообще этого не понимаю. Разве if ((<A>AorB).a) то же самое, что использовать if (A.a) потому что вы заставляете его снова набрать A ?

Если вы определяете a: undefined для типа B, вам нужно будет установить его в undefined при создании / передаче переменной.
Чтобы обойти это, вы можете объявить a как a?: undefined , и машинописный текст будет счастлив, если вы его опустите.

может быть лучше a?: never , теперь нельзя присвоить undefined по ошибке

Что, если вы не дадите имя каждому члену союза? (я не могу сделать утверждение типа, как указано выше, без имени для утвержденного типа?)

type u = "str" | {prop:"val"};
function f(arg:u){return arg.prop} // TypeScript: Property 'prop' does not exist on type 'u'

Любое решение?

я нахожу решение в книге:

interface A { x:  number;} 
interface B { y:  string;}
function doStuff ( q: A | B ) {
  if ( 'x'  in q) {
    //  if type A...
  }
  else {
    // if type B...
  }
}

Выдержка из: Басарат Али Сайед. «Глубокое погружение в TypeScript». Apple Книги.

свойство объекта и может использоваться в качестве защиты типа, а TypeScript может знать, какой тип вы использовали.

type ColorItemType = {
    colorId: number,
    colorName: string,
}

type NumItemType = {
    numId: number,
    numName: string
}

type ResType = {
    itemId: number,
    // 0 color 1 num
    type: number,
    itemInfo: {
        colorList: Array<ColorItemType>
        numList: Array<NumItemType>
    }
}

const request = () => {
    return [{
        itemId: 1,
        type: 0,
        itemInfo: {
            colorList: [{
                colorId: 1,
                colorName: 'blue'
            }],
            numList: []
        }
    }];
};

const dataSource: Array<ResType> = request();

const formatData = dataSource.map(dataItem => {
    const list: Array<ColorItemType | NumItemType> = dataItem.type === 1 ? dataItem.itemInfo.numList : dataItem.itemInfo.colorList;
    return list.map(listItem => {
        return {
            // An error will be reported here
            value: listItem.numId || listItem.colorId,
            label: listItem.numName || listItem.colorName
        };
    });
});

Проблема здесь в том, что поскольку B не объявляет свойство a , во время выполнения оно может иметь свойство a любого возможного типа (поскольку вы можете назначить объект с любым набором свойств в B если он имеет свойство b типа string). Вы можете заставить его работать, явно объявив свойство a: undefined в B (таким образом гарантируя, что B не будет иметь случайного свойства a ):

type A = { a: string; } 
type B = { b: string; a: undefined }
type AorB = A | B;

declare const AorB: AorB;

if (AorB.a) {
    // Ok
}

У меня не работает 07.10.2020

И я считаю, что такое поведение TS нехорошо, потому что оно мало помогает разработчику ...

Была ли эта страница полезной?
5 / 5 - 1 рейтинги

Смежные вопросы

blendsdk picture blendsdk  ·  3Комментарии

dlaberge picture dlaberge  ·  3Комментарии

Antony-Jones picture Antony-Jones  ·  3Комментарии

uber5001 picture uber5001  ·  3Комментарии

wmaurer picture wmaurer  ·  3Комментарии