递归排除 Typescript 中的只读属性

Posted

技术标签:

【中文标题】递归排除 Typescript 中的只读属性【英文标题】:Recursively exclude readonly properties in Typescript 【发布时间】:2020-10-03 06:52:10 【问题描述】:

我很确定我缺乏使用复杂泛型的经验,所以我希望有人对如何实现这一点有想法。我的用例是为我的 React/Formik 表单值创建“表单类型”,而无需重新键入新定义或传入具有许多不可更新属性的完整对象。

我找到了这个答案,它展示了如何从 TypeScript 类型中排除只读属性,但我发现很难将我的大脑包裹起来使其递归。此外,我希望它省略返回空嵌套对象的属性(如果可能)。

How to exclude getter only properties from type in typescript

type IfEquals<X, Y, A=X, B=never> =
  (<T>() => T extends X ? 1 : 2) extends
  (<T>() => T extends Y ? 1 : 2) ? A : B;

type WritableKeys<T> = 
  [P in keyof T]-?: IfEquals< [Q in P]: T[P] ,  -readonly [Q in P]: T[P] , P>
[keyof T];

type ReadonlyKeys<T> = 
  [P in keyof T]-?: IfEquals< [Q in P]: T[P] ,  -readonly [Q in P]: T[P] , never, P>
[keyof T];

type Writable<T> = Pick<T, WritableKeys<T>>

type Brand = 
  readonly id: number;
  name: string;
  logo: 
    readonly id: number;
    url: string;
  ,
  creator: 
    readonly id: number;
  
;

type EditableBrand = Writable<Brand>;
// type EditableBrand = 
//   name: string;
//   logo: 
//     url: string;
//   
// ;

【问题讨论】:

【参考方案1】:

TypeScript 存储库中有一个 suggestion 用于 DeepReadonly&lt;T&gt; 类型,还有一些建议的实现。和 *** 上的 question 相同。 Here。是如何实现DeepReadonly 的示例。您可以使用相同的技术来实现DeepWritable 类型:

type IfEquals<X, Y, A=X, B=never> =
  (<T>() => T extends X ? 1 : 2) extends
  (<T>() => T extends Y ? 1 : 2) ? A : B;

type WritableKeys<T> = 
  [P in keyof T]: IfEquals< [Q in P]: T[P] ,  -readonly [Q in P]: T[P] , P>
[keyof T];

type DeepWritablePrimitive = undefined | null | boolean | string | number | Function;
type DeepWritable<T> =
    T extends DeepWritablePrimitive ? T :
    T extends Array<infer U> ? DeepWritableArray<U> :
    T extends Map<infer K, infer V> ? DeepWritableMap<K, V> :
    T extends Set<infer T> ? DeepWriableSet<T> : DeepWritableObject<T>;

type DeepWritableArray<T> = Array<DeepWritable<T>>;
type DeepWritableMap<K, V> = Map<K, DeepWritable<V>>;
type DeepWriableSet<T> = Set<DeepWritable<T>>;

type DeepWritableObject<T> = 
    [K in WritableKeys<T>]: DeepWritable<T[K]>
;

Playground


让我们扩展类型以省略返回空嵌套对象的键:

...

type EmptyKeys<T> = 
  [P in keyof T]:  extends T[P] ? P : never
[keyof T];

type OmitEmptyKeys<T> = Omit<T, EmptyKeys<T>>;
type DeepWritable<T> = ... : OmitEmptyKeys<DeepWritableObject<T>>;

Playground

【讨论】:

这非常有帮助。谢谢! @ericchernuka 很高兴为您提供帮助!点赞是最好的奖励:)

以上是关于递归排除 Typescript 中的只读属性的主要内容,如果未能解决你的问题,请参考以下文章

typescript 排除属性

排除在TypeScript中监视目录

TypeScript中类的使用详解

Protobuf-net:如何从隐式所有公共字段的波尔图合同中排除只读属性(只有吸气剂)?

TypeScript——接口

使用带有递归 JSON 的 Typescript 接口