【问题标题】:is there a way to get all required properties of a typescript object有没有办法获取打字稿对象的所有必需属性
【发布时间】:2019-03-29 19:15:29
【问题描述】:

有没有办法获取打字稿接口或对象的所有必需属性。 Object.getOwnPropertyDescriptors(myObject)keyof T 之类的东西,但信息属性是必需的/可选的

【问题讨论】:

  • 你试过使用元数据反射api吗?
  • @toskv - 链接?我认为 TypeScript 在反射特性方面提供的不多,而 JavaScript 自己的反射特性也无法回答上述问题。
  • 它确实提供了一些关于装饰器反射的东西。这解释了一点。 blog.wolksoftware.com/…
  • 我无法为元数据反射api添加所需的装饰器,我正在使用的接口是由swagger生成器生成的
  • 那么答案很简单。这是不可能的。

标签: typescript reflection


【解决方案1】:

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

【讨论】:

  • 非常有用且有趣的概念,我结合使用它来构造一个只有可选键type PickOptional&lt;T&gt; = Pick&lt;T, OptionalKeys&lt;T&gt;&gt;的新对象
  • 为什么需要-??在我看来,从每个键中删除可选标签不会影响最终结果?我错过了什么?
  • ...因为您的示例中的 SomeType 没有索引签名。这是预期的行为。
【解决方案2】:

这是我的解决方案:

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&lt;A, K&gt; extends Pick&lt;B, K&gt;更改为Pick&lt;B, K&gt; extends Pick&lt;A, K&gt;,使其成为_RequiredKeys,从而找到OptionalKeys

所以这个解决方案(我是为我的rbx 包创建的)适用于联合类型和单例类型。

【讨论】:

    猜你喜欢
    • 2010-11-23
    • 1970-01-01
    • 2019-08-15
    • 1970-01-01
    • 1970-01-01
    • 2017-06-15
    • 1970-01-01
    • 2019-09-07
    • 1970-01-01
    相关资源
    最近更新 更多