如何基于TypeScript的State类型将嵌套属性four设为必填?
问题
我定义了如下TypeScript类型:
type State = { one?: string; two?: { three?: { four?: string; five?: string; six: number }, seven: string } }
我希望复用State类型生成一个新类型,仅将嵌套属性four设为必填,其余属性保持原状态。我知道Required工具类型,但不确定如何应用到深层嵌套属性上。
背景:我有一个State[]类型的数组,需要通过filter和map筛选出包含有效four属性(某ID)的元素,代码如下:
const states: State[] = [...]; states .filter((state) => state.two?.three?.four && valid(state.two?.three?.four)) .map((state) => mapFour(state.two.three.four))
但TypeScript编译器报错,提示state.two.three.four可能为undefined——这是因为类型收窄无法跨filter和map作用域生效。因此我需要编写显式类型守卫(形如.filter((state):state is MyNewTypeWhereFourIsNotOptional => ...)),所以必须创建这个将深层嵌套属性设为必填的新类型。
解决方案
方案1:手动复用原类型定义新类型
针对当前State的结构,可以直接嵌套定义新类型,复用原类型的属性类型避免重复代码:
type StateWithRequiredFour = { // 保持原属性的可选状态 one?: State['one']; // two设为必填,否则无法访问后续嵌套属性 two: { // three设为必填,同理 three: { // four设为必填 four: string; // 保持原属性的可选状态 five?: State['two']['three']['five']; // six原本就是必填,直接复用类型 six: State['two']['three']['six']; }; // seven原本就是必填,复用类型 seven: State['two']['seven']; }; };
方案2:通用深层必填工具类型
如果需要频繁处理深层属性的必填转换,可以编写一个通用工具类型RequiredDeepPath,支持通过路径字符串指定要设为必填的深层属性:
type RequiredDeepPath<T, Path extends string> = // 拆分路径为当前层级和剩余路径 Path extends `${infer First}.${infer Rest}` ? T extends object ? { [K in keyof T]: // 如果当前键是路径的第一部分,递归处理剩余路径并将当前层级设为必填 K extends First ? RequiredDeepPath<Required<T[K]>, Rest> : T[K] } : T // 路径是单个键的情况,仅将该键设为必填 : T extends object ? Required<Pick<T, Path & keyof T>> & Omit<T, Path & keyof T> : T; // 应用到State类型,指定路径'two.three.four' type StateWithRequiredFour = RequiredDeepPath<State, 'two.three.four'>;
编写类型守卫并使用
有了新类型后,编写对应的类型守卫函数:
const hasValidFour = (state: State): state is StateWithRequiredFour => { // 验证four存在且有效 return !!state.two?.three?.four && valid(state.two.three.four); };
修改数组处理代码,使用带类型守卫的filter:
states .filter(hasValidFour) .map((state) => mapFour(state.two.three.four)) // 此处编译器不再报错
内容的提问来源于stack exchange,提问作者Experimenter
相关产品推荐
相关产品推荐

