如何在TypeScript编译时确保函数参数值的唯一性?
实现函数调用时重复标识符触发类型错误
要实现同一标识符重复传入函数时触发TypeScript类型错误,核心是利用泛型累积已使用的标识符集合,让函数每次调用后更新自身的类型约束,限制后续调用只能传入未使用过的ID。
方案1:链式调用的函数实现
先定义允许的标识符列表,再通过泛型函数追踪已使用的ID:
// 定义所有允许使用的标识符 type AllIds = 'id-1' | 'id-2'; // 定义泛型函数类型,UsedIds记录已使用的ID,默认是空集合 type UniqueFoo<UsedIds extends AllIds = never> = { (id: Exclude<AllIds, UsedIds>): UniqueFoo<UsedIds | typeof id>; }; // 实现函数,每次调用后返回更新泛型约束的自身 const foo: UniqueFoo = (id) => { console.log(`my id: ${id}`); return foo as UniqueFoo<typeof id>; }; // 使用示例 const fooAfterFirstCall = foo('id-1'); // 正常执行,返回的函数已排除'id-1' fooAfterFirstCall('id-1'); // ❌ 类型错误:'id-1'已被使用 fooAfterFirstCall('id-2'); // ✅ 正常执行 fooAfterFirstCall('id-2'); // ❌ 类型错误:'id-2'已被使用
方案2:类封装实现
如果更倾向于类的写法,可以用类的泛型参数记录已使用的ID:
type AllIds = 'id-1' | 'id-2'; class UniqueFoo<UsedIds extends AllIds = never> { call(id: Exclude<AllIds, UsedIds>): UniqueFoo<UsedIds | typeof id> { console.log(`my id: ${id}`); // 类型断言更新泛型参数 return this as unknown as UniqueFoo<UsedIds | typeof id>; } } // 使用示例 const foo = new UniqueFoo(); const foo1 = foo.call('id-1'); // 正常调用,返回的实例已排除'id-1' foo1.call('id-1'); // ❌ 类型错误 foo1.call('id-2'); // ✅ 正常调用
原理说明
- 泛型参数
UsedIds会累积每次调用传入的标识符,初始值为never(空集合)。 Exclude<AllIds, UsedIds>确保函数参数只能是未被使用过的标识符。- 每次调用后返回的函数/实例,其泛型参数会更新为
UsedIds | typeof id,相当于把当前ID加入已使用集合,后续调用就无法再传入该ID。
需要注意:TypeScript是静态类型系统,同一个变量的类型不会在运行时动态更新,所以必须通过赋值新变量来获取更新后的类型约束,才能触发重复调用的类型错误。
内容的提问来源于stack exchange,提问作者Mark Kurkowski
相关产品推荐
相关产品推荐

