TypeScript中如何同时校验操作对象结构与回调类型?
问题描述
我有一个表示业务操作的数据结构:
const operations = { list: { request: "a", response: "b", }, get: { request: "a", response: "b", }, } as const;
我希望创建一个可接受基于上述operations的回调的类型:
type Ops = typeof operations; type Callbacks = { [Property in keyof Ops]: ( param: Ops[Property]["request"] ) => Ops[Property]["response"]; };
此时若定义回调时遗漏任何项,编译器会报错:
const callbacks: Callbacks = { }; // ^^^--- Type '{}' is missing the following properties from type 'Callbacks': list, get
现在我想创建另一个类型来校验operations对象的结构:
interface OperationDescriptor<A, B> { request: A; response: B; } type Operations = { [key: string]: OperationDescriptor<any, any>; }; const operations: Operations = { list: { request: "a", response: "b", }, get: { request: "a", response: "b", }, } as const; // 正常 const badOperations: Operations = { list: { request: "a", response: "b", }, get: { // 报错,缺少response属性 request: "a", }, } as const;
但这样做后,编译器不再对回调遗漏项报错,因为Operations类型不知道operations的具体键。请问是否有办法兼顾以下两点:
- 定义类型以校验
operations对象的结构; - 拥有能基于
operations结构校验回调函数的Callbacks类型?
解决方案
可以通过泛型工具函数实现两者兼顾,既校验operations的结构符合OperationDescriptor规范,又保留其具体的键和字面量类型信息。
步骤1:定义结构校验的泛型函数
创建一个泛型函数,约束传入的对象必须符合{ [key: string]: OperationDescriptor<any, any> }结构,同时保留原对象的具体类型:
interface OperationDescriptor<A, B> { request: A; response: B; } // 泛型函数:校验结构的同时保留原对象的具体类型 const createOperations = <T extends { [K in keyof T]: OperationDescriptor<any, any> }>(ops: T) => ops;
步骤2:用该函数创建operations对象
这样生成的operations既会被校验结构合法性,又会完整保留所有具体的键和字面量类型:
const operations = createOperations({ list: { request: "a", response: "b", }, get: { request: "a", response: "b", }, } as const); // 结构合法,正常通过 // 结构错误时直接报错 const badOperations = createOperations({ list: { request: "a", response: "b", }, get: { request: "a", // 此处报错:Property 'response' is missing in type '{ request: "a"; }' but required in type 'OperationDescriptor<any, any>' }, } as const);
步骤3:基于operations生成Callbacks类型
此时typeof operations仍然保留了具体的键和类型信息,之前的Callbacks类型可以正常工作,遗漏回调项时编译器会报错:
type Ops = typeof operations; type Callbacks = { [Property in keyof Ops]: ( param: Ops[Property]["request"] ) => Ops[Property]["response"]; }; // 遗漏项会报错:Type '{}' is missing the following properties from type 'Callbacks': list, get const callbacks: Callbacks = { };
原理说明
泛型函数createOperations的泛型参数T extends { [K in keyof T]: OperationDescriptor<any, any> },会约束传入的ops必须满足每个属性都是OperationDescriptor类型;同时TypeScript会自动推导T为传入对象的具体字面量类型,不会丢失键和类型信息,从而同时实现了结构校验和保留具体类型的需求。
内容的提问来源于stack exchange,提问作者Adam A
相关产品推荐
相关产品推荐

