typescript - 如何使用映射类型获取对象类型的可选部分?

标签 typescript mapped-types

比如我有这个类型

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 = {
}

但它不起作用,我不确定为什么

最佳答案

问题的第一部分是您的 OptionalKeys 类型。关系是相反的,如果你有一个联合,联合是成员的父类(super class)型,而不是相反。例如:

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 示例 here

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 所指出的,这种类型的非严格 null 检查版本可以使用:

 type OptionalKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? K : never }[keyof T]

它的工作方式是测试从他的类型中选择的 P 属性是否可分配给 {} 如果是这样就意味着属性 P 是可选的,就好像它是必需的一样,结果 Pick 不会分配给 {}

这个版本实际上会更好地挑选出可选属性,以及 baseType | 类型的必需属性。 undefined 根据您的用例,我可以加分或减分。

关于typescript - 如何使用映射类型获取对象类型的可选部分?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52454345/

相关文章:

javascript - 桶状文件 - 了解其预期用例?

javascript - 在 JavaScript 代码中重构 switch-case 语句

angular - typescript 编译器无法跳过导入的js文件

typescript - 如何在不破坏数组属性的情况下实现 TypeScript 深度部分映射类型

html - Angular 2 : Having a fixed element positioned by relation to its parent

node.js - 如何从依赖项的依赖项导入接口(interface)

typescript - 映射类型可以使属性成为可选的,但前提是满足条件?

typescript - 从记录中排除键

typescript - 如何获得 TypeScript 映射类型的逆?