Typescript: L'accès à la propriété dans l'union de types d'objet échoue pour les propriétés non définies sur tous les membres de l'union

Créé le 10 déc. 2016  ·  12Commentaires  ·  Source: microsoft/TypeScript

Version de TypeScript: 2.1.1

Code

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

declare const AorB: AorB;

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

Comportement prévisible:
Le code se compile sans erreur. Même si a n'existe pas (nécessairement) sur tous les constituants du syndicat, le fait qu'il existe sur certains devrait me permettre de vérifier .a et de l'utiliser s'il est présent.

Comportement réel:
Typecript se plaint: "La propriété a n'existe pas sur le type AorB ... La propriété a n'existe pas sur le type B."

Question

Commentaire le plus utile

je trouve une solution sur le livre:

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

Extrait de: Basarat Ali Syed. «TypeScript Deep Dive.» Livres Apple.

une propriété sur un objet et peut être utilisé comme garde de type, et le TypeScript peut savoir quel type vous avez utilisé.

Tous les 12 commentaires

Le doc dit:

Pour que le même code fonctionne, nous devons utiliser une assertion de type:

let pet = getSmallPet();

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

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

Puis dans votre échantillon:

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

Le problème ici est que, comme B ne déclare pas a propriété a de n'importe quel type possible (car vous pouvez attribuer un objet avec n'importe quel ensemble de propriétés à un B tant qu'il a une propriété b de type chaîne). Vous pouvez le faire fonctionner explicitement en déclarant une propriété a: undefined dans B (garantissant ainsi que B n'aura pas a propriété

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

declare const AorB: AorB;

if (AorB.a) {
    // Ok
}

Cela a du sens. PET de cerveau.

Si vous définissez a: undefined sur le type B, vous devrez le définir sur undefined lorsque vous créez / passez une variable.
Pour contourner cela, vous pouvez déclarer a comme a?: undefined , et dactylographié sera heureux si vous l'omettez.

Si je comprends bien les commentaires, cela devrait fonctionner (mais jette; testé sur le terrain de jeu): Est-ce un bug ou pas? 🤔

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

Je ne comprends pas du tout cela. if ((<A>AorB).a) n'est-il pas identique à l'utilisation de if (A.a) parce que vous le forcez à taper A ?

Si vous définissez a: undefined sur le type B, vous devrez le définir sur undefined lorsque vous créez / passez une variable.
Pour contourner cela, vous pouvez déclarer a comme a?: undefined , et dactylographié sera heureux si vous l'omettez.

pourrait être mieux a?: never , maintenant vous ne pouvez pas attribuer undefined par erreur

Et si vous ne donnez pas de nom à chacun des membres du syndicat? (impossible de faire une assertion de type comme ci-dessus sans nom pour le type affirmé, puis-je?)

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

Toute solution?

je trouve une solution sur le livre:

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

Extrait de: Basarat Ali Syed. «TypeScript Deep Dive.» Livres Apple.

une propriété sur un objet et peut être utilisé comme garde de type, et le TypeScript peut savoir quel type vous avez utilisé.

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

Le problème ici est que, comme B ne déclare pas a propriété a de n'importe quel type possible (car vous pouvez attribuer un objet avec n'importe quel ensemble de propriétés à un B tant qu'il a une propriété b de type chaîne). Vous pouvez le faire fonctionner explicitement en déclarant une propriété a: undefined dans B (garantissant ainsi que B n'aura pas a propriété

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

declare const AorB: AorB;

if (AorB.a) {
    // Ok
}

Cela ne fonctionne pas pour moi 07.10.2020

Et je pense que ce comportement de TS n'est pas bon car il n'aide pas beaucoup un développeur ...

Cette page vous a été utile?
5 / 5 - 1 notes