typescript - Hur slår man ihop två delvisa objekt till ett komplett objekt?


Jag har ett gränssnitt som kräver alla egenskaper. Att skapa objektet är dock en tvåstegsprocess. Därför planerar jag att skapa 2 Partial-versioner av objektet och sedan slå ihop dem för att uppfylla gränssnittet som inte är delvis. Exempel:

interface IComplete {
    a: string,
    b: string
}

const part1: Partial<IComplete> = {
    a: 'hello'
}

const part2: Partial<IComplete> = {
    b: 'world'
}

const together: IComplete = {
    ...part1,
    ...part2
}

Även om together alltid är komplett, klagar kompilatorn:

Type '{ a?: string; b?: string; }' is not assignable to type 'IComplete'.
   Property 'a' is optional in type '{ a?: string; b?: string; }' but required in type 'IComplete'.

Lekplats

Finns det ett rekommenderat sätt att uppnå detta? Det är viktigt för mitt fall att gränssnittet IComplete inte görs delvis, det vill säga att egenskaperna a och b aldrig är null eller undefined.

Svar #1

@RyanCavanaughs svar är bra. Ett sätt att låta typinferring göra sitt arbete samtidigt som man fortfarande får fördelen av att kompilatorn säkerställer att part1 och part2 är Partial<IComplete> är att använda en hjälpfunktion som nedan:

interface IComplete {
  a: string,
  b: string,
  c: (x: string) => number,
  d: (x: number) => string
}

// helper function
const asPartialIComplete = <T extends Partial<IComplete>>(t: T) => t;

const part1 = asPartialIComplete({
  a: 'hello',
  c: (x) => x.length
})

const part2 = asPartialIComplete({
  b: 'world',
  d: (x) => x + ""
});

const together: IComplete = {
  ...part1,
  ...part2
}

I ovanstående är både part1 och part2 begränsade av asPartialIComplete till att vara giltiga Partial<IComplete>-objekt (så parametrarna för c- och d-metoderna härleds som string respektive number). Men typerna för part1 och part2 är tillräckligt smala för att kompilatorn ska inse att typeof part1 & typeof part2 är IComplete.

Hoppas att det också hjälper. Lycka till!

Svar #2

Det enklaste sättet skulle vara att ta bort typannoteringarna och låta typutledning göra sitt arbete:

interface IComplete {
    a: string,
    b: string
}

const part1 = {
    a: 'hello'
}

const part2 = {
    b: 'world'
}

const together: IComplete = {
    ...part1,
    ...part2
}

Detta har vissa skadliga effekter, till exempel kommer parametrar i funktionsuttryck i part1 eller part2 inte att få härledda typer.

Istället kan du använda Pick - tyvärr måste du skriva ut nycklarna som du väljer från varje typ när du använder detta tillvägagångssätt:

interface IComplete {
    a: string,
    b: string,
    c: number
}

const part1: Pick<IComplete, "a"> = {
    a: 'hello'
}

const part2: Pick<IComplete, "b" | "c"> = {
    b: 'world',
    c: 43
}

const together: IComplete = {
    ...part1,
    ...part2
}

Relaterade artiklar