[英]Typescript repetitive declaration merging
I am currently working on making a redux-like library with TypeScript. 我目前正在使用TypeScript制作类似redux的库。 The basic action looks like this:
基本动作如下所示:
interface ActionBase {
type: string;
payload: any;
}
I then extend the action interface for each action type. 然后,我为每种动作类型扩展动作接口。 For example, for a button-click event, I would have something like:
例如,对于按钮单击事件,我将具有以下内容:
interface ButtonClickAction extends ActionBase {
type: 'BUTTON_CLICK';
payload: {
// Include some kind of metadata in here
};
}
I then am adding some helper functions 然后,我添加了一些辅助功能
function isInstanceOfButtonClick(action: ActionBase ): action is ButtonClickAction {
return action.type === 'BUTTON_CLICK';
}
function buildButtonClickAction(payload): ButtonClickAction {
return {
type: 'BUTTON_CLICK',
payload,
};
}
The problem is that I am doing this for over 20 different types of action. 问题是我正在执行20多种不同类型的操作。 Is there a dry-er way to do this?
有没有更干的方法可以做到这一点? For every action I need:
对于每个动作,我需要:
"BUTTON_CLICK"
) "BUTTON_CLICK"
) ButtonClickAction
) ButtonClickAction
) buildButtonClickAction
) buildButtonClickAction
) isInstanceOfButtonClick
) isInstanceOfButtonClick
) I can achieve all the concrete items (1, 4, 5) using a class or function, but I don't have a dry way to do 2 and 3. Right now, I have something like this for every action: 我可以使用类或函数来实现所有具体项目(1、4、5),但是我没有干的方法来完成2和3。现在,我对每个操作都具有类似的内容:
const KEY = 'BUTTON_CLICK';
namespace ButtonClick {
export type Payload = {...}
export interface Action extends ActionBase {
type: typeof KEY;
payload: Payload;
}
}
let ButtonClick = makeActionValues<typeof KEY, ButtonClick.Payload, ButtonClick.Action>(KEY)
export default ButtonClick;
Is there a nicer way to do this? 有没有更好的方法可以做到这一点?
How about something like a function which creates a dictionary of Action
factories, where each factory has its own isInstance()
and buildAction()
methods for the appropriate type of Action
? 像一个函数创建一个
Action
工厂字典的函数怎么样,其中每个工厂针对合适的Action
类型都有自己的isInstance()
和buildAction()
方法? Like this: 像这样:
interface ActionFactory<T extends string, P> {
isInstance(action: Action): action is Action<T, P>;
buildAction(payload: P): Action<T, P>;
}
interface Action<T extends string=string, P=any> {
type: T,
payload: P,
}
function getActionFactories<M extends object>(mappings: M): {[T in keyof M]: ActionFactory<T, M[T]>} {
const ret: any = {};
Object.keys(mappings).forEach((k: keyof M) => {
type T = keyof M;
type P = M[T];
ret[k] = class Act {
static isInstance(action: Action): action is Action<T, P> {
return action.type === k;
}
static buildAction(payload: P): Action<T, P> {
return new Act(payload);
}
type: T = k;
private constructor(public payload: P) { }
}
});
return ret;
}
You use it by creating a mapping of action keys to payload types: 通过创建操作键到有效负载类型的映射来使用它:
const _: any = void 0;
const ActionPayloads = {
ButtonClick: _ as { whatever: string },
SomeOtherAction: _ as { parameter: number },
WhoKnows: _ as { notMe: boolean },
}
The above is a bit ugly but it's the most DRY I could be... otherwise I'd need to specify the key names twice; 上面的代码有点丑陋,但这是我能做到的最干的……否则,我需要两次指定密钥名称; once for the payload mapping and once for the list of keys.
一次用于有效负载映射,一次用于密钥列表。 Then you call
getActionFactories()
: 然后调用
getActionFactories()
:
const Actions = getActionFactories(ActionPayloads);
The resulting Actions
object acts sort of like a namespace. 产生的
Actions
对象的行为有点像名称空间。 Observe: 观察:
const buttonClick = Actions.ButtonClick.buildAction({ whatever: 'hello' });
const someOtherAction = Actions.SomeOtherAction.buildAction({ parameter: 4 });
const whoKnows = Actions.WhoKnows.buildAction({ notMe: false });
const randomAction = Math.random() < 0.33 ? buttonClick : Math.random() < 0.5 ? someOtherAction : whoKnows
if (Actions.WhoKnows.isInstance(randomAction)) {
console.log(randomAction.payload.notMe);
}
Does that work for you? 那对你有用吗?
I'd like to be able to get the type of the resulting
action.payload
我希望能够得到所产生的类型
action.payload
Well, to get the type of the payload for ButtonClick
, you could use the ActionPayloads
object and do something like: 好了,要获取
ButtonClick
的有效负载类型,可以使用ActionPayloads
对象并执行以下操作:
const buttonClickPayload: typeof ActionPayloads.ButtonClick = {whatever: 'hello'};
const buttonClick = Actions.ButtonClick.buildAction(buttonClickPayload);
Or, if you want Actions
to expose this type, you could add a phantom Payload
property to ActionFactory
: 或者,如果您希望
Actions
公开这种类型,则可以向ActionFactory
添加一个虚拟的Payload
属性:
interface ActionFactory<T extends string, P> {
isInstance(action: Action): action is Action<T, P>;
buildAction(payload: P): Action<T, P>;
Payload: P; // phantom property
}
Then you could refer to the payload type like this: 然后,您可以像这样引用有效负载类型:
const buttonClickPayload: typeof Actions.ButtonClick.Payload = {whatever: 'hello'};
const buttonClick = Actions.ButtonClick.buildAction(buttonClickPayload);
But be careful not to actually try to use the value of Actions.ButtonClick.Payload
, since it won't really exist: 但请注意不要实际尝试使用
Actions.ButtonClick.Payload
的值 ,因为它实际上并不存在:
console.log(Actions.ButtonClick.Payload.whatever); // okay in TS but blows up at runtime.
Hope that helps! 希望有帮助!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.