Typescript, grensesnitt, type, flere valgfrie egenskaper, egenskap betinget av en annen egenskap
Jeg har behov for et grensesnitt eller en type som krever egenskapen updatedAt hvis egenskapen updatedBy er til stede (og omvendt); det bør imidlertid tillate at ingen av egenskapene er satt.
interface BaseRow {
id: string
createdAt: string
createdBy: string
}
interface Updated {
updatedAt: string
updatedBy: string
}
interface UpdatedRow extends BaseRow, Updated {}
type Row = BaseRow | UpdatedRow
Basert på det ovenstående, ville jeg forvente at følgende ville få TypeScript-kompilatoren til å kaste en feil fordi updatedAt er til stede, men updatedBy ikke er det.
const x: Row = {
id: 'someID',
createdAt: 'someISO',
createdBy: 'someID',
updatedAt: 'someISO'
}
Ovenstående gir imidlertid ingen feil.
Hvorfor fungerer ikke løsningen ovenfor som forventet? Hva er den beste måten å oppnå en interface eller type som betinget krever to eller flere egenskaper?
Typescript-lekeplass
Svar #1
Ifølge PR-en som implementerer streng sjekking på objektliteraler, er sjekken som utføres:
... det er en feil at objektbokstavelig spesifiserer egenskaper som ikke eksisterer i måltypen
Selv om egenskapene er på forskjellige grener av unionstypen, eksisterer egenskapen på unionstypen. Siden den andre oppdaterte egenskapen ikke eksisterer, overholder ikke objektbokstaven UpdatedRow-grensesnittet, men den overholder Row-grensesnittet, så den kan tildeles variabelen. Dette er grunnen til at det ikke er noen feil.
En løsning, som kanskje eller kanskje ikke er anvendbar i ditt tilfelle, er å sikre at en oppdatert rad er inkompatibel med en ikke oppdatert rad ved å legge til en streng-egenskap som er typet til en forskjellig streng-literaltype på de to grensesnittene:
interface BaseRow {
id: string
createdAt: string
createdBy: string
}
interface Updated {
type: 'u'
updatedAt: string
updatedBy: string
}
interface UpdatedRow extends BaseRow, Updated {}
type Row = (BaseRow & { type: 'n'}) | UpdatedRow
const x: Row = { // Error, no updatedBy
type: 'u',
id: 'someID',
createdAt: 'someISO',
createdBy: 'someID',
updatedAt: 'someISO'
}
const x2: Row = { // Error, with updatedAt is not assignable to (BaseRow & { type: 'n'})
type: 'n',
id: 'someID',
createdAt: 'someISO',
createdBy: 'someID',
updatedAt: 'someISO'
}
