最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

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

programmeradmin2浏览0评论

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

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论