Первая часть проблемы относится к вашему типу OptionalKeys
.Отношение является обратным, если у вас есть объединение, объединение является супертипом члена, а не наоборот.Например:
type N = number | undefined extends number ? "Y": "N" // will be "N"
type Y = number extends number | undefined ? "Y": "N" // will be "Y"
Так что в нашем случае OptionalKeys
будет:
type OptionalKeys<T> = {[P in keyof T]-: undefined extends T[P]? P : never }[keyof T]
Нам также нужно исключить undefined
из ключей, так как оно будет тамиз-за необязательности свойств.
Вторая часть проблемы заключается в том, как создать псевдоним рекурсивного типа, который будет работать правильно во всех ситуациях.Для этого мы можем обратиться к найденному примеру DeepReadOnly
здесь
type Foo = {
foo?: number
fooArray?: number[]
bar: string
obj?: {
qwe?: number
asd: string
}
objArray?: Array<{
qwe?: number
asd: string
}>
}
type OptionalKeys<T> = Exclude<{ [P in keyof T]: undefined extends T[P] ? P : never }[keyof T], undefined>
type primitive = string | number | boolean | undefined | null
type PickOptionalProperties<T> =
T extends primitive ? T :
T extends Array<infer U> ? PickOptionalPropertiesArray<U> :
PickOptionalPropertiesObject<T>
interface PickOptionalPropertiesArray<T> extends ReadonlyArray<PickOptionalProperties<T>> { }
type PickOptionalPropertiesObject<T> = {
readonly [P in OptionalKeys<T>]: PickOptionalProperties<Exclude<T[P], undefined>>
}
type Foo24 = PickOptionalProperties<Foo>
const o: Foo24 = {
foo: 0,
fooArray: [1, 2],
obj: {
qwe: 1,
},
objArray: [
{ qwe: 1 }
]
}
Редактировать
Как @jcalz указал на не строгий нольпроверить версию этого типа можно с помощью:
type OptionalKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? K : never }[keyof T]
Это работает путем проверки того, присваивается ли выбор свойства P
из типа he типу {}
, если это так, это означает, что свойствоP
является необязательным, так как если бы это было необходимо, результирующий Pick
не может быть назначен на {}
.
Эта версия на самом деле будет лучше выбирать только дополнительные свойства и требуемые свойствавведите baseType | undefined
, который может быть плюсом или минусом в зависимости от вашего варианта использования.