如何创建一个从打字稿中的类中提取所有方法的类型?
Posted
技术标签:
【中文标题】如何创建一个从打字稿中的类中提取所有方法的类型?【英文标题】:How to create a type that extract all methods from a class in typescript? 【发布时间】:2022-01-22 09:55:57 【问题描述】:假设我有一堂课Foo
class Foo
bar()
// anything
baz()
// anything
我如何创建一个类型ExtractMethods
,它将接收一个类并返回一个接口或具有类方法的类型?
例如
type FooMethod = ExtractMethods<Foo> // => bar(): void; baz(): void;
我知道对此问题的一个常见评论是“为什么不为Foo
创建一个界面并使用您创建的界面?”
但在我想解决的情况下,Foo
不是我创建的类,而是第三方类。我可以为Foo
创建一个接口,但它永远不会完美地反映这些方法,以防将来类随着更新而发生变化,我还需要更新接口,这是我试图避免的事情。
【问题讨论】:
如果您的代码示例显示了一些用例和边缘情况,这会有所帮助,因为现在type ExtractMethods<T> = T
将适用于您的示例。无论如何,this 对你有用吗?据我所知,您无法将方法(存在于类原型中)与函数值属性(往往存在于实例中)区分开来,因此用例真正决定了您想要的是简单/困难/不可能。跨度>
@jcalz 听起来很完美。我不知道不可能将方法与函数属性区分开来。但对我来说,没关系
@jcalz 如果你愿意,请将其添加为答案
我想知道是否有一种偷偷摸摸的方法来区分方法和函数值属性,使用方法参数的二元性?我想这不可靠,因为并非所有方法/函数都有参数。
@kaya3 是的,如果你有方法参数,你可能会以某种方式进行双方差检查,但据我所知,没有很好的方法来概括这一点。据我所知,映射类型总是将方法转换为 func-props。不过,我很想被证明是错误的!
【参考方案1】:
让我们编写一个类型函数PickMatching<T, V>
,它接受一个对象类型T
和一个属性值类型V
,并仅将T
的属性类型可分配给V
的那些属性计算为另一个对象类型。如果我们使用key remapping in mapped types,这很简单:
type PickMatching<T, V> =
[K in keyof T as T[K] extends V ? K : never]: T[K]
通过将键映射到never
,我们实质上是从映射类型中省略了该属性。然后我们可以通过从T
中选择所有函数值属性来表达ExtractMethods<T>
:
type ExtractMethods<T> = PickMatching<T, Function>;
这会产生以下结果:
class Foo
bar()
baz()
notAMethod = 123;
funcProp = () => 10;
type FooMethod = ExtractMethods<Foo>
/* type FooMethod =
bar: () => void;
baz: () => void;
funcProp: () => number;
*/
如您所见,number
类型的notAMethod
不存在于FooMethod
中。 bar()
和 baz()
方法根据需要出现在 FooMethod
中。重要的是,funcProp
的函数类型也出现在FooMethod
中。类型系统无法可靠地区分 方法 和 函数值属性。主要区别在于成员是直接存在于实例上还是存在于类原型上,但类型系统将类实例和类原型视为同一类型。
这确实是实现ExtractMethods
的最佳方法。
另一方面,如果您只想为Foo
之类的特定 类执行此操作,并且可以从类型级别下拉到值级别,则可以使用以下事实:编译器理解spreading 一个类的实例到一个新对象只会给你实例属性:
const spreadFoo = ... new Foo() ;
/* const spreadFoo:
notAMethod: number;
funcProp: () => number;
*/
从那个你可以计算FooMethod
(假设你没有任何非方法原型成员):
type FooMethod = Omit<Foo, keyof typeof spreadFoo>;
/* type FooMethod =
bar: () => void;
baz: () => void;
*/
但这种方法是否可行取决于您的用例。
Playground link to code
【讨论】:
【参考方案2】:基于this article,我想出了以下解决方案(playground):
class Test
foo()
bar()
zar = 3;
type SubType<Base, Condition> = Pick<Base,
[Key in keyof Base]: Base[Key] extends Condition ? Key : never
[keyof Base]>;
type MethodsOnly<T> = SubType<T, () => unknown>;
type T = Pick<Test, keyof MethodsOnly<Test>>
const test = as T;
test.bar; // legal
test.foo; // legal
test.zar; // Property 'zar' does not exist on type 'T'.(2339)
【讨论】:
以上是关于如何创建一个从打字稿中的类中提取所有方法的类型?的主要内容,如果未能解决你的问题,请参考以下文章