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