在运行时这是不可能的,因为属性的必需性/可选性只存在于 TypeScript 类型系统中,在代码实际运行时它已经是 erased。您可以通过decorators 等添加自己的运行时信息,但为此您需要修改生成类和对象的实际代码。因此,不可能在给定对象或构造函数的情况下获取所需属性名称的数组。
在设计时,可以提取一个类型的必需/可选键,作为keyof T 的子类型。该解决方案依赖于conditional types 以及空对象类型{} 被认为可分配给weak type(没有必需属性的类型)这一事实。像这样:
type RequiredKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? never : K }[keyof T];
type OptionalKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? K : never }[keyof T];
还有一个用法示例:
interface SomeType {
required: string;
optional?: number;
requiredButPossiblyUndefined: boolean | undefined;
}
type SomeTypeRequiredKeys = RequiredKeys<SomeType>;
// type SomeTypeRequiredKeys = "required" | "requiredButPossiblyUndefined" ?
type SomeTypeOptionalKeys = OptionalKeys<SomeType>;
// type SomeTypeOptionalKeys = "optional" ?
这不适用于带有index signatures 的类型:
interface SomeType {
required: string;
optional?: number;
requiredButPossiblyUndefined: boolean | undefined;
[k: string]: unknown; // index signature
}
type SomeTypeRequiredKeys = RequiredKeys<SomeType>;
// type SomeTypeRequiredKeys = never ?
type SomeTypeOptionalKeys = OptionalKeys<SomeType>;
// type SomeTypeOptionalKeys = string ?
不确定您的用例是否关心可索引类型。如果是这样,有一个更复杂的解决方案,它首先提取已知的文字键,然后检查必需/可选:
(编辑:以下内容已更新以解决 TS4.3 中的重大更改,请参阅 ms/TS#44143)
type RequiredLiteralKeys<T> = keyof { [K in keyof T as string extends K ? never : number extends K ? never :
{} extends Pick<T, K> ? never : K]: 0 }
type OptionalLiteralKeys<T> = keyof { [K in keyof T as string extends K ? never : number extends K ? never :
{} extends Pick<T, K> ? K : never]: 0 }
type IndexKeys<T> = string extends keyof T ? string : number extends keyof T ? number : never;
导致:
type SomeTypeRequiredKeys = RequiredLiteralKeys<SomeType>;
// type SomeTypeRequiredKeys = "required" | "requiredButPossiblyUndefined" ?
type SomeTypeOptionalKeys = OptionalLiteralKeys<SomeType>;
// type SomeTypeOptionalKeys = "optional" ?
type SomeTypeIndexKeys = IndexKeys<SomeType>;
// type SomeTypeIndexKeys = string ?