TypeScript - Begränsningar på literaler i arrayinitialiseringar
Här är bas-koden:
export const LETTERS = {
// internal
a: 'a',
// external
b: 'b',
c: 'c',
} as const;
export const internalLetters = [
LETTERS.a,
] as const;
export type LETTER_TYPE = typeof LETTERS[keyof typeof LETTERS];
Anta nu att jag vill ha en hjälpfunktion som accepterar vilken bokstav som helst och avgör om den är intern:
const isInternal = (letter: LETTER_TYPE) => internalLetters.includes(letter);
Och nu visas felet:
Kan du förklara vad som är fel med sådan kod och hur man kan uppnå mitt mål?
Svar #1
Om vi kontrollerar typen av internalLetters får vi:
const internalLetters: readonly ["a"]
Detta betyder att den endast tillåter "a" som dess värde för objekt och inget annat. Den tillåter inte ens en tom array (se bilden nedan).
TypeScript upprätthåller denna begränsning till en sådan nivå att att anropa internalLetters.includes först kontrollerar om det angivna värdet överhuvudtaget kan vara en medlem av internalLetters, dvs. det tillåter inte någon övermängd av de tillåtna värdena; i ditt fall tillåter det inte värdet att vara av typen "a" | "b" | "c" när det enda accepterade värdet är "a".
Det finns ett par sätt att ta itu med detta:
- Använd
internalLetters.findför att utföraisInternal-kontrollen - Sätt
internalLetterstill att vara av explicit typreadonly LETTER_TYPE[], vilket tillåter.includesatt tillåta den generella typenLETTER_TYPE
Du kan hitta båda dessa tillvägagångssätt i denna TS Playground.
Svar #2
När man smalnar av en typ med TypeScripts typ-predikat, berättar man för kompilatorn att man antar att den givna WiderType uppfyller en typ av NarrowerType genom att kasta WiderType till NarrowerType (as NarrowerType). Detta låter dig kontrollera om detta uttalande är sant med hjälp av JavaScript. När det kombineras med nyckelordet is NarrowerType berättar du för kompilatorn att behandla det givna värdet som en NarrowerType när funktionen returnerar true.
Med andra ord, för en mängd T, och en mängd K som är en delmängd av T, kontrollerar vi om en given medlem av T också är medlem av K. Således är en medlem av T en typ av K.
Som du nämnde:
Att ge typen LETTER_TYPE till internalLetters-arrayen är inte en lösning, eftersom den bara ska innehålla bokstaven: LETTERS.a
Detta kan lösas genom att kombinera as const med nyckelordet satisfies.
const LETTERS = {
// internal
a: 'a',
// external
b: 'b',
c: 'c',
} as const;
type LETTER_TYPE = keyof typeof LETTERS
const internalLetters = [
LETTERS.a,
] as const satisfies LETTER_TYPE[];
type INTERNAL_LETTER_TYPE = typeof internalLetters[number]
const isInternal = (letter: LETTER_TYPE): letter is INTERNAL_LETTER_TYPE => {
return internalLetters.includes(letter as INTERNAL_LETTER_TYPE);
}
console.log(isInternal('a')) // true | 'a' is INTERNAL_LETTER_TYPE
console.log(isInternal('b')) // false | 'b' is LETTER_TYPE
console.log(isInternal('c')) // false | 'c' is LETTER_TYPE

