在TypeScript中以编程方式将方法添加到具有类型安全性的类?

时间:2018-07-27 01:35:55

标签: typescript

我正在为Web服务编写客户端,并且端点之一从可能的20个参数(可能会增加)中获取一个参数。

我在客户端公开了每种可能性的方法。现在我正在这样做:

// keys.ts
export const FUNCTION_ONE = 'FunctionOne';
export const FUNCTION_TWO = 'FunctionTwo';
...
export const FUNCTION_TWENTY = 'FunctionTwenty';

// client.ts
import * as keys from './keys';
import { camelcase } from 'lodash';

export default class Client {
    makeCall(method: string): Promise<void> {
        // some implementation
    }
}

Object.values(keys).forEach((key) => {
    (Client.prototype as any)[camelcase(key)] = function () {
        return this.makeCall(key);
    };
});

Typescript对这些方法一无所知,因为它们是通过编程方式添加的。我不想手动编写每个方法,因为该服务可能会添加更多内容,并且我希望将它们添加到keys.ts文件中很容易。

我当时正在考虑对键进行修改,这将要求我键入键的驼峰形式(可以接受的折衷方案),然后我可以用它来构建可与类结合使用的类型。像这样:

// keys.ts
function key<T extends string>(command: string, name: T) {
    return { command, name };
}

export const FUNCTION_ONE = key('FunctionOne', 'functionOne');
...

// client.ts
export default class Client {
    // same as before
}

interface ClientInterface<T extends Record<string, { name: string }>> {
    // implementation??
}

export default type Client = ClientInterface<keys>;

如何编写这种ClientInterface类型,该类型将使用键中的所有名称作为方法来生成接口?还是有一种更好的方法可以完全做到这一点?

1 个答案:

答案 0 :(得分:2)

您可以使用:

type ClientInterface<T extends Record<string, { name: string }>> =
    {[N in T[keyof T]["name"]]: () => Promise<void> };
export type ClientType = ClientInterface<typeof keys>;

或者,如果您愿意在驼峰式的情况下实际命名常量,那么它将变得更加简单,并且常量上的“重命名”命令将能够更新所有调用:

type ClientInterface<T> =
    {[N in keyof T]: () => Promise<void> };
export type ClientType = ClientInterface<typeof keys>;