Hvordan skrive en ordbok laget fra et objekt i TypeScript?
Ganske ofte bruker jeg objekter som ordbøker f.eks.
type Foo = { a: string }
type MyDictionary = { [key: string]: Foo }
function foo(dict: MyDictionary) {
// Requirement 1: Should be of type Foo[]
const values = Object.values(dict)
// Requirement 2: Should be of type Foo | undefined
const aValue = dict.b
// Requirement 3: Should fail
dict.c = undefined
}
Løsning 1
Mitt standardvalg er
type MyDictionary = Record<string, Foo> // aka { [key: string]: Foo }
Dette vil gå gjennom 1 og 3, men feile på 2.
Løsning 2
{ [key: string]: Foo | undefined }
Dette vil passere 2, men feile på 1 og 3.
Løsning 3
Et annet alternativ er å holde seg til alternativ 1, men bruke en getter-funksjon for å gjøre ting tryggere
export function safeGet<T extends {}, K extends keyof T>(obj: T, key: K): T[K] | undefined {
return obj[key]
}
safeGet(dict, 'b')
Noen andre forslag?
Merk: Jeg vet at Map gjør dette godt, men å bruke Map er ikke alltid en praktisk refaktorering. Dette spørsmålet gjelder spesifikt objekter som ordbøker.
Svar #1
Du ser etter funksjonaliteten som tilbys av --noUncheckedIndexedAccess kompilatoralternativet, som legger til undefined i domenet for typen du får når du leser en egenskap dekket av en indeksignatur, uten også å gjøre det for typen du får lov til å skrive til den samme egenskapen. Og merk at det ikke påvirker returtypen til Object.values(), hvis deklarasjon er
interface ObjectConstructor {
values<T>(o: { [s: string]: T } | ArrayLike<T>): T[];
}
Merk at dette kompileralternativet ikke er en del av --strict-pakken med kompileralternativer, og blir derfor ikke betraktet som et "standard" type-sikkerhetsalternativ. Det er fordi, selv om det noen ganger kan være ønskelig å beskytte mot undefined på denne måten, kan det være ganske irriterende i andre tilfeller, ved å generere feil i veldig vanlig idiomatisk JavaScript.
For eksempel har array-typer i TypeScript en number indekssignatur. Å aktivere --noUncheckedIndexedAccess hindrer det svært vanlige bruksområdet med å iterere gjennom et array ved å løkke gjennom dets numeriske indekser:
const arr = ["a", "b", "c", "d", "e"];
for (let i = 0; i < arr.length; i++) {
console.log(arr[i].toUpperCase()); // error!
// -------> ~~~~~~ Object is possibly undefined
}
Teknisk sett eksisterer sporadiske matriser, så man kunne hevde at denne feilen burde skje... men i praksis er det ofte mer problemer enn det er verdt å la kompilatoren gjøre slike kontroller.
Så hvis du vil ha denne sjekken, må du aktivere den eksplisitt.
Lenke til kode på lekeplassen
Svar #2
API-en du leter etter tilbys av Map.
type Foo = { a: string }
type MyDictionary = Map<string, Foo>();
function foo(dict: MyDictionary) {
// Requirement 1: Should be of type Foo[]
const values = [...dict.values()]
// Requirement 2: Should be of type Foo | undefined
const aValue = dict.get("b")
// Requirement 3: Should fail
dict.set("c", undefined)
}
Lekeplass
