TypeScript:如何根据Action类型限制action函数的参数个数
解决TypeScript Action函数的参数约束问题
问题描述
现有以下TypeScript代码实现了动作创建函数action,但存在不符合预期的行为:
- 调用
action(TEST1)时符合预期,传入第二个参数会触发报错; - 调用
action(TEST2)时允许不传入payload参数,而我们需要实现的是:仅当ActionTypeMaps[T]为undefined时,才允许单参数调用action,否则必须传入对应类型的payload。
原代码:
const TEST1 = "TEST1"; type TEST1 = "TEST1"; const TEST2 = "TEST2"; type TEST2 = "TEST2"; interface ActionTypeMaps { TEST1: undefined; TEST2: string; } export interface IAction<T extends keyof ActionTypeMaps> { type: T; payload: ActionTypeMaps[T]; } export interface IActionWithoutPayload<T extends keyof ActionTypeMaps> { type: T; payload: undefined; } export function action<T extends keyof ActionTypeMaps>( type: T ): IActionWithoutPayload<T>; export function action<T extends keyof ActionTypeMaps>( type: T, payload: ActionTypeMaps[T] ): IAction<T>; export function action<T extends keyof ActionTypeMaps>( type: T, payload?: ActionTypeMaps[T] ): IActionWithoutPayload<T> | IAction<T> { if (payload === undefined) { return { type, payload: undefined }; } return { payload, type, }; }
问题原因
原函数的第一个重载签名允许所有T extends keyof ActionTypeMaps类型的参数都可以单参数调用,没有对ActionTypeMaps[T]的类型做区分,导致即使ActionTypeMaps[T]是string这类非undefined类型,依然能匹配单参数重载。
解决方案
修改第一个重载的返回类型,通过条件类型约束,仅当ActionTypeMaps[T]为undefined时,该重载才生效;否则返回never,让TypeScript拒绝匹配这个重载。
修改后的代码:
const TEST1 = "TEST1"; type TEST1 = "TEST1"; const TEST2 = "TEST2"; type TEST2 = "TEST2"; interface ActionTypeMaps { TEST1: undefined; TEST2: string; } export interface IAction<T extends keyof ActionTypeMaps> { type: T; payload: ActionTypeMaps[T]; } export interface IActionWithoutPayload<T extends keyof ActionTypeMaps> { type: T; payload: undefined; } // 仅允许ActionTypeMaps[T]为undefined的情况使用单参数调用 export function action<T extends keyof ActionTypeMaps>( type: T ): T extends { [K in T]: ActionTypeMaps[K] }[T] extends undefined ? IActionWithoutPayload<T> : never; // 非undefined类型必须传入payload参数 export function action<T extends keyof ActionTypeMaps>( type: T, payload: ActionTypeMaps[T] ): IAction<T>; export function action<T extends keyof ActionTypeMaps>( type: T, payload?: ActionTypeMaps[T] ): IActionWithoutPayload<T> | IAction<T> { if (payload === undefined) { return { type, payload: undefined }; } return { payload, type, }; }
验证效果
action(TEST1):正常通过类型检查;action(TEST1, "any"):触发类型报错,符合预期;action(TEST2):触发类型报错,提示缺少payload参数;action(TEST2, "test content"):正常通过类型检查。
内容的提问来源于stack exchange,提问作者albertjan
相关产品推荐
相关产品推荐

