typescript 将类数组的方法名提取为类型并集

ifmq2ha2  于 2022-12-19  发布在  TypeScript
关注(0)|答案(1)|浏览(124)

我有一个数组类,并希望提取所有的方法到一个类型的联盟。
例如:

class A{
  getBooks(): Book[]{}
  getBook(): Book{}
}

class B{
  getUsers(): User[]{}
  getUser(): User{}
  getBooksOfUser(userId: string)Book[]{}
}

我想有一个类型,其中有所有的方法,像这样:

type allMethods = "getBooks" | "getBook" | "getUsers" | "getUser" | "getBooksOfUser";

我尝试了几种不同的东西,但无法得到它的工作:

const testResolvers = <const> [A, B]
type ExtractFunctionKeys<T> = { [P in keyof T]-?: T[P] extends Function ? P : never}[keyof T]
export type ValuesOf<T extends any[]>= ExtractFunctionKeys<T[number]>;

type Foo = ValuesOf<typeof testResolvers>;

谢谢你的帮助!

gfttwv5a

gfttwv5a1#

在上述尝试中有两个主要问题:

  1. T[number]indexed access type)产生T元组/数组中每种类型的 * 联合 *;当使用属于这种联合的变量时,TypeScript仅将键(共享键)的 * 交集 * 保持为有效;在您的示例中,A | B没有公共密钥,因此keyof (A | B)已经是never(即,即使没有过滤方法)
  2. typeof testResolversreadonly [typeof A, typeof B],即它把typeof操作符分配给元组元素,我们得到类的类型 constructor 而不是实际的类类型;那么当访问它们的密钥时(例如使用keyof typeof A),我们得到的只是"prototype"
    问题1:我们想要一个 * 交集 *,而不是一个 union,TypeScript会将索引键的 union(交集任何元素中的所有索引键)保持为有效。没有简单的方法可以从Tuple建立这样的交集(我们无法使用索引存取表示法)。但它仍然是可行的,例如:
// Convert a tuple into an intersection of every type in the tuple
type TupleToIntersection<T> =
    T extends readonly never[]
    ? unknown : (
        T extends readonly [infer First, ...infer Rest]
        ? First & TupleToIntersection<Rest>
        : never)

type intersection = TupleToIntersection<typeof testResolvers>
//   ^? type intersection = typeof A & typeof B

问题2:要从构造函数类型中取回 class 类型,我们只需要确保访问"prototype"属性,通常是再次通过索引访问。如果需要,我们甚至可以构建一个helper类型来有条件地执行转换:

// Get the class (prototype) from its constructor, if available
type ConstructorToPrototype<C> = C extends { prototype: infer P } ? P : C

type AClass = ConstructorToPrototype<typeof A>
//   ^? type AClass = A

然后,我们可以将这些辅助类型插入到链中,以获得提取元组中所有类的方法的并集的所需行为:

// 1. Convert the tuple type into an intersection
// 2. Get the classes instead of constructor types
// 3. Get the keys which have function values (i.e. methods)
type ValuesOf<T extends readonly unknown[]> = ExtractFunctionKeys<ConstructorToPrototype<TupleToIntersection<T>>>;

type Foo = ValuesOf<typeof testResolvers>;
//   ^? type Foo = "getBooks" | "getBook" | "getUsers" | "getUser" | "getBooksOfUser"

Playground链接

相关问题