typescript - Is there a way to undo interface extends, i.e. to strip the base interface from the derived interface? - Stack Over

In Typescript, I have an interface B which extends interface A.interface A {a: "a",b: "

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.

Share Improve this question edited Mar 3 at 13:33 RenaudC5 3,8391 gold badge13 silver badges30 bronze badges asked Mar 3 at 9:36 Liam GoodacreLiam Goodacre 4533 silver badges13 bronze badges 3
  • Does this approch meet your needs? Please test thoroughly against use cases you care about. If it satisfies them then I'll write an answer explaining. If not, please edit to add the failing use cases to the question so we can write something that works for them. Let me know, thanks. – jcalz Commented Mar 3 at 14:04
  • @jcalz yeah it works! I haven't tested it beyond the given use case of Record<string>, but that's all I care about right now. Thanks for helping! – Liam Goodacre Commented Mar 3 at 17:12
  • @LiamGoodacre it doesnt work for 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
Add a comment  | 

1 Answer 1

Reset to default 1
  1. Extract keys into tuples for both types, so string and e for example wouldn't combine.
  2. Find common keys and extract the difference from D
  3. 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

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信