typescript - Uniontyp med valfria egenskaper


Jag vill få en union av flera typer med valfria egenskaper istället för att ta bort egenskaper. Jag skrev följande kod:

type A = { a: string, s: number };
type B = { b: string, s: number };
type C = { c: string, s: number };

type T_OR = A | B | C; // s
type T_AND = A & B & C; // a, b, c, s
type Result = Partial<T_AND> & T_OR; // a, b, c, s

Jag förväntar mig att Result-typen kommer att innehålla // a?, b?, c?, s-fält, men Typescript använder distributiva villkorstyper och omvandlar Result till (Partial<T_AND> & A) | (Partial<T_AND> & B) | (Partial<T_AND> & C).

Hur kan jag förhindra detta beteende och få förväntat resultat?

Svar #1

Ditt problem beror inte på fördelning av konditionella typer, utan snarare på fördelning av union och snitt typer. För TypeScript är (A | B) & C lika med (A | C) & (B | C)

Resultatet är inte vad du förväntar dig också eftersom om du har en valfri egenskap korsad med en icke-valfri kommer resultatet att bli en obligatorisk egenskap.

Du kan få det resultat du vill genom att använda Pick för att välja lämpliga nycklar från T_OR, nämligen endast de som är offentligt synliga i unionen:

type A = { a: string, s: number };
type B = { b: string, s: number };
type C = { c: string, s: number };

type T_OR = A | B | C; // s
type T_AND = A & B & C; // a, b, c, s
type Result = Partial<T_AND> & Pick<T_OR, keyof T_OR>; // a?, b?, c?, s

Svar #2

Alternativt sätt:

type A = { a: string, s: number };
type B = { b: string, s: number };
type C = { c: string, s: number };

type D = { s : number }; // <-- Declare an new type `D`

type T_OR = A | B | C; 

type Result = Partial<T_OR> & D; // a?, b?, c?, s

Faktum är att när man använder Partial, skulle det ge samma resultat att ändra union-typ till intersection-typ:

type T_AND = A & B & C; 

type Result = Partial<T_AND> & D; // a?, b?, c?, s

Åsido:

Det är viktigt att förstå att Union (|) och Intersection (&) operatorer i TypeScript gäller för värdena, inte för egenskaperna.

Tänk på typer som mängder av värden.

Eventuella värden som överensstämmer med Union-typen, T_OR, kan antingen ha en uppsättning värden för typen A eller B eller C eller en kombination av A eller B eller C:

const first: T_OR = {
  s: 1,
  a: 'first'
}; // Ok


const second: T_OR = {
 s: 2,
 a: 'second'
}; // Ok


const third: T_OR = {
 s: 3,
 c: 'third'
} // Ok

const fourth: T_OR = {
 s: 1,
 a: 'first',
 b: 'second'
}; // Ok

const fifth: T_OR = {
 s: 4,
 b: 'second',
 c: 'third'
};

const seventh: T_OR = {
 s: 4,
 a: 'first',
 c: 'third'
};

const eighth: T_OR = {
 s: 4,
 a: 'second',
 c: 'third',
 b: 'second'
};

Medan, för en snitt av typen läggs mer begränsning till värdet. Endast följande gäller:

const val: T_AND = {
  s: 99,
  a: 'second',
  c: 'third',
  b: 'second' 
};