interface ComplexObject { mandatory: string; title1?: string; title2?: string; } type GetOptional<T> = {}; type Option = GetOptional<ComplexObject>; function createApp(option: Option) { // {title1?: string; title2?: string;} }
上述的代码我们期望通过
提出接口的所有字段
interface ComplexObject { mandatory: string; title1?: string; title2?: string; } type GetOptional<T> = { [P in keyof T]: T[P]; }; type Option = GetOptional<ComplexObject>; function createApp(option: Option) {}
上述代码就提出了
interface ComplexObject { mandatory: string; title1?: string; title2?: string; } type GetOptional<T> = { // Capitalize将属性名的第一个首字母大写 [P in keyof T as `get${Capitalize<P & string>}`]: T[P]; }; type Option = GetOptional<ComplexObject>; function createApp(option: Option) {}
仅保留可选属性
interface ComplexObject { mandatory: string; a: number; title1?: string; title2?: number; } type OptionalKeys<T> = { [K in keyof T]-?: T extends Record<K, T[K]> ? never : K; }[keyof T]; type GetOptional<T> = Pick<T, OptionalKeys<T>>; type Option = GetOptional<ComplexObject>; function createApp(option: Option) {}
OptionalKeys<T> 通过将每个可能的键K 映射到它们自己或never 上来计算类型T 中的所有可选键。映射键的条件检查是建立在是否每个属性K 在T 中是可选的(即不包含在T extends Record<K, T[K]> 的属性K )GetOptional<T> 用于生成一个新的类型,包含所有指定的属性,关于Pick使用请查看
在 TypeScript 的高级类型操作中,[K in keyof T]-? 表示一个映射类型 (Mapped Type),用于遍历类型T 的所有键K 。这里的目的是产生一个新类型,其中T 的所有属性都是必需的,即没有可选属性,具体来说K in keyof T :对于类型T 中的每个键K -? :这是一个修饰符,用于从属性前移除可选性。这意味着如果T 中的属性K 被标记为可选(属性名称后跟一个问号 ?,比如 title1?),-?会将其转换为必需属性
将保留的可选属性改为必填属性
只需要加上
interface ComplexObject { mandatory: string; a: number; title1?: string; title2?: number; } type OptionalKeys<T> = { [K in keyof T]-?: T extends Record<K, T[K]> ? never : K; }[keyof T]; type GetOptional<T> = Pick<T, OptionalKeys<T>>; type Option = Required<GetOptional<ComplexObject>>; function createApp(option: Option) {}