TypeScript根据对象属性类型定义匹配处理函数的类型问题
问题原因
现有Record类型定义中,所有处理函数的参数均被标注为完整的Activity联合类型,TypeScript无法建立对象key与参数子类型的关联,因此不会自动触发类型窄化,导致无法访问不同类型独有的属性。
实现方法
通过映射类型遍历所有metaData.type的字面量值,结合Extract工具类型筛选出对应type的Activity子类型,将其作为对应处理函数的参数类型即可实现自动窄化。
- 首先定义类型提取工具,用于匹配指定type对应的Activity子类型:
type ActivityOfType<T extends Activity["metaData"]["type"]> = Activity & { metaData: Extract<Activity["metaData"], { type: T }> }
Extract会从metaData的联合类型中,筛选出type属性等于泛型T的成员,与原Activity类型交叉后得到精准窄化的子类型。
- 使用映射类型重新定义格式化函数集合的类型:
export const activityFormatFunctions: { [K in Activity["metaData"]["type"]]: (activity: ActivityOfType<K>) => string } = { createScenario: (activity) => { // 自动窄化为createScenario类型,可直接访问对应独有属性 activity.metaData.data.scenarioId; return "" }, createTask: (activity) => { activity.metaData.data.taskId; return "" }, deleteScenario: (activity) => { activity.metaData.data.scenarioName; return "" }, deleteTask: (activity) => { activity.metaData.data.taskName; return "" }, updateScenario: (activity) => { // 可直接访问updateScenario独有的scenario属性、data下的scenarioName,无类型报错 activity.metaData.data.scenarioName; activity.metaData.scenario; return "" }, updateTask: (activity) => { activity.metaData.data.taskName; return "" }, };
额外优化
如果需要统一调用处理函数,可以封装通用调用方法,全程保留类型校验:
function formatActivity<T extends Activity["metaData"]["type"]>( activity: ActivityOfType<T> ): string { return activityFormatFunctions[activity.metaData.type](activity); }
该实现会自动获得以下类型能力:
- 新增Activity类型时,TS会强制要求补充对应处理函数,避免遗漏
- 每个处理函数内部会自动做类型窄化,非法访问不存在的属性会直接报类型错误
- 调用
formatActivity时无需手动断言类型,TS会自动推导返回值类型
内容的提问来源于stack exchange,提问作者hornta
相关产品推荐
相关产品推荐

