网站搜索

typescript - 有没有办法撤销接口继承,即从派生接口中剥离基接口?


在 Typescript 中,我有一个接口 B,它继承自接口 A。

interface A {
   a: "a",
   b: "b"
}

interface B extends A {
   c: "c",
   d: "d"
}

我想写一个泛型,它可以“剥离”A中的B,即可以给我一个只包含B中指定部分的类型,而不包含从A继承的部分。

type StripType<T2, T1> = ??
type C = StripType<B, A>;   // { c: "c", d: "d" }

在这种情况下,非常简单,我可以这样写 StripType,它就能工作:

type StripType<T2, T1> = Omit<T2, keyof T1>;

然而,当使用更复杂的继承时,这种实现将不再起作用:

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

我如何重写 StripType 以使其更健壮?我已经尝试了很多方法,但到目前为止都没有成功。请注意,最好能有一个通用的解决方案,但如果不可能,那么一个只能够去除 Record<string, string> 的方法也会有用,因为这是我实际的使用场景。

答案#1

  1. 将键提取为两种类型的元组,例如 string 和 e 不会组合在一起。
  2. 查找公共键并从D中提取差异
  3. 创建一个具有不同键的映射类型。

操场

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