如何实现命令对象方法参数与字段同类型的TypeScript类型安全
实现命令对象的类型安全参数自动推导
问题背景
我需要创建一组游戏命令对象,每个对象包含input字段定义命令参数的校验规则,校验通过后执行resolve函数处理解析后的参数。当前的问题是:尽管校验环节已确保参数符合指定类型,但resolve函数内的参数始终是string[]类型,完全丢失类型安全,使用时必须重复做类型断言。我希望resolve的参数类型能根据当前命令的input字段自动推导,比如setscore命令的参数类型自动变成["b" | "blue" | "r" | "red", number],无需手动指定泛型。
现有示例代码
const commands = [{ name: "setscore", // 第一个参数必须是数组中的元素,第二个必须是数字 input: [["b", "blue", "r", "red"] as const, Number()], resolve({input}) { // 所有参数都被识别为string,丢失类型信息 const [teamName, score] = input // 使用时必须再次类型断言 } }]
期望的参数类型
["b" | "blue" | "r" | "red", number]
解决方案
通过定义类型映射规则+辅助函数的方式,让TypeScript自动推导每个命令的参数类型,实现类型安全效果:
1. 定义输入规则与解析类型的映射
首先定义输入校验规则的类型,以及对应的解析后参数类型:
// 输入校验规则的可选类型:字符串枚举数组 或 Number构造函数 type InputSchemaElement = readonly string[] | typeof Number; // 将输入规则映射为实际参数类型 type ParsedInput<T extends InputSchemaElement> = T extends readonly string[] ? T[number] : T extends typeof Number ? number : string;
2. 定义命令的泛型类型
基于输入规则数组,定义命令对象的类型,让resolve的参数类型自动关联input的规则:
type Command<T extends readonly InputSchemaElement[]> = { name: string; input: T; resolve: (args: { input: { [K in keyof T]: ParsedInput<T[K]> } }) => void; };
3. 用辅助函数自动推导泛型
直接定义命令数组时,TypeScript无法自动捕获每个命令的精确泛型,因此需要一个辅助函数来触发自动推导:
function createCommand<T extends readonly InputSchemaElement[]>(command: Command<T>): Command<T> { return command; }
4. 创建类型安全的命令数组
使用createCommand创建每个命令,TypeScript会自动推导resolve的参数类型:
const commands = [ createCommand({ name: "setscore", input: [["b", "blue", "r", "red"] as const, Number], resolve({ input }) { const [teamName, score] = input; // teamName 自动推导为 "b" | "blue" | "r" | "red" // score 自动推导为 number console.log(`设置${teamName}队分数为${score}`); // 此处无需任何类型断言,完全类型安全 }, }), createCommand({ name: "join", input: [["player", "spectator"] as const], resolve({ input }) { const [role] = input; // role 自动推导为 "player" | "spectator" }, }), ];
原理说明
as const用于保留字符串数组的精确字面量类型,避免TypeScript将其拓宽为string[]- 辅助函数
createCommand让TypeScript自动捕获每个命令input的具体类型,无需手动指定泛型 ParsedInput类型将输入规则(如字符串枚举数组、Number构造函数)映射为实际的参数类型,实现校验规则与业务逻辑的类型同步
内容的提问来源于stack exchange,提问作者jablonka
相关产品推荐
相关产品推荐

