如何用TypeScript泛型验证keyof的所有键都已指定?
如何在TypeScript中强制对象所有键必须分配到查询或修改参数中?
我写了一个辅助函数,用于包装对象并根据键属于查询函数还是修改函数来处理所有键。现在需要编写TypeScript类型声明,确保对象的所有键都必须被指定到queries或mutations两个参数中的某一个里。
当前代码
我现在的函数声明如下:
function createService< T, Q extends keyof T, M extends Exclude<keyof T, Q>, >(obj: T, queries: readonly Q[], mutations: readonly M[]) {}
这段代码能验证同一个键不能同时出现在两个参数中,但无法确保所有键都被分配到其中一个参数里。
示例用法
interface Test { a: () => void; b: () => void; } const obj: Test = { a: () => {}, b: () => {} }; // 符合要求的调用 createService(obj, ["a"], ["b"]); createService(obj, ["a", "b"], []); // 错误情况 createService(obj, ["a", "b"], ["b"]); // 当前代码已能检测:"b"重复设置 createService(obj, ["a"], []); // 无报错但应该报错:"b"未被分配 createService(obj, [], ["b"]); // 无报错但应该报错:"a"未被分配
尝试过的无效写法
我试过添加一个泛型检查,但无法通过编译:
function createService< T, Q extends keyof T, M extends Exclude<keyof T, Q>, CHECK extends never = Exclude<keyof T, Q | M>, // 编译错误:Type 'string' is not assignable to type 'never' >(obj: T, queries: readonly Q[], mutations: readonly M[]) {}
解决方案
可以通过泛型约束来强制Q | M完全覆盖keyof T,也就是确保所有键都被分配到两个参数中的一个。以下是可行的写法:
function createService< T, Q extends keyof T, M extends Exclude<keyof T, Q>, _ extends never = Exclude<keyof T, Q | M> >(obj: T, queries: readonly Q[], mutations: readonly M[]) {}
原理说明
这个写法的核心是:如果存在未被分配的键,Exclude<keyof T, Q | M>会返回这些未分配的键组成的联合类型,而这个类型无法赋值给never,因此会触发编译错误。这样就能同时满足两个需求:
- 同一个键不能同时出现在
queries和mutations中(原有逻辑已实现) - 对象的所有键必须被分配到其中一个参数里(新增约束实现)
测试后可以看到,之前未分配键的情况会正确报错,重复键的情况依然会被检测到,完全符合需求。
内容的提问来源于stack exchange,提问作者WaeCo
相关产品推荐
相关产品推荐

