使用接口';TypeScript泛型中的属性?
我正在尝试创建一个帮助器通用使用接口';TypeScript泛型中的属性?,typescript,generics,redux,Typescript,Generics,Redux,我正在尝试创建一个帮助器通用actionCreator函数,以避免为我的每个操作创建actionCreator类型的函数: interface Response { readonly value: 'A' | 'B'; } enum ActionTypes { created = 'ns/created', deleted = 'ns/deleted', } interface Created { readonly type: ActionTypes.crea
actionCreator
函数,以避免为我的每个操作创建actionCreator类型的函数:
interface Response {
readonly value: 'A' | 'B';
}
enum ActionTypes {
created = 'ns/created',
deleted = 'ns/deleted',
}
interface Created {
readonly type: ActionTypes.created;
readonly payload: Response;
}
interface Deleted {
readonly type: ActionTypes.deleted;
}
type Action = Created | Deleted;
// actionCreator = (type, payload) => ({type, payload});
// actionCreator(ActionTypes.created, response);
// actionCreator(ActionTypes.deleted);
是否可以告诉TS,actionCreator
不应该只返回字符串和可选的有效负载对象,而是应该知道type Action
以检查类型和有效负载对?
类似于
函数actionCreator(type:T.type,payload:T.payload)
的操作无效。以下所有操作都假定您有两种以上的操作类型。如果您真的只有两个,那么使用函数重载并完成:
function actionCreator(type: ActionTypes.created, payload: Response): Created;
function actionCreator(type: ActionTypes.deleted): Deleted;
function actionCreator(type: any, payload?: any) {
return (typeof payload === 'undefined') ? { type } : { type, payload }
}
有关更一般的内容,请继续阅读:
一旦该功能进入TypeScript 2.8(预计将于2018年3月发布),您现在就可以使用
typescript@next
)您可以指定一个功能,该功能的行为(我认为)与您想要的一样,而无需触摸现有代码,如下所示:
// If K is a key of T, return T[K]. Otherwise, return R
type MaybePropType<T, K extends string, R=never> = K extends keyof T ? T[K] : R
// Given K, one of the "type" properties of the Action union,
// produce the constituent of Action union whose type is K. So:
// ActionFromType<ActionTypes.created> is Created, and
// ActionFromType<ActionTypes.deleted> is Deleted.
type ActionFromType<K extends Action['type'], AA = Action>
= AA extends infer A
? MaybePropType<AA, 'type'> extends K ? AA : never
: never
// The one-argument actionCreator() call accepts an element of ActionTypes
// corresponding to an Action with no payload, and produces an Action of that type.
function actionCreator<
T extends ActionTypes & ('payload' extends keyof ActionFromType<T> ? never : {})
>(type: T): ActionFromType<T>;
// The two-argument actionCreator() call accepts an element from ActionTypes
// corresponding to an Action with a payload, and a value of the payload type,
// and produces an Action of that type.
function actionCreator<
T extends ActionTypes & ('payload' extends keyof ActionFromType<T> ? {} : never)
>(type: T, payload: MaybePropType<ActionFromType<T>, 'payload'>): ActionFromType<T>;
// The implementation of actionCreator() creates a one-or-two-property
// value from its arguments. Note that this implementation won't be right
// if you ever add new Action types that require more properties
function actionCreator(type: any, payload?: any) {
return (typeof payload === 'undefined') ? { type } : { type, payload }
}
这需要进行大量的类型转换,一些错误消息将是神秘的,这取决于一个非常新的特性
如果您不介意更改您的代码,您可以得到一些与TypeScript 2.7兼容的东西,其中
actionCreator()
更容易推理。。。但之前的样板可能有点吓人:
// create a mapping from action type to payload type
type ActionPayloads = {
'ns/created': Response
}
// create a list of actions with no payloads
type ActionsWithoutPayloads = 'ns/deleted';
// this is a helper type whose keys are the ActionTypes
// and whose values represent the part of the Action without the type
// so, just {} for a payloadless type, and {readonly payload: P} for
// a payloaded type
type ActionMap = { [K in ActionsWithoutPayloads]: {} } &
{ [K in keyof ActionPayloads]: { readonly payload: ActionPayloads[K] } }
// this is a helper function which makes sure we define ActionTypes
// correctly
const keysOfActionMap = <T extends { [k: string]: keyof ActionMap }>(x: T) => x;
// now we have ActionTypes. It is a plain object, not an enum,
// but it behaves similarly
const ActionTypes = keysOfActionMap({
created: 'ns/created',
deleted: 'ns/deleted'
});
// some helper type functions
type IntersectionToObject<T> = { [K in keyof T]: T[K] }
type ValueOf<T> = T[keyof T];
// extract Action from previous stuff.
// Action by itself is a union of actions from before, while
// Action<K> is the action corresponding to K from ActionTypes.
type Action<K extends keyof ActionMap = keyof ActionMap> =
ValueOf<{
[P in K]: IntersectionToObject<{ readonly type: P } & ActionMap[P]>
}>
// if you need names for Created and Deleted:
type Created = Action<typeof ActionTypes.created>;
type Deleted = Action<typeof ActionTypes.deleted>;
唷!希望有帮助;祝你好运
// create a mapping from action type to payload type
type ActionPayloads = {
'ns/created': Response
}
// create a list of actions with no payloads
type ActionsWithoutPayloads = 'ns/deleted';
// this is a helper type whose keys are the ActionTypes
// and whose values represent the part of the Action without the type
// so, just {} for a payloadless type, and {readonly payload: P} for
// a payloaded type
type ActionMap = { [K in ActionsWithoutPayloads]: {} } &
{ [K in keyof ActionPayloads]: { readonly payload: ActionPayloads[K] } }
// this is a helper function which makes sure we define ActionTypes
// correctly
const keysOfActionMap = <T extends { [k: string]: keyof ActionMap }>(x: T) => x;
// now we have ActionTypes. It is a plain object, not an enum,
// but it behaves similarly
const ActionTypes = keysOfActionMap({
created: 'ns/created',
deleted: 'ns/deleted'
});
// some helper type functions
type IntersectionToObject<T> = { [K in keyof T]: T[K] }
type ValueOf<T> = T[keyof T];
// extract Action from previous stuff.
// Action by itself is a union of actions from before, while
// Action<K> is the action corresponding to K from ActionTypes.
type Action<K extends keyof ActionMap = keyof ActionMap> =
ValueOf<{
[P in K]: IntersectionToObject<{ readonly type: P } & ActionMap[P]>
}>
// if you need names for Created and Deleted:
type Created = Action<typeof ActionTypes.created>;
type Deleted = Action<typeof ActionTypes.deleted>;
// actions without payloads only accept one parameter
function actionCreator<K extends ActionsWithoutPayloads>(
type: K): Action<K>;
// actions with payloads require two parameters
function actionCreator<K extends keyof ActionPayloads>(
type: K, payload: ActionPayloads[K]): Action<K>;
// same impl as before
function actionCreator(type: any, payload?: any) {
return (typeof payload === 'undefined') ? { type } : { type, payload }
}
declare const resp: Response;
// no errors
const created = actionCreator(ActionTypes.created, resp); // Created
const deleted = actionCreator(ActionTypes.deleted); // Deleted
const okayDeleted = actionCreator('ns/deleted'); // okay this time
// errors
const badCreated = actionCreator(ActionTypes.created); // missing payload
const badDeleted = actionCreator(ActionTypes.deleted, "payload?!"); // unexpected payload