有没有办法获取打字稿对象的所有必需属性

时间:2018-10-25 08:32:57

标签: typescript reflection

有没有一种方法可以获取打字稿接口或对象的所有必需属性。 select2 'Test product manufacturer', from: 'Product manufacturer', search: true Object.getOwnPropertyDescriptors(myObject)之类的东西,但是具有信息属性是必需的/可选的

2 个答案:

答案 0 :(得分:2)

在运行时这是不可能的,因为属性的必要性/可选性仅存在于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 

不确定您的用例是否关心可索引类型。如果是这样,那么有一个更复杂的解决方案可以通过首先提取已知文字键然后检查是否需要/可选来解决该问题:

type RequiredLiteralKeys<T> = { [K in keyof T]-?:
  string extends K ? never : number extends K ? never : {} extends Pick<T, K> ? never : K
} extends { [_ in keyof T]-?: infer U } ? U extends keyof T ? U : never : never;

type OptionalLiteralKeys<T> = { [K in keyof T]-?:
  string extends K ? never : number extends K ? never : {} extends Pick<T, K> ? K : never
} extends { [_ in keyof T]-?: infer U } ? U extends keyof T ? U : never : never;

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 

希望有帮助。祝你好运!

答案 1 :(得分:0)

这是我的解决方法:

type _OptionalKeys<A extends object, B extends object> = {
  [K in KnownKeys<A> & KnownKeys<B>]: Pick<A, K> extends Pick<B, K> ? never : K
};

/**
 * OptionalKeys grabs the keys which are optional from a type `T`.
 * For example, `{ a: string; b: string | undefined; c?: string }` => `'c'`.
 */
export type OptionalKeys<T extends object> = _OptionalKeys<
  T,
  Required<T>
>[KnownKeys<T>];

基本上,我们正在检查OptionalKeys,以便可以提取出RequiredKeys。

/**
 * RequiredKeys grabs the keys which are required from a type `T`.
 * For example, `{ a: string; b: string | undefined; c?: string }` => `'b' | 'c'`.
 */
export type RequiredKeys<T extends object> = Exclude<
  KnownKeys<T>,
  OptionalKeys<T>
>;

那么KnownKeys是什么?

/**
 * Extracts the keys of a union type
 */
// tslint:disable-next-line:no-any
export type KeysOfUnion<T> = T extends any ? keyof T : never;

/**
 * Extracts the known keys from an object – regardless of whether it has an
 * index signature.
 */
export type KnownKeys<T extends object> = {
  [K in keyof T]: string extends K ? never : number extends K ? never : K
} extends { [_ in keyof T]: infer U }
  ? {} extends U
    ? never
    : U
  : never;

当然,您可以颠倒这种方法,因此先找到RequiredKeys,然后通过在{{中将OptionalKeys更改为Pick<A, K> extends Pick<B, K>来找到Pick<B, K> extends Pick<A, K> 1}}设为_OptionalKeys

因此,此解决方案(我为rbx包创建了此解决方案)适用于联合体类型和单例类型。