TypeScript:递归从类中检索字段属性。

huangapple go评论128阅读模式
英文:

TypeScript: Recursively retrieve field properties from class

问题

我试图使用这个答案在调用set方法时排除函数,该方法本质上是类的字段,但我收到错误消息Type instantiation is excessively deep and possibly infinite. 我看到了这个问题,但我不能使用// @ts-ignore,因为那样会失去类型安全性,并且接受的答案修改了它,以至于我不知道如何在我的示例中应用它。我还看到了这个问题,但再次,如何在这里应用有点令人困惑,可能是因为我不太理解TS递归泛型。如何递归检索字段属性(同时排除函数并考虑字段是否可选)而不会出现过多深度错误?

注意:链接代码中可能存在其他问题,因此我希望有经验的TS专家来修复它们。

Fiddle 和代码:

// types taken from https://stackoverflow.com/a/62362197/1253609
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]: T[P] extends Function ? never : 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]>;
};

class Base {
  set(data?: Partial<DeepWritable<typeof this>>) {
    Object.assign(this, data);
  }
}

class Parent extends Base {
  name?: string;
  arr?: Parent[];
};

const record = new Parent();
record.set({
  // https://github.com/microsoft/TypeScript/issues/34933
  arr: [{
    name: '0'
  }]
})
console.log(record.arr);
英文:

I'm trying to use this answer to exclude functions from when I call the set method with essentially the fields of the class, but I get the error Type instantiation is excessively deep and possibly infinite. I saw this question, but I can't use // @ts-ignore because I'll lose my type safety, and the accepted answer modifies it in such a way that I don't know how to apply in my example. I also saw this question, but once again, it's a little confusing how to apply here, probably due to my lack of understanding TS recursive generics. How do I recursively retrieve the field properties (while excluding functions and respecting if the fields are optional or not) and not get the excessively deep error?

Note: there might be some additional issues in the linked code, so I'm wishing for a seasoned TS expert to fix them.

Fiddle and code:

// types taken from https://stackoverflow.com/a/62362197/1253609
type IfEquals&lt;X, Y, A=X, B=never&gt; =
(&lt;T&gt;() =&gt; T extends X ? 1 : 2) extends
(&lt;T&gt;() =&gt; T extends Y ? 1 : 2) ? A : B;
type WritableKeys&lt;T&gt; = {
[P in keyof T]: T[P] extends Function ? never : IfEquals&lt;{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, P&gt;
}[keyof T];
type DeepWritablePrimitive = undefined | null | boolean | string | number | Function;
type DeepWritable&lt;T&gt; =
T extends DeepWritablePrimitive ? T :
T extends Array&lt;infer U&gt; ? DeepWritableArray&lt;U&gt; :
T extends Map&lt;infer K, infer V&gt; ? DeepWritableMap&lt;K, V&gt; :
T extends Set&lt;infer T&gt; ? DeepWriableSet&lt;T&gt; : DeepWritableObject&lt;T&gt;;
type DeepWritableArray&lt;T&gt; = Array&lt;DeepWritable&lt;T&gt;&gt;;
type DeepWritableMap&lt;K, V&gt; = Map&lt;K, DeepWritable&lt;V&gt;&gt;;
type DeepWriableSet&lt;T&gt; = Set&lt;DeepWritable&lt;T&gt;&gt;;
type DeepWritableObject&lt;T&gt; = {
[K in WritableKeys&lt;T&gt;]: DeepWritable&lt;T[K]&gt;
};
class Base {
set(data?: Partial&lt;DeepWritable&lt;typeof this&gt;&gt;) {
Object.assign(this, data);
}
}
class Parent extends Base {
name?: string;
arr?: Parent[];
};
const record = new Parent();
record.set({
// https://github.com/microsoft/TypeScript/issues/34933
arr: [{
name: &#39;0&#39;
}]
})
console.log(record.arr);

答案1

得分: 1

问题出现在递归部分,似乎是由于 Map 导致的,首先检查是否为 Map&lt;any, any&gt; 似乎可以解决它。您错过了失去可选性的键。

type DeepWritable1&lt;T&gt; =
  | T extends DeepWritablePrimitive ? T
  : T extends (infer U)[] ? DeepWritable1&lt;U&gt;[]
  : T extends Map&lt;any, any&gt; ? (
    T extends Map&lt;infer K, infer V&gt; ? Map&lt;K, DeepWritable1&lt;V&gt;&gt; : never
  )
  : T extends Set&lt;infer V&gt; ? Set&lt;DeepWritable1&lt;V&gt;&gt;
  : DeepWritableRecord1&lt;T&gt;;

type DeepWritableRecord1&lt;T&gt; = {
  // 需要保留可选性
  [K in keyof Pick&lt;T, WritableKeys&lt;T&gt;&gt;]: DeepWritable1&lt;T[K]&gt;
}

链接:https://tsplay.dev/WGdl0w

英文:

Problem with recursion seems to be caused by Map, checking for Map&lt;any, any&gt; first seems to fix it.
You have missed keys losing optionality

type DeepWritable1&lt;T&gt; =
  | T extends DeepWritablePrimitive ? T
  : T extends (infer U)[] ? DeepWritable1&lt;U&gt;[]
  : T extends Map&lt;any, any&gt; ? (
    T extends Map&lt;infer K, infer V&gt; ? Map&lt;K, DeepWritable1&lt;V&gt;&gt; : never
  )
  : T extends Set&lt;infer V&gt; ? Set&lt;DeepWritable1&lt;V&gt;&gt;
  : DeepWritableRecord1&lt;T&gt;;

type DeepWritableRecord1&lt;T&gt; = {
  // need to keep optionality
  [K in keyof Pick&lt;T, WritableKeys&lt;T&gt;&gt;]: DeepWritable1&lt;T[K]&gt;
}

https://tsplay.dev/WGdl0w

huangapple
  • 本文由 发表于 2023年6月8日 20:29:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/76431872.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定