Typescript - Ovänat fel vid användning av includes med en typad array
Med följande kod producerar vi två arrayer:
const PROVIDERS: {
PROVIDER_1: 'PROVIDER_1';
PROVIDER_2: 'PROVIDER_2';
PROVIDER_3: 'PROVIDER_3';
} = {
PROVIDER_1: 'PROVIDER_1',
PROVIDER_2: 'PROVIDER_2',
PROVIDER_3: 'PROVIDER_3',
};
const GOOD_PROVIDERS = [PROVIDERS.PROVIDER_1, PROVIDERS.PROVIDER_2];
const BAD_PROVIDERS = [PROVIDERS.PROVIDER_3];
Typerna är:
const GOOD_PROVIDERS: ("PROVIDER_1" | "PROVIDER_2")[]
const BAD_PROVIDERS: ("PROVIDER_3")[]
Sedan, om jag försöker kontrollera om ett värde ingår i en av dessa arrayer stöter jag på flera kompileringsfel som jag inte vet hur jag ska hantera:
const provider: 'PROVIDER_1' | 'PROVIDER_2' | 'PROVIDER_3' = getProvider();
const isGood = GOOD_PROVIDERS.includes(provider);
const isBad = BAD_PROVIDERS.includes(provider);
Inkluderingarna av GOOD_PROVIDERS ger detta fel
Argument av typen '"PROVIDER_1" | "PROVIDER_2" | "PROVIDER_3"' kan inte tilldelas till parametern av typen '"PROVIDER_1" | "PROVIDER_2"'. Typen '"PROVIDER_3"' kan inte tilldelas till typen '"PROVIDER_1" | "PROVIDER_2"'.ts(2345)
Inkluderingarna av BAD_PROVIDERS orsakar detta fel
Argument av typen '"PROVIDER_1" | "PROVIDER_2" | "PROVIDER_3"' kan inte tilldelas parametern av typen '"PROVIDER_3"'. Typen '"PROVIDER_1"' kan inte tilldelas typen '"PROVIDER_3"'.ts(2345)
Varför antar Array.includes att argumentet redan finns i listan? Finns det något annat sätt att kontrollera detta?
Svar #1
Jag ändrade för some()-metoden:
array.some((arr) => arr === otherParameter)
I ditt fall skulle vara:
const isGood = GOOD_PROVIDERS.some((GOOD_PROVIDER) => GOOD_PROVIDER === provider);
Svar #2
Varför antar Array.includes att argumentet redan finns i listan?
Länken i din kommentar svarar redan på det på ett sätt. TL;DR är att lägga till typer till en array är likt att skapa en abstrakt uppsättning värden, och att gå utanför denna uppsättning väljs att vara ett kompilatorfel. Vissa kan tycka om det, andra kan hata det, men det är som det är (åtminstone för nu).
Finns det något annat sätt att kontrollera detta?
Ja.
Från och med TypeScript 1.6 kan du använda (x) => x är Typ typvakter. Jag tycker att denna funktion (tillsammans med (x) => bekräftar x är Typ assertionsfunktioner) är väldigt bekväm att arbeta med, extremt användbar och ärligt talat lite som magi:
I följande exempel kommer jag att anta att du inte har kontroll över definitionen av PROVIDERS-objektet. Om du har det, gå till nästa exempel.
const PROVIDERS: {
PROVIDER_1: 'PROVIDER_1';
PROVIDER_2: 'PROVIDER_2';
PROVIDER_3: 'PROVIDER_3';
} = {
PROVIDER_1: 'PROVIDER_1',
PROVIDER_2: 'PROVIDER_2',
PROVIDER_3: 'PROVIDER_3',
};
type GoodProvider = typeof PROVIDERS.PROVIDER_1 | typeof PROVIDERS.PROVIDER_2;
type BadProvider = typeof PROVIDERS.PROVIDER_3;
type Provider = GoodProvider | BadProvider;
// Notice that types here are broadened, - otherwie this won't work
// The distinction between good vs bad providers has been moved to typings instead
const GOOD_PROVIDERS: Provider[] = [PROVIDERS.PROVIDER_1, PROVIDERS.PROVIDER_2];
const BAD_PROVIDERS: Provider[] = [PROVIDERS.PROVIDER_3];
function isGoodProvider(provider: Provider): provider is GoodProvider {
return GOOD_PROVIDERS.includes(provider);
}
// You might not need both functions though; but if you do, here's the second one
function isBadProvider(provider: Provider): provider is BadProvider {
return BAD_PROVIDERS.includes(provider);
}
Prova det.
Om du har kontroll över PROVIDERS-objektet har du tur, eftersom du kan göra några stora optimeringar - både textmässiga (mindre kod att skriva) och logiska (ingen logikduplication).
Genom att använda as const kan du definiera källan-till-sanning-objekt och härleda alla nödvändiga typer från dem - detta är tillvägagångssättet jag använder överallt, och det är fantastiskt i sin robusthet:
const GOOD_PROVIDERS = {
PROVIDER_1: 'PROVIDER_1',
PROVIDER_2: 'PROVIDER_2',
} as const;
const BAD_PROVIDERS = {
PROVIDER_3: 'PROVIDER_3',
} as const;
type GoodProvider = keyof typeof GOOD_PROVIDERS;
type BadProvider = keyof typeof BAD_PROVIDERS;
type Provider = GoodProvider | BadProvider;
function isGoodProvider(provider: Provider): provider is GoodProvider {
return provider in GOOD_PROVIDERS;
}
function isBadProvider(provider: Provider): provider is BadProvider {
return provider in BAD_PROVIDERS;
}
Prova det.
