TypeScript如何基于特定属性收窄对象类型实现问卷渲染
TypeScript问卷渲染器类型实现最佳方案
这是TS里典型的可辨识联合(Discriminated Union) 应用场景,两种思路不需要二选一,搭配用才是最稳妥的实现:
- 先抽离所有问题共有的公共字段到基础类型,避免重复定义
- 基于基础类型定义每个细分问题类型,用唯一字面量值的
type字段作为类型辨识标记 - 把所有细分类型组合成一个联合类型作为总Question类型导出,各细分类型也按需单独导出
具体实现代码
首先定义基础公共类型:
type BaseQuestion = { label: string; // 后续新增公共字段比如id、required、placeholder都可以加在这里,所有问题类型自动继承 }
然后定义两类具体问题类型:
type TextQuestion = BaseQuestion & { type: 'text'; answer?: string; } type MultipleOptionsQuestion = BaseQuestion & { type: 'multiple-options'; answer: Array<string>; } // 导出总联合类型,同时按需导出细分类型 export type Question = TextQuestion | MultipleOptionsQuestion; export type { TextQuestion, MultipleOptionsQuestion };
接下来调整渲染逻辑,你原来的写法里不管question类型是什么,都会同时把参数传给两个渲染函数,TS没法自动收窄类型,改成渲染映射表的写法就能实现全流程类型安全:
// 细分渲染函数明确指定入参类型,内部自动推导answer的正确类型 function renderTextQuestion(question: TextQuestion) { question.answer // 自动推导为string | undefined,和类型定义完全一致 // 文本题渲染逻辑 } function renderMultipleOptionsQuestion(question: MultipleOptionsQuestion) { question.answer // 自动推导为string[],符合预期 // 多选题渲染逻辑 } // 定义类型安全的渲染映射表 const renderMap = { 'text': renderTextQuestion, 'multiple-options': renderMultipleOptionsQuestion } satisfies Record<Question['type'], (question: Question) => unknown>; function renderer(question: Question) { return renderMap[question.type](question); }
方案优势
- 新增问题类型时(比如单选题、评分题、矩阵题),只需要新增对应的细分类型,再把它加到
Question联合类型里,TS会立刻在renderMap位置抛出类型错误,提醒你补充对应的渲染逻辑,不会漏写分支 - 不需要写任何
as类型断言,全流程类型自动推导,从根源上避免把错误类型的问题传给对应渲染函数的问题 - 公共字段统一维护,后续修改公共属性只需要改
BaseQuestion一处,不用逐个修改每个问题类型的定义
内容的提问来源于stack exchange,提问作者wmattei
相关产品推荐
相关产品推荐

