TypeScript中如何移除派生接口的基接口成员?
实现TypeScript泛型
StripType剥离继承自父类型的成员 在TypeScript中,当定义继承自其他接口的类型时,有时需要剥离掉继承自父类型的成员,只保留子类型自身显式定义的部分。比如:
interface A { a: "a", b: "b" } interface B extends A { c: "c", d: "d" }
期望实现一个泛型StripType<T2, T1>,使得StripType<B, A>返回{ c: "c", d: "d" }。
简单场景下,使用type StripType<T2, T1> = Omit<T2, keyof T1>可以满足需求,但面对包含索引签名的父类型(比如Record<string, string>)时会失效:
type R = Record<string, string>; interface D extends R { e: "e" } // 用Omit得到的是 { [x: number]: string },不符合期望的 { e: "e" } type E = Omit<D, keyof R>;
通用解决方案
利用TypeScript条件类型判断属性是否为子类型自身显式定义的成员。核心逻辑是:遍历子类型的所有键,仅保留那些父类型对应键的类型无法赋值给子类型对应键类型的属性(这类属性要么是子类型新增的,要么是重写了父类型的属性)。
type StripType<T2, T1> = { [K in keyof T2 as T1[K] extends T2[K] ? never : K]: T2[K] };
测试验证
- 基础继承场景:
interface A { a: "a", b: "b" } interface B extends A { c: "c", d: "d" } type C = StripType<B, A>; // { c: "c", d: "d" } 符合预期
- 继承Record场景:
type R = Record<string, string>; interface D extends R { e: "e" } type E = StripType<D, R>; // { e: "e" } 符合预期
- 子类型重写父类型属性的场景:
interface B extends A { a: "new a", c: "c" } type C = StripType<B, A>; // { a: "new a", c: "c" } 符合预期(保留重写的属性)
仅针对Record<string, string>的简化方案
如果只需要处理继承Record<string, string>的场景,可以直接判断属性键对应的类型是否为更具体的字面量类型(而非通用的string):
type StripRecord<T extends Record<string, string>> = { [K in keyof T as string extends T[K] ? never : K]: T[K] };
测试:
type E = StripRecord<D>; // { e: "e" } 符合预期
内容的提问来源于stack exchange,提问作者Liam Goodacre
相关产品推荐
相关产品推荐

