我想创建一个通用的TS类型,给定一个现有的接口返回一个接口,使所有最后一级属性可选,所以给定以下类型
interface FormDeep {
email: string;
address: {
street: {
line1: string;
line2: string;
}
town: string;
}
}
此类型应有效
const newAddress: DeepPartialLastLevel<FormDeep> = {
email: undefined,
address: {
street: {
line1: undefined,
line2: undefined
},
town: undefined,
}
}
但这个不应该
const newAddressNotValid: DeepPartialLastLevel<FormDeep> = {
email: undefined,
address: {
street: undefined, // street is not last level so it should be mandatory
town: undefined,
}
}
我已经成功地创建了一个这样做的类型
type DeepPartialLastLevel<T> = {
[K in keyof T]: T[K] extends object ? DeepPartialLastLevel<T[K]> : (T[K] | undefined)
}
当我不希望密钥被指定时,问题就出现了(VS被定义并且等于undefined
)。
总结如下:我希望下面的对象有效
const newAddress2: DeepPartialLastLevel<FormDeep> = {
email: undefined,
address: { // TS complains because town is missing
street: {
line1: undefined,
line2: undefined
},
}
}
我需要的属性是可选的基础上的条件T[K] extends object
的东西一样
[K in keyof T as T[K] extends object? [K]: [K]?]: T[K] extends object ? DeepPartialLastLevel<T[K]> : T[K]
但从TS语法的观点来看这是无效的
我创建了一个简单的TSPlayground来再现
1条答案
按热度按时间ryhaxcpt1#
算法:
要循环遍历字段,我们将使用mapped types,并使用键重Map过滤掉需要的字段。基本上,如果字段不是一个对象,我们将其键
never
,这将完全删除字段:使用inject关键字,我们将在推断类型
OnlyObjects
中存储上一个Map类型的结果:现在,我们将再次使用
mapped types
Map对象的键(不包括OnlyObjects
的键),这是使用内置的实用工具类型Exclude完成的,为了将它们标记为可选,我们将使用?
可选属性修饰符:把一切都放在一起:
排除部分也可以使用内置的实用程序类型Partial和Omit来完成:
他们中的任何一个都像预期的那样工作。
测试:
链接到Playground