node.js - Typescript, Enum med strängar och nummer
Jag har ett gränssnitt med
interface mathTest {
mathAction: MathActionEnum;
}
Anledningen till detta är att jag vill att denna egenskap bara ska ha ett av de specifika värdena från enumen nedan.
enum MathActionEnum {
'byOne' = 1,
'byTwo' = 2,
'byFour' = 4,
'byEight' = 8,
}
Anta mathAction='byOne' -> mottaget från ett API-svar.
Första scenariot: när jag utför en aritmetisk operation behöver jag siffervärdet: let result: number=amount/MathActionEnum[mathAction] men jag får ett fel:
Högra sidan av en aritmetisk operation måste vara av typen 'any', 'number', 'bigint' eller en enum-typ
Det är ett nummer men jag behöver ändå casta det med Number(MathActionEnum[mathAction]) för att felet ska försvinna.
Andra scenariot: likhetskontroll, jag behöver strängvärdet: if (mathAction === MathActionEnum[MathActionEnum.byOne]) men jag får ett fel:
Detta villkor kommer alltid att returnera 'false' eftersom typerna 'MathActionEnum' och 'string' inte har någon överlappning
Vilket är vettigt.
Jag är lite vilse, finns det ett sätt att skriva syntaxen som jag förväntar mig att den ska vara? Kanske behöver jag definiera saker på ett annat sätt? Tack
Svar #1
TypeScript-enumerationer är absolut INTE lämpliga för någon form av nyckel-värde-kartläggning. Avsikten är att ha en gruppering av unikt identifierbara etiketter, men etiketter är där det slutar. Även om de faktiskt kan ha en nummervärdesrepresentation under huven, är de inte avsedda att användas som en nyckel-värde-lagring. Du måste göra en typomvandling för att »extrahera numret«, och då blir typen bara number, så du undergräver effektivt syftet med enumerationer.
I praktiken, tänk på dem som nycklar utan användbara värden:
const MathActionEnum = Object.freeze({
byOne: Symbol(),
byTwo: Symbol(),
byFour: Symbol(),
byEight: Symbol(),
})
Överväg det nyare alternativet, const-assertion, istället. De kommer att ge dig typesäkerhet både för nycklar och värden:
const MathActions = {
'byOne': 1,
'byTwo': 2,
'byFour': 4,
'byEight': 8,
} as const
type MathAction = keyof typeof MathActions
type MathActionValue = typeof MathActions[MathAction]
Du får full typkontroll på både nycklar och värden:
const example = (action: MathAction) => {
return 2 * MathActions[action]
}
example('byOne')
// compile error, not a valid key
example('foo')
// -------------
const example2 = (actionValue: MathActionValue) => {
return 2 * actionValue
}
example2(4)
// compile error, not a valid value
example2(19)
Du kan till och med lägga till typkontroller för att kontrollera om godtyckliga värden är en nyckel eller ett värde:
const isAction = (action: string): action is MathAction => {
return Object.keys(MathActions).includes(action)
}
isAction
const isActionValue = (actionValue: number): actionValue is MathActionValue => {
return Object.values(MathActions).includes(actionValue as any)
}
Du kommer till och med att få IDE-autokomplettering för både nycklar och värden:
Här är en lekplats
Svar #2
Typescript exponerar inte rätt enum-typ (löste detta med en lösning) Det filtrerar helt enkelt bort icke-numeriska värden.
Object.values(yourTsEnum).filter((item : any) => /^([0-9]*)$/i.test(String(item))).map(item => Number(item))
