In Typescript, I have an interface B which extends interface A.
interface A {
a: "a",
b: "b"
}
interface B extends A {
c: "c",
d: "d"
}
I want to write a generic that can "strip" A from B, i.e. can give me a type that contains only the parts that are specified in B, and not those that are inherited from A.
type StripType<T2, T1> = ??
type C = StripType<B, A>; // { c: "c", d: "d" }
In this case, it is quite simple, I can write StripType
like this and it will work:
type StripType<T2, T1> = Omit<T2, keyof T1>;
However, when using more complex inheritances, this implementation ceases to work:
type R = Record<string, string>;
interface D extends R {
e: "e"
}
type E = StripType<D, R>; // { [x: number]: string } <--- I want it to be { e: "e" }
How can I rewrite StripType
so that it is more robust? I have tried a lot of things, but none of them have worked so far. Please note that a general solution to this problem would be preferable, but if this isn't possible then a method that can just strip Record<string, string>
will also be helpful, since this is my actual use case.
In Typescript, I have an interface B which extends interface A.
interface A {
a: "a",
b: "b"
}
interface B extends A {
c: "c",
d: "d"
}
I want to write a generic that can "strip" A from B, i.e. can give me a type that contains only the parts that are specified in B, and not those that are inherited from A.
type StripType<T2, T1> = ??
type C = StripType<B, A>; // { c: "c", d: "d" }
In this case, it is quite simple, I can write StripType
like this and it will work:
type StripType<T2, T1> = Omit<T2, keyof T1>;
However, when using more complex inheritances, this implementation ceases to work:
type R = Record<string, string>;
interface D extends R {
e: "e"
}
type E = StripType<D, R>; // { [x: number]: string } <--- I want it to be { e: "e" }
How can I rewrite StripType
so that it is more robust? I have tried a lot of things, but none of them have worked so far. Please note that a general solution to this problem would be preferable, but if this isn't possible then a method that can just strip Record<string, string>
will also be helpful, since this is my actual use case.
1 Answer
Reset to default 1- Extract keys into tuples for both types, so
string
ande
for example wouldn't combine. - Find common keys and extract the difference from
D
- Create a mapped type with the diff keys.
Playground
type Keys<D> = {[K in keyof D as number]: [K]}[number];
type CommonKeys<T, U> = T extends [any] ? U extends [any] ? [T[0], U[0]] extends [U[0], T[0]] ? T : never : never: never;
type DiffKeys<T, U> = T extends [unknown] ? IsExactlyInUnion<T, U> extends never ? T : never: never;
type StripTypes<A, B, K = Keys<A>, C = CommonKeys<Keys<A>, Keys<B>>> =
{[P in DiffKeys<K, C> as P extends [any] ? P[0] : never]: P extends [any] ? A[P[0]] : never};
type IsExact<T, U> = [T] extends [U] ? ([U] extends [T] ? true : false) : false;
type IsExactInUnion<T, U> = U extends any ? IsExact<T, U> extends true ? true : never : never;
type IsExactlyInUnion<T, U> = [IsExactInUnion<T, U>] extends [never] ? never : T;
type R = Record<string, string> & {a: 'a'}
interface D extends R {
e: 'e',
}
type dKeys = Keys<D>;
type rKeys = Keys<R>;
type commonKeys = CommonKeys<Keys<D>, Keys<R>>;
type dDiff = DiffKeys<Keys<D>, CommonKeys<Keys<D>, Keys<R>>>
type E = StripTypes<D, R>; // type E = {e: "e";}
type R2 = {a: 'a'}
interface D2 extends R2 {
e: 'e',
[x: string]: any
}
/*
type E2 = {
[x: string]: any;
e: "e";
}
*/
type E2 = StripTypes<D2, R2>;
// a possible edge case when values are the same
type R3 = {[x: string]: 'e'}
interface D3 extends R3 {
e: "e",
}
type E3 = StripTypes<D3, R3>; // type E = {e: "e";}
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745101186a4611282.html
type R2 = {[x:string]: 'e'}
, since comparing values doesn't make sense, check my solution for this case – Alexander Nenashev Commented Mar 3 at 17:19