我有一些代码,比如:
const methodsList = [
'foo',
'bar',
// ... 20 other items ...
]
export class Relayer {
constructor() {
for (const methodName of methodsList) {
this[methodName] = (...args) => {
// console.log('relaying call to', methodName, args)
// this is same for all methods
}
}
}
}
const relayer = new Relayer()
relayer.foo('asd') // TS error
relayer.bar('jkl', 123) // TS error
现在,当我使用类实例时,TypeScript在调用relayer.foo()
或relayer.bar()
时会发出抱怨。要使代码编译,我必须将其转换为as any
或类似的代码。
我有一个声明foo
、bar
和其他方法的接口:
interface MyInterface {
foo: (a: string) => Promise<string>
bar: (b: string, c: number) => Promise<string>
// ... 20 other methods
}
如何让TypeScript学习动态声明的foo
和bar
类方法?declare
语法在这里有用吗?
发布于 2020-04-09 15:23:27
第一步是创建一个类型或接口,当用methodsList
中的值进行索引时,结果将是一个函数:
// The cast to const changes the type from `string[]` to
// `['foo', 'bar']` (An array of literal string types)
const methodsList = [
'foo',
'bar'
] as const
type HasMethods = { [k in typeof methodsList[number]]: (...args: any[]) => any }
// Or
type MethodNames = typeof methodsList[number] // "foo" | "bar"
// k is either "foo" or "bar", and obj[k] is any function
type HasMethods = { [k in MethodNames]: (...args: any[]) => any }
然后,在构造函数中,为了能够分配methodsList
的键,您可以添加一个类型断言,即this is HasMethods
// General purpose assert function
// If before this, value had type `U`,
// afterwards the type will be `U & T`
declare function assertIs<T>(value: unknown): asserts value is T
class Relayer {
constructor() {
assertIs<HasMethods>(this)
for (const methodName of methodsList) {
// `methodName` has type `"foo" | "bar"`, since
// it's the value of an array with literal type,
// so can index `this` in a type-safe way
this[methodName] = (...args) => {
// ...
}
}
}
}
现在,在构造之后,您必须仍然转换类型:
const relayer = new Relayer() as Relayer & HasMethods
relayer.foo('asd')
relayer.bar('jkl', 123)
在使用工厂函数进行构造时,还可以消除这些强制转换:
export class Relayer {
constructor() {
// As above
}
static construct(): Relayer & HasMethods {
return new Relayer() as Relayer & HasMethods
}
}
const relayer = Relayer.construct()
另一种方法是创建一个新的类和类型断言,new
将生成一个HasMethods
对象:
class _Relayer {
constructor() {
assertIs<HasMethods>(this)
for (const methodName of methodsList) {
this[methodName] = (...args) => {
// ...
}
}
}
}
export const Relayer = _Relayer as _Relayer & { new (): _Relayer & HasMethods }
const relayer = new Relayer();
relayer.foo('asd')
relayer.bar('jkl', 123)
或者,如果您只使用new
,然后在methodsList
中使用方法,则可以:
export const Relayer = class Relayer {
constructor() {
assertIs<HasMethods>(this)
for (const methodName of methodsList) {
this[methodName] = (...args) => {
// ...
}
}
}
} as { new (): HasMethods };
您还可以使用MyInterface
接口而不是HasMethods
,跳过第一步。这也为你的通话提供了类型安全。
发布于 2020-03-31 05:55:16
使用以下语法:
export class Relayer {
constructor() {}
public foo(){
// your foo method
this.executedOnEachFunction();
}
public bar(){
// your bar method
this.executedOnEachFunction();
}
executedOnEachFunction(){
// what you want to do everytime
}
}
发布于 2020-03-31 06:06:05
对我来说,这听起来像是需要一个接口。
interface MyInterface {
foo(): void; // or whatever signature/return type you need
bar(): void;
// ... 20 other items ...
}
export class Relayer implements MyInterface {
constructor() {}
foo(): void {
// whatever you want foo to do
}
// ... the rest of your interface implementation
}
它看起来像是在实现某种类型的接口。在构造函数中,您定义的是什么方法实现,而不是在类主体中定义它们。可能有助于阅读类类型接口
https://stackoverflow.com/questions/60951788
复制