Typescript: O acesso à propriedade na união de tipos de objeto falha para propriedades não definidas em todos os membros da união

Criado em 10 dez. 2016  ·  12Comentários  ·  Fonte: microsoft/TypeScript

Versão TypeScript: 2.1.1

Código

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

declare const AorB: AorB;

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

Comportamento esperado:
O código é compilado sem erros. Mesmo que a não exista (necessariamente) em todos os constituintes do sindicato, o fato de existir em alguns deve permitir-me verificar se há .a e usá-lo se estiver presente.

Comportamento real:
O texto datilografado reclama: "A propriedade a não existe no tipo AorB ... A propriedade a não existe no tipo B."

Question

Comentários muito úteis

eu encontro uma solução no livro:

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

Trecho de: Basarat Ali Syed. “Mergulho Profundo de TypeScript.” Livros da Apple.

uma propriedade em um objeto e pode ser usada como uma proteção de tipo, e o TypeScript pode saber qual tipo você usou.

Todos 12 comentários

O doc diz:

Para fazer o mesmo código funcionar, precisaremos usar uma declaração de tipo:

let pet = getSmallPet();

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

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

Então, em sua amostra:

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

O problema aqui é que, como B não declara uma propriedade a , pode em tempo de execução ter uma propriedade a de qualquer tipo possível (porque você pode atribuir um objeto com qualquer conjunto de propriedades para um B , desde que tenha uma propriedade b do tipo string). Você pode fazer funcionar explicitamente declarando uma propriedade a: undefined em B (garantindo assim que B não terá alguma propriedade a aleatória):

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

declare const AorB: AorB;

if (AorB.a) {
    // Ok
}

Faz todo o sentido. Peido de cérebro.

Se você definir a: undefined no tipo B, você terá que defini-lo para undefined ao criar / passar uma variável.
Para contornar isso, você pode declarar a como a?: undefined , e o texto digitado ficará feliz se você omiti-lo.

Se entendi os comentários corretamente, isso deve funcionar (mas joga; testado no playground): Isso é um bug ou não? 🤔

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

Eu não entendo nada disso. if ((<A>AorB).a) o mesmo que usar if (A.a) porque você o está forçando de volta para digitar A ?

Se você definir a: undefined no tipo B, você terá que defini-lo para undefined ao criar / passar uma variável.
Para contornar isso, você pode declarar a como a?: undefined , e o texto digitado ficará feliz se você omiti-lo.

pode ser melhor a?: never , agora você não pode atribuir undefined por engano

E se você não der um nome a cada um dos membros do sindicato? (não posso fazer uma declaração de tipo como acima sem um nome para o tipo declarado, posso?)

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

Qualquer solução?

eu encontro uma solução no livro:

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

Trecho de: Basarat Ali Syed. “Mergulho Profundo de TypeScript.” Livros da Apple.

uma propriedade em um objeto e pode ser usada como uma proteção de tipo, e o TypeScript pode saber qual tipo você usou.

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

O problema aqui é que, como B não declara uma propriedade a , pode em tempo de execução ter uma propriedade a de qualquer tipo possível (porque você pode atribuir um objeto com qualquer conjunto de propriedades para um B , desde que tenha uma propriedade b do tipo string). Você pode fazer funcionar explicitamente declarando uma propriedade a: undefined em B (garantindo assim que B não terá alguma propriedade a aleatória):

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

declare const AorB: AorB;

if (AorB.a) {
    // Ok
}

Não funciona para mim 10.07.2020

E eu acho que esse comportamento do TS não é bom porque não ajuda muito um desenvolvedor ...

Esta página foi útil?
5 / 5 - 1 avaliações