[英]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]>
對於每一個鍵K
的T
。 (可能的警告:此類型似乎不算作循環條件類型,並且不會導致編譯器錯誤,但是我不確定它是否完全合法。有關循環條件類型的討論,請參見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.