设置:
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的类型)。
因此由于某种原因,此功能只能深入一个级别。这是打字稿的限制吗?我可以使用某种类型的递归来实际找到泛型吗?
答案 0 :(得分:0)
我不知道编译器实际上如何处理inferring条件类型。我不惊讶在放弃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
对于某些Entity<R>
只是R
,则返回R
。那应该很好。否则,检查T
是否为object
。如果不是,则仅返回never
。 (如果我们要检查原始类型,这可以使我们短路到never
)。否则,为_SchemaOf<T[K]>
的每个键K
计算T
的并集。 (可能的警告:此类型似乎不算作循环条件类型,并且不会导致编译器错误,但是我不确定它是否完全合法。有关循环条件的讨论,请参见this GitHub issue类型。
因此,_SchemaOf<Entity<A>>
应该只是A
,而_SchemaOf<{a: X, b: Y, c: Z}>
应该等于_SchemaOf<X> | _SchemaOf<Y> | _SchemaOf<Z>
。最终将递归遍历所有对象类型,并拉出所有Entity
类型并获得它们的并集。
这几乎是您想要的,但是当您传递的内容不是SchemaOne<T>
的{{1}},例如T
时,它的确会返回一些奇怪的答案。即使_SchemaOf<{a: Entity<A>, b: string}>
不是A
,也只会给您{a: Entity<A>, b: string}
。
所以我们进行如下检查:
SchemaOne<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
好的,希望对您有所帮助。祝你好运!