Как получить необязательную часть типа объекта с сопоставленным типом? - PullRequest
0 голосов
/ 22 сентября 2018

Например, у меня есть этот тип

type Foo = {
    foo?: number
    bar: string
    obj?: {
        qwe?: number
        asd: string
    }
}

, и я хочу иметь тип

type Foo2 = {
    foo: number
    obj: {
        qwe: number
    }
}

Я пробовал это

type OptionalKeys<T> = {[P in keyof T]: T[P] extends undefined ? P : never }[keyof T]

type PickOptionalProperties<T> = {
    [P in OptionalKeys<T>]-?: PickOptionalProperties<T[P]>
};
type Foo2 = PickOptionalProperties<Foo>  
const o: Foo2 = {
}

Но это не такне работает, и я не уверен, почему

1 Ответ

0 голосов
/ 22 сентября 2018

Первая часть проблемы относится к вашему типу 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, который может быть плюсом или минусом в зависимости от вашего варианта использования.

...