泛型类型的递归推断

时间:2019-07-12 02:19:29

标签: typescript

设置:

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的类型)。

因此由于某种原因,此功能只能深入一个级别。这是打字稿的限制吗?我可以使用某种类型的递归来实际找到泛型吗?

1 个答案:

答案 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

好的,希望对您有所帮助。祝你好运!

Link to code