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'
}

Relaterte artikler