如何定义TypeScript函数:接受A/B实例集合但拒绝A|B联合类型参数?
TypeScript 函数参数类型约束实现
需求:编写单个TypeScript函数,满足以下要求:
- 支持传入任意数量的
A类或B类实例(A与B相互独立,无继承关系) - 禁止传入类型为
A|B的联合类型变量作为参数(包括单个或多个联合类型变量) - 必须使用单个函数实现,不能拆分为多个函数
初始示例代码:
class A {/* 省略具体实现 */} class B {/* 省略具体实现 */} let a:A = new A(); fn(a); // 明确类型调用,允许通过编译 let b:B = new B(); fn(b); // 明确类型调用,允许通过编译 let c:A|B = someCondition ? new A() : new B(); fn(c); // 联合类型变量调用,需触发TypeScript编译错误
补充说明:实际需要支持可变数量的参数,示例如下:
class A {/* 省略具体实现 */} class B {/* 省略具体实现 */} let a:A = new A(); let b:B = new B(); let c:A|B = someCondition ? new A() : new B(); interface Fn { (...args: V[]) => void; } const fn: Fn = (...args) => {/* 省略函数实现 */}; fn(a); // 单个明确类型参数,允许 fn(b); // 单个明确类型参数,允许 fn(a, b, a, b); // 多个混合明确类型参数,允许 fn(c); // 联合类型变量参数,需触发编译错误
解决方案
通过自定义类型约束,区分明确的A/B类型和A|B联合类型,实现需求:
class A {/* 省略具体实现 */} class B {/* 省略具体实现 */} // 判断类型是否为联合类型 type IsUnion<T> = [T] extends [infer U] ? U extends T ? T extends U ? false : true : false : false; // 仅允许非联合类型的A或B type AllowedArg<T> = IsUnion<T> extends false ? (T extends A | B ? T : never) : never; // 对参数数组的每个元素应用AllowedArg约束 type AllowedArgs<T extends any[]> = { [K in keyof T]: AllowedArg<T[K]>; }; // 定义符合要求的函数 const fn = <T extends any[]>(...args: AllowedArgs<T>) => { // 在这里编写函数逻辑 console.log(args); }; // 测试用例 let a:A = new A(); fn(a); // ✅ 编译通过 let b:B = new B(); fn(b); // ✅ 编译通过 fn(a, b, a, b); // ✅ 编译通过 let c:A|B = Math.random() > 0.5 ? new A() : new B(); fn(c); // ❌ 编译错误:类型'A | B'不能赋值给类型'never' fn(c, c); // ❌ 编译错误:类型'A | B'不能赋值给类型'never'
原理说明
IsUnion<T>:通过条件类型判断传入的类型是否为联合类型。如果T是联合类型,返回true,否则返回false。AllowedArg<T>:仅允许非联合类型的A或B,如果T是联合类型则返回never,从而触发编译错误。AllowedArgs<T>:遍历参数数组的每个元素,对每个元素应用AllowedArg约束,确保所有参数都是明确的A或B实例,而非联合类型变量。
内容的提问来源于stack exchange,提问作者Yusuke Kawasaki
相关产品推荐
相关产品推荐

