已知接口可通过条件类型条件包含方法,类如何实现?
在类中实现条件性方法(基于条件类型)
当然可以在类中实现类似接口里的条件性方法逻辑,但TypeScript对类成员的直接条件性定义有语法限制,不能像接口那样直接给单个成员用条件类型。不过我们可以通过几种方式达成类似效果:
1. 让类实现带有条件类型的接口
你可以直接让类实现你定义的Iter<T>接口,TypeScript会根据泛型T的类型自动校验方法是否需要存在:
interface Iter<T> { flatten: T extends Iterable<infer A> ? () => Set<A> : never; } class MyIter<T> implements Iter<T> { constructor(private value: T) {} flatten!: T extends Iterable<infer A> ? () => Set<A> : never; // 根据T的类型实现对应逻辑 // 比如当T是可迭代类型时: // flatten() { // const set = new Set<A>(); // for (const item of this.value) { // set.add(item); // } // return set; // } } // 用法示例 const iterableIter = new MyIter([1,2,3]); iterableIter.flatten(); // 正常调用,返回Set<number> const nonIterableIter = new MyIter(123); nonIterableIter.flatten; // 类型为never,调用会报错
这里用!是非空断言,因为TypeScript无法在类定义阶段确定flatten的具体类型,需要我们在实际实例化时根据T的类型来实现对应的方法(或者在类里写条件分支的逻辑)。
2. 利用泛型条件分支实现方法逻辑
另一种方式是在类的方法里通过类型守卫和泛型条件来处理不同情况,确保只有当T是可迭代类型时,方法才有实际可用的逻辑:
class MyIter<T> { constructor(private value: T) {} flatten(): T extends Iterable<infer A> ? Set<A> : never { if (typeof this.value[Symbol.iterator] === 'function') { const set = new Set(); for (const item of this.value as Iterable<unknown>) { set.add(item); } return set as T extends Iterable<infer A> ? Set<A> : never; } throw new Error('Value is not iterable'); } } const arrIter = new MyIter([1,2,3]); arrIter.flatten(); // 返回Set<number> const numIter = new MyIter(456); numIter.flatten(); // 运行时抛出错误,类型上也会提示返回never
这种方式下,方法始终存在,但类型上会根据T的类型返回不同结果,运行时也会做校验,确保只有可迭代类型能正常执行。
3. 通过静态工厂方法返回不同类型的实例
如果需要更严格的“方法存在与否”的区分,可以用静态工厂方法根据输入类型返回不同的类实例,其中部分类包含flatten方法,部分不包含:
interface IterableIter<A> { flatten(): Set<A>; } interface NonIterableIter { // 不含flatten方法 } class BaseIter<T> { constructor(protected value: T) {} } class ConcreteIterableIter<A> extends BaseIter<Iterable<A>> implements IterableIter<A> { flatten(): Set<A> { const set = new Set<A>(); for (const item of this.value) { set.add(item); } return set; } } class ConcreteNonIterableIter<T> extends BaseIter<T> implements NonIterableIter {} class MyIterFactory { static create<T>(value: T): T extends Iterable<infer A> ? IterableIter<A> : NonIterableIter { if (typeof value[Symbol.iterator] === 'function') { return new ConcreteIterableIter(value) as any; } return new ConcreteNonIterableIter(value) as any; } } // 用法 const iter1 = MyIterFactory.create([1,2,3]); iter1.flatten(); // 正常调用 const iter2 = MyIterFactory.create(789); iter2.flatten; // 类型报错,因为该实例没有这个方法
这种方式能在类型层面严格区分是否存在flatten方法,比前两种方式更严谨,但需要定义多个类来拆分逻辑。
需要注意的是,TypeScript不允许在类的成员定义中直接使用条件类型来决定成员是否存在(比如flatten?: T extends ...这种写法是不合法的),所以必须通过上述间接方式来实现类似接口的条件性成员效果。
内容的提问来源于stack exchange,提问作者dipea
相关产品推荐
相关产品推荐

