簡體   English   中英

泛型類型的遞歸推斷

[英]Recursive infer with generic types

設置:

export type SchemaOne<T> =
  | Entity<T>
  | SchemaObjectOne<T>;
export interface SchemaObjectOne<T> {
  [key: string]: SchemaOne<T>;
}
export type SchemaOf<T> = T extends SchemaOne<infer R> ? R : never;

const sch: Entity<ArticleResource> = ArticleResource.getEntitySchema();
const a = { a: sch  };
const aa: SchemaOne<ArticleResource> = a;
// works!
type Z = SchemaOf<typeof a>;
// Z is ArticleResource as expected

const b = { a: { b: sch }  };
type ZZ = SchemaOf<typeof b>;
// ZZ is unknown - SADBEAR

所以我的遞歸定義正確匹配(從https://github.com/Microsoft/TypeScript/issues/3496#issuecomment-128553540盜取的方法)。 (因此,作品在aa定義之后)。 但是,現在我希望能夠推斷出該類型,即使不使其更通用。 (獲得bb的類型)。

因此由於某種原因,此功能只能深入一個級別。 這是打字稿的限制嗎? 我可以使用某種遞歸來實際找到泛型嗎?

我不知道編譯器實際上如何推斷條件類型。 我不驚訝它會放棄unknown (或any )之前進行多少次類型實例化。 由於infer R本身並不能為您完成此操作,因此建議您使用自己的方式檢查類型以確定給定類型應返回什么R (如果有)。

首先,我要定義它們以便編譯...您自己的類型可能有所不同,但是它們不在問題陳述中(所以我可以選擇它們,對嗎?……對嗎?)

// Making these up

interface Entity<T> {
  e: T;
}

class ArticleResource {
  static getEntitySchema<T>(): Entity<T> {
    return null!;
  }
  ar = "ArticleResource";
}

這是SchemaOf<T>的一種可能的實現:

type _SchemaOf<T> = T extends Entity<infer R>
  ? R
  : T extends object ? { [K in keyof T]: _SchemaOf<T[K]> }[keyof T] : never;

(我將其命名為_SchemaOf因為我將使用它來構建最終的SchemaOf )。 在這里,如果T對於某些R只是Entity<R> ,則返回R 那應該很好。 否則,檢查T是否為object 如果不是,則never返回。 (如果我們檢查原始類型,這將使​​我們never會短路)。 否則,計算的聯合_SchemaOf<T[K]>對於每一個鍵KT (可能的警告:此類型似乎不算作循環條件類型,並且不會導致編譯器錯誤,但是我不確定它是否完全合法。有關循環條件類型的討論,請參見GitHub問題 。 )

因此, _SchemaOf<Entity<A>>應該只是A ,而_SchemaOf<{a: X, b: Y, c: Z}>應該等效於_SchemaOf<X> | _SchemaOf<Y> | _SchemaOf<Z> _SchemaOf<X> | _SchemaOf<Y> | _SchemaOf<Z> _SchemaOf<X> | _SchemaOf<Y> | _SchemaOf<Z> 最終將遞歸遍歷對象類型,並拉出所有Entity類型並獲得它們的並集。

這幾乎是您想要的,但是當您為T傳遞的不是SchemaOne<T>東西時,確實會返回一些奇怪的答案,例如_SchemaOf<{a: Entity<A>, b: string}> 即使{a: Entity<A>, b: string}不是SchemaOne<A> ,這也只會給您A

因此,我們進行如下檢查:

type SchemaOf<T> = T extends SchemaOne<_SchemaOf<T>> ? _SchemaOf<T> : never;

我們檢查_SchemaOf<T>的結果,以確保SchemaOne<_SchemaOf<T>>匹配T 如果是這樣,那太好了。 如果沒有,我們never

讓我們測試一下:

type Z = SchemaOf<typeof a>;
// Z is ArticleResource as expected

const b = { a: { b: sch } };
type ZZ = SchemaOf<typeof b>;
// ZZ is ArticleResource too

對你有用。 接着:

const c = { a: { b: sch, c: "string" } };
type _C = _SchemaOf<typeof c>; // ArticleResource
type C = SchemaOf<typeof c>; // never

我認為這會拒絕c 並且,查看工會的東西:

const d = { a: sch, b: null! as Entity<string> };
type D = SchemaOf<typeof d>; // string | ArticleResource
const dd: SchemaOne<D> = d; // okay

好的,希望對您有所幫助。 祝好運!

鏈接到代碼

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM