Typescript: El acceso a la propiedad en la unión de tipos de objetos falla para las propiedades no definidas en todos los miembros de la unión

Creado en 10 dic. 2016  ·  12Comentarios  ·  Fuente: microsoft/TypeScript

Versión de 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
}

Comportamiento esperado:
El código se compila sin errores. Aunque a no existe (necesariamente) en todos los constituyentes del sindicato, el hecho de que exista en algunos debería permitirme verificar .a y usarlo si está presente.

Comportamiento real:
Mecanografiado se queja: "La propiedad a no existe en el tipo AorB ... La propiedad a no existe en el tipo B."

Question

Comentario más útil

Encuentro una solución en el libro:

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

Extracto de: Basarat Ali Syed. "Análisis profundo de TypeScript". Libros de Apple.

una propiedad en un objeto y se puede usar como un tipo de protección, y TypeScript puede saber qué tipo usó.

Todos 12 comentarios

El doctor dice:

Para que funcione el mismo código, necesitaremos usar una aserción 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

Luego, en tu muestra:

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

El problema aquí es que debido a que B no declara una propiedad a , en tiempo de ejecución podría tener una propiedad a de cualquier tipo posible (porque puede asignar una objeto con cualquier conjunto de propiedades a B siempre que tenga una propiedad b de tipo cadena). Puede hacer que funcione declarando explícitamente una propiedad a: undefined en B (asegurando así que B no tendrá una propiedad a aleatoria):

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

declare const AorB: AorB;

if (AorB.a) {
    // Ok
}

Tiene mucho sentido. Pedo cerebral.

Si define a: undefined en el tipo B, tendrá que establecerlo en undefined cuando cree / pase una variable.
Para evitar eso, puede declarar a como a?: undefined , y el mecanografiado estará feliz si lo omite.

Si entiendo los comentarios correctamente, esto debería funcionar (pero arroja; probado en el patio de recreo): ¿Es esto un error o no? 🤔

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

No entiendo esto en absoluto. ¿No es if ((<A>AorB).a) lo mismo que usar if (A.a) porque lo estás forzando a volver a escribir A ?

Si define a: undefined en el tipo B, tendrá que establecerlo en undefined cuando cree / pase una variable.
Para evitar eso, puede declarar a como a?: undefined , y el mecanografiado estará feliz si lo omite.

podría ser mejor a?: never , ahora no puede asignar undefined por error

¿Qué pasa si no le da un nombre a cada uno de los miembros del sindicato? (no puedo hacer una afirmación de tipo como se indicó anteriormente sin un nombre para el tipo afirmado, ¿verdad?)

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

¿Alguna solución?

Encuentro una solución en el libro:

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

Extracto de: Basarat Ali Syed. "Análisis profundo de TypeScript". Libros de Apple.

una propiedad en un objeto y se puede usar como un tipo de protección, y TypeScript puede saber qué tipo usó.

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

El problema aquí es que debido a que B no declara una propiedad a , en tiempo de ejecución podría tener una propiedad a de cualquier tipo posible (porque puede asignar una objeto con cualquier conjunto de propiedades a B siempre que tenga una propiedad b de tipo cadena). Puede hacer que funcione declarando explícitamente una propiedad a: undefined en B (asegurando así que B no tendrá una propiedad a aleatoria):

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

declare const AorB: AorB;

if (AorB.a) {
    // Ok
}

No me funciona 07.10.2020

Y creo que este comportamiento de TS no es bueno porque no ayuda mucho a un desarrollador ...

¿Fue útil esta página
5 / 5 - 1 calificaciones