TypeScript中如何基于对象自身属性定义类型解决循环引用问题
解决方案
核心问题说明
你遇到的循环引用问题本质是TS类型系统不支持「在约束泛型参数结构的同时,读取该泛型参数的属性反过来约束自身」,TS需要先确定泛型的完整结构才能读取内部属性,二者形成了推导死锁。
基础示例实现
TS没有原生的self关键字指代正在定义的类型,你可以通过泛型函数的自动推导实现类似效果:
type X<T> = T extends 'a' ? 'a' : 'b' function defineX<T extends string>(val: X<T>) { return val } // 自动校验值合法 const x = defineX('a') // 合法 const y = defineX('c') // 合法,会被推导为'b'类型
实际Questionnaire场景实现
推荐的实现思路是先定义所有合法的Question联合类型,再通过泛型函数自动推导约束,完全避免循环引用:
- 先定义所有Question类型的集合
// 可根据业务扩展任意Question类型 type BooleanQuestion = { type: 'boolean', required?: boolean, label: string } type TextQuestion = { type: 'text', maxLength?: number, label: string } type SelectQuestion = { type: 'select', options: string[], label: string } type Question = BooleanQuestion | TextQuestion | SelectQuestion
- 定义泛型工具函数用于生成Questionnaire
function defineQuestionnaire<T extends Record<string, Question>>(config: T) { return config }
- 直接使用即可自动校验每个Question的类型匹配
const myQuestionnaire = defineQuestionnaire({ isAgree: { type: 'boolean', required: true, label: '是否同意用户协议' }, nickname: { type: 'text', maxLength: 16, label: '用户昵称' }, gender: { type: 'select', options: ['男', '女', '保密'], label: '性别' } })
如果需要单独使用Questionnaire泛型类型,可以用Extract工具类型实现:
type Questionnaire<T extends Record<string, { type: Question['type'] }>> = { [K in keyof T]: Extract<Question, { type: T[K]['type'] }> } // 显式声明类型使用 type MyQuestionnaire = Questionnaire<{ isAgree: { type: 'boolean' }, nickname: { type: 'text' } }>
内容的提问来源于stack exchange,提问作者Garrett
相关产品推荐
相关产品推荐

