如何为TypeScript函数参数指定排他类型
如何为TypeScript函数参数指定排他类型
嘿,这个需求我之前也碰到过!其实核心就是利用TypeScript的条件类型,把你不想接受的类型“排除”掉,让编译器在静态检查阶段就直接报错。
先给你理清楚思路:首先得明确什么是“已经被包装”的类型,假设我们的包装类型是这样的(不管是接口、类型别名还是类都可以):
// 先定义包装类型,这里用类型别名举例 type Wrapped<T> = { wrappedValue: T; };
接下来要写的wrap函数,需要满足:只要传入的是Wrapped类型的实例,就直接触发编译错误;其他类型都正常接受并返回包装后的结果。
我们可以用泛型+条件类型来实现这个逻辑:
function wrap<T>(value: T extends Wrapped<any> ? never : T): Wrapped<T> { return { wrappedValue: value }; }
来测试一下效果:
- 正常调用(传入未包装的值):完全没问题,返回的类型也符合预期
const wrappedString = wrap("我是未包装的字符串"); // wrappedString 的类型是 Wrapped<string>,完美
- 错误调用(传入已经包装过的值):编译器直接红报错, exactly what we want!
const alreadyWrapped = wrap("测试"); const badCall = wrap(alreadyWrapped); // 这里会提示:类型“Wrapped<string>”的参数不能赋给类型“never”的参数。
原理其实很简单:当你传入的T是Wrapped的子类型时,条件类型T extends Wrapped<any> ? never : T会返回never——而TypeScript不允许任何值赋值给never类型,所以静态检查直接就把这种非法调用拦下来了。
如果你的包装类型是类而不是类型别名,逻辑也是一样的:
class WrappedClass<T> { constructor(public value: T) {} } function wrapClass<T>(value: T extends WrappedClass<any> ? never : T): WrappedClass<T> { return new WrappedClass(value); } // 同样的效果 const classWrapped = wrapClass(123); wrapClass(classWrapped); // 编译报错
另外还有个小细节:如果传入的是包含Wrapped类型的联合类型,这个逻辑也能正常工作——因为TypeScript的条件类型对联合类型是“分发式”的,会自动拆分联合类型逐一判断,把其中属于Wrapped的部分变成never,最终只保留合法的类型。
比如:
type MixedType = number | Wrapped<boolean>; wrap(123); // 正常 wrap({ wrappedValue: true }); // 报错,正确
这样就完美实现了你要的“静态拒绝已包装值,接受其他值”的需求啦!
备注:内容来源于stack exchange,提问作者user3624334
相关产品推荐
相关产品推荐

