Typescript: Kollisioner av egenskapsnamn i typer vs gränssnitt
Säg att jag har 2 typer med samma egenskapsnamn men olika datatyper:
type A = {
myProperty:string
}
type B = {
myProperty:number
}
Om jag utökar båda med ett interface, ser jag ett fel som förväntat:
interface C extends A, B {}
Interface 'C' cannot simultaneously extend types 'A' and 'B'.
Named property 'myProperty' of types 'A' and 'B' are not identical.
Men om jag använder en typ för att utöka båda, finns det inget undantag. I det här fallet, vad blir den resulterande datatypen för myProperty? Finns det ett sätt att kontrollera den resulterande datatypen?
type C = A & B; // no error
Jag kan inte sätta ett värde för myProperty (sträng eller nummer) om C är deklarerad som en typ. Vad är det bästa sättet att hantera detta scenario?
function F(myParameter:C) {
console.log(myParameter);
}
F({myProperty: 90});
(property) myProperty: never
Type 'number' is not assignable to type 'never'.ts(2322)
index.ts(3, 3): The expected type comes from property 'myProperty' which is declared here on type 'C'
Samma fel om jag anropar funktionen som F({myProperty: 'test data'});.
Lekplats: https://codesandbox.io/s/typescript-playground-export-forked-dglbq5?file=/index.ts
Svar #1
Om du vill kombinera två motstridiga typer men ändå vill instansiera den resulterande typen, finns det flera alternativ för att hantera det. Observera att båda lösningarna inte kommer att lösa konflikter för egenskaper som är inbäddade djupare än första nivån.
Säg att vi har två gränssnitt A och B som vi vill korsas.
interface A {
a: string
b: string
}
interface B {
a: number
c: string
}
Det första alternativet kan vara att utelämna den konfliktande egenskapen från den resulterande typen.
type IntersectByOmitting<A, B> = {
[K in keyof (A & B) as K extends keyof A & keyof B
? [A[K] & B[K]] extends [never]
? never
: K
: K
]: K extends keyof A ? A[K] : B[K & keyof B]
}
type T0 = IntersectByOmitting<A, B>
// ^? type T0 = { b: string; c: string; }
Eftersom a löses till never i snittet, kommer det att utelämnas från den resulterande typen.
Som du sa i kommentaren kanske du istället vill ha en förening av de motstridiga egenskaperna.
type IntersectByUnion<A, B> = {
[K in keyof (A & B)]: K extends (keyof A & keyof B) ?
[A[K] & B[K]] extends [never]
? A[K] | B[K]
: A[K] & B[K]
: K extends keyof A ? A[K] : B[K & keyof B]
}
type T1 = IntersectByUnion<A, B>
// type T1 = {
// a: string | number;
// b: string;
// c: string;
// }
Typen av a är nu string | number.
Lekplats
