You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的具体键。请问是否有办法兼顾以下两点:

  1. 定义类型以校验operations对象的结构;
  2. 拥有能基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 03:37:33