Typescript如何定义特定值组合联合类型解决API handler访问报错
TypeScript 实现按资源操作权限约束API映射访问类型
问题背景
现有按资源分类的API处理函数集合,结构如下:
const API = { A: { CREATE: createA, UPDATE: updateA, DELETE: deleteA }, B: { CREATE: createB, UPDATE: updateB // B资源无DELETE处理函数 }, C: { CREATE: createC, UPDATE: updateC, DELETE: deleteC } }
需要实现getHandler方法,根据传入的资源名和操作名返回对应的处理函数。初始实现将key和action都定义为全量联合类型时,TS会提示B资源不存在DELETE属性的类型错误;手动拆分KeyAction为判别联合类型后,报错依然存在。
原因说明
手动拆分联合类型不生效的核心原因是:函数参数直接解构后,TS会把key和action判定为两个独立的联合类型变量,丢失了两者的绑定约束关系——即不会记住「当key为B时,action只能是CREATE/UPDATE」的规则,因此会认为action可能取到DELETE,导致访问API[key][action]时报错。
可落地方案
方案1:基于实际API结构反向推导类型(推荐,零额外类型维护成本)
通过as const锁定API对象的字面量类型,再反向映射生成所有合法的入参组合,配合泛型保留key和action的绑定关系:
// 示例处理函数定义 const createA = () => {} const updateA = () => {} const deleteA = () => {} const createB = () => {} const updateB = () => {} const createC = () => {} const updateC = () => {} const deleteC = () => {} // 给API对象加as const,锁定精确的属性结构 const API = { A: { CREATE: createA, UPDATE: updateA, DELETE: deleteA }, B: { CREATE: createB, UPDATE: updateB }, C: { CREATE: createC, UPDATE: updateC, DELETE: deleteC } } as const // 从API实际结构反向生成所有合法的(key, action)组合类型 type KeyAction = { [K in keyof typeof API]: { key: K action: keyof typeof API[K] } }[keyof typeof API] // 用泛型+Extract绑定入参中key和action的对应关系 const getHandler = <K extends KeyAction['key']>(params: Extract<KeyAction, { key: K }>) => { const { key, action } = params return API[key][action] }
该实现的效果:
- 传入非法组合比如
{key: 'B', action: 'DELETE'}时,TS会直接抛出类型错误,符合业务约束 - 访问
API[key][action]无任何类型报错 - 后续新增、修改API的资源或操作时,不需要手动更新类型定义,TS会自动同步推导合法入参范围
方案2:手动定义API类型约束(适合需要明确标注API类型的场景)
如果需要显式约束API对象的结构,可以先手动定义API的接口类型,再基于接口推导合法入参:
// 先定义API的类型约束 interface APIType { A: { CREATE: () => void UPDATE: () => void DELETE: () => void } B: { CREATE: () => void UPDATE: () => void } C: { CREATE: () => void UPDATE: () => void DELETE: () => void } } // 声明API时绑定类型 const API: APIType = { A: { CREATE: createA, UPDATE: updateA, DELETE: deleteA }, B: { CREATE: createB, UPDATE: updateB }, C: { CREATE: createC, UPDATE: updateC, DELETE: deleteC } } // 基于接口反向推导KeyAction type KeyAction = { [K in keyof APIType]: { key: K action: keyof APIType[K] } }[keyof APIType] const getHandler = <K extends keyof APIType>(params: Extract<KeyAction, { key: K }>) => { const { key, action } = params return API[key][action] }
注意:不要在函数入参位置直接解构,会导致TS丢失类型绑定关系,在函数体内解构即可避免该问题。
内容的提问来源于stack exchange,提问作者CSSer
相关产品推荐
相关产品推荐

