javascript - Hur man begränsar objekttyper baserat på ett värde i objektet?


För tillfället använder jag denna typalias:

export type Country = {
  name: 'USA' | 'Russia' | 'Bulgaria' | 'Romania' | 'Austria' | 'Great Britain' | 'Italy',
  code: 'us' | 'ru' | 'bg' | 'ro' | 'au' | 'gb' | 'it'
};

Men det tillåter att skapa felaktiga objekt:

{ name: 'USA', code: 'ru'}

Vad är det bästa sättet att skapa en typ för detta ändamål?

Svar #1

Du skulle kunna använda en diskriminerad union-approach:

type Country = {
    name: 'USA',
    code: 'us',
} | {
    name: 'Russia',
    code: 'ru',
} | {
    name: 'Bulgaria',
    code: 'bg',
} | {
    name: 'Romania',
    code: 'ro',
} | {
    name: 'Austria',
    code: 'au',
} | {
    name: 'Great Britain',
    code: 'gb',   
} | {
    name: 'Italy',
    code: 'it',   
};

// works
const myCountry: Country = {
    name: 'USA',
    code: 'us',
};

// does not
const myCountry2: Country = {
    name: 'USA',
    code: 'ru',
};

Mer läsning här

Du kan också använda en parameterkarta, men då skulle du behöva specificera parametern på gränssnittet:

type CountryMap = {
    USA: 'us',
    Russia: 'ru',
    Bulgaria: 'bg',
    Romania: 'ro',
    Austria: 'au',
    'Great Britain': 'gb',
    Italy: 'it',
};

interface ICountry<T extends keyof CountryMap> {
    name: T;
    code: CountryMap[T];
}

// works
const myICountry: ICountry<'USA'> = {
    name: 'USA',
    code: 'us'
};

// does not
const myICountry2: ICountry<'USA'> = {
    name: 'USA',
    code: 'ru',
};

Fungerande TypeScript-lekplats för båda exemplen här

Svar #2

Det är inte bra att konstruera sådana typer manuellt. Tillhandahåll en enda typ som definierar landskoder (det har du redan) och komplettera den med en exporterad funktion av typen CountryCode -> CountryName. Du kanske vill kräva några andra parametrar som definierar namngivningsstrategi (lokalisering, korta/långa namn, etc.).

Relaterade artiklar