问题
We are using ngrx with Angular/TypeScript. To add some type safety to Actions, we created the following
export interface TypedAction<T> extends Action {
type: string;
payload: T;
}
export class ExtensionRegistrationAction implements TypedAction<ExtensionRegistrationPayload> {
readonly type = ExtensionActionTypes.REGISTER_EXTENSION;
constructor(public payload: ExtensionRegistrationPayload) {}
}
export class ExtensionRegistrationSucceededAction implements TypedAction<void> {
readonly type = ExtensionActionTypes.REGISTER_EXTENSION_SUCCESS;
constructor(public payload: void) {}
}
export class ExtensionRegistrationFailedAction implements TypedAction<{messageCode: string}> {
readonly type = ExtensionActionTypes.REGISTER_EXTENSION_FAILURE;
constructor(public payload: {messageCode: string}) {}
}
export type ExtensionActions = ExtensionRegistrationAction
| ExtensionRegistrationFailedAction
| ExtensionRegistrationSucceededAction;
// The above lets us do the following
export function appExtensionsReducer(state: AppExtensionState = initialAppExtensionsState,
action: ExtensionActions): AppExtensionState {
switch (action.type) {
case ExtensionActionTypes.REGISTER_EXTENSION:
const registerAction = action as ExtensionRegistrationAction;
return {
registrations: state.registrations.concat(registerAction.payload)
};
// Handle other actions...
default: {
return state;
}
}
}
To get rid of the boiler place code, I thought I could create a function that generates those classes.
export function createTypedActionConstructor<T>(type: string): TypedAction<T> {
return class TypedActionImpl implements TypedAction<T> {
readonly type = type;
constructor(public payload: T) {}
}
}
But I get an error because what I'm returning is the class that implements TypedAction, not the instance. The error says:
TS2322 Type 'typeof TypedActionImpl' is not assignable to type 'TypedAction'. Property 'type' is missing.
What I did get working
The following code gets around the problem by requiring two declarations, one for the interface that implements T
and one for a factory function that creates that type T
. The problem is that it doesn't help that much with boiler plate code bloat.
export function createTypedActionFactory<T>(type: string): (payload: T) => TypedAction<T> {
return function(payload: T): TypedAction<T> {
return {type, payload};
}
}
// Now to create an action I need the two statements
export interface ExtensionRegistrationAction extends TypedAction<ExtensionRegistrationPayload> {}
export const createExtensionRegistrationAction = createTypedActionFactory<ExtensionRegistrationPayload>(
ExtensionActionTypes.REGISTER_EXTENSION);
Question Is it impossible to return a dynamic implementation of a generic class from TypeScript?
回答1:
should be:
export function createTypedActionConstructor<T>(type: string): { new (payload: T): TypedAction<T> } {
...
}
Edit
Another option is to add multiple function signatures:
export function createTypedActionConstructor(type: ExtensionActionTypes.REGISTER_EXTENSION_FAILURE): { new (payload: {messageCode: string}): ExtensionRegistrationFailedAction };
export function createTypedActionConstructor(type: ExtensionActionTypes.REGISTER_EXTENSION_SUCCESS): { new (payload: void): ExtensionRegistrationSucceededAction };
...
来源:https://stackoverflow.com/questions/43000982/how-to-create-a-factory-that-returns-anonymous-classes-that-implement-a-generic