如何让TypeScript工具类型Required对接口嵌套成员生效?
问题本质
TypeScript 内置的Required<T>是浅层转换工具,原生实现仅会移除当前对象层级属性的?可选修饰符,不会递归处理属性值为对象的嵌套结构,因此嵌套层级的可选属性不会被标记为必填。
原生Required的实现源码如下,可以看到没有递归逻辑:
/** * Make all properties in T required */ type Required<T> = { [K in keyof T]-?: T[K]; };
解决方案
自定义递归的深度必填工具类型DeepRequired<T>,遍历属性时如果检测到属性值是对象类型,就递归对该属性值做必填转换,直到遍历到原始类型为止。
基础实现(适配普通对象场景)
type DeepRequired<T> = { [K in keyof T]-?: T[K] extends object ? DeepRequired<T[K]> : T[K] }
兼容数组、函数的增强实现
如果你的类型中包含数组、函数这类特殊引用类型,基础实现会错误递归,可以加分支判断单独处理:
type DeepRequired<T> = // 函数类型直接返回,不做递归 T extends (...args: any[]) => any ? T // 数组类型单独处理,递归转换数组元素的类型 : T extends Array<infer Item> ? Array<DeepRequired<Item>> // 普通对象递归移除所有层级可选修饰符 : T extends object ? { [K in keyof T]-?: DeepRequired<T[K]> } : T
效果验证
替换原代码中的Required<Camera>为DeepRequired<Camera>后,TypeScript会校验所有嵌套层级的必填属性:
interface Camera { id? :string; name? : { firstName?:string; secondName? :string }; site? :string } // 以下代码会触发类型错误:name属性缺少必填的firstName、secondName const badCamera :DeepRequired<Camera> ={ id :'1', name :{}, site :'hello world' } // 所有层级属性补全后可正常通过校验 const validCamera :DeepRequired<Camera> ={ id :'1', name :{ firstName: "cam-01", secondName: "entrance" }, site :'hello world' }
内容的提问来源于stack exchange,提问作者Nyi Nyi Hmue Aung
相关产品推荐
相关产品推荐

