如何为带关联约束的泛型接口记录定义TypeScript类型?
实现可扩展查询语言操作符的TypeScript类型定义
这是完全可行的,核心思路是通过泛型嵌套约束+辅助函数保留窄类型来实现需求,以下是具体方案:
1. 基础类型定义
先明确类型标识到TypeScript类型的映射,以及单个操作数、操作符的类型约束:
// 定义类型标识与实际参数类型的映射表 type ArgTypes = { string: string; number: number; ref: unknown; // 可根据业务需求替换为更具体的类型 }; // 单个操作数的类型:绑定类型标识、输入参数类型、转换后的输出类型 type OpOperand<TypeKey extends keyof ArgTypes, OutputType> = { type: TypeKey; from: (input: ArgTypes[TypeKey]) => OutputType; }; // 单个操作符的类型:关联左右操作数的类型标识和输出类型,约束compare函数参数 type Operator< LKey extends keyof ArgTypes, LOutput, RKey extends keyof ArgTypes, ROutput > = { lhs: OpOperand<LKey, LOutput>; rhs: OpOperand<RKey, ROutput>; compare: (lhsResult: LOutput, rhsResult: ROutput) => boolean; };
2. 操作符集合的类型与辅助函数
为了避免对象字面量的类型拓宽问题(比如"number"被自动转为string),我们用泛型辅助函数来推导并保留窄类型:
// 操作符集合的基础约束 type OperatorsConfig = Record<string, Operator<keyof ArgTypes, any, keyof ArgTypes, any>>; // 辅助函数:让TypeScript自动推导每个操作符的具体类型,保留窄类型字面量 function createOperators<T extends OperatorsConfig>(config: T): T { return config; }
3. 使用示例
现在可以编写类型安全的操作符配置,TS会自动提供IDE提示并校验类型:
const ops = createOperators({ equal: { lhs: { type: 'string', from: (v) => v.trim(), // v自动推导为string类型 }, rhs: { type: 'number', from: (v) => v.toString(), // v自动推导为number类型 }, compare: (lhs, rhs) => lhs === rhs, // lhs自动为string,rhs自动为string }, greaterThan: { lhs: { type: 'number', from: (v) => v, // v为number }, rhs: { type: 'number', from: (v) => v * 1.1, // v为number }, compare: (lhs, rhs) => lhs > rhs, // lhs和rhs均为number }, });
方案说明
- 满足条件1:
from的参数类型通过ArgTypes[TypeKey]与type的字面量值强关联; - 满足条件2:
lhs和rhs可独立指定不同的类型标识和输出类型; - 满足条件3:
compare函数的参数类型自动继承对应from函数的返回类型; - 解决类型拓宽问题:通过
createOperators泛型函数,TypeScript会保留每个type字段的字面量类型(如'string'不会被转为string),而非自动拓宽为通用类型。
内容的提问来源于stack exchange,提问作者Kris Reeves
相关产品推荐
相关产品推荐

