我试图使用泛型将函数的参数类型和返回类型链接在一起。我已经找到了解决方案,但是我发现它不是最优的,因为我觉得它可以做得更好。
基本上,我具有以下功能:
function postCommand<T extends AvailableCommands>(
commandName: T,
commandArguments: ArgumentType<T>
): Observable<PayloadType<T>> {
return httpClient.post<PayloadType<T>>(`/command/${commandName}`, commandArguments);
}
结合以下(示例)定义:
type AvailableCommands =
| 'create-group'
| 'create-post';
type ArgumentType<T extends AvailableCommands> = T extends 'create-group'
? CreateGroupArguments
: T extends 'create-post'
? CreatePostArguments
: never;
type PayloadType<T extends AvailableCommands> = T extends 'create-group'
? CreateGroupPayload
: T extends 'create-post'
? CreatePostPayload
: never;
注意:*Arguments
和*Payload
在对象的简单接口范围之外定义。例如,使用groupId
的键body
和CreatePostArguments
。
httpClient
也在范围之外定义(基本上是Angular7 HTTP客户端)
上面的代码使我可以将函数postCommand
与输入变量commandName
一起使用自动建议,并根据输入的commandArguments
对commandName
进行自动建议。{当我订阅返回的observable时,也会推断出{1}}。
现在,使用条件类型很丑陋,其他开发人员不容易理解。
这导致我尝试寻找另一种方法,我偶然发现了[
这将更改代码,如下所示:
commandPayload
现在,这看起来已经更具可读性,并且效果相同。我仍然有自动建议。唯一的警告是:如果我从function postCommand<T extends AvailableCommands>(
commandName: T,
commandArguments: CommandMap[T]['arguments']
): Observable<CommandMap[T]['payload']> {
return httpClient.post<CommandMap[T]['payload']>(`/command/${commandName}`, commandArguments);
}
interface CommandMap {
'create-group': { arguments: CreateGroupArguments; payload: CreateGroupPayload };
'create-post': { arguments: CreatePostArguments; payload: CreatePostPayload };
}
接口中省略了create-post
,则编译器不会介意。以上两个实现都存在这个问题。
除了我提到的警告以外,这主要是我想要的。所以,我的问题是:
有没有一种方法可以使编译器考虑到以下事实:我没有为CommandMap
中的所有AvailableCommands
定义映射,而是仍然自动建议{{1}的参数和返回类型}?
(我使用最新版本创建了Playground,并模拟了CommandMap
的模拟实现以及我用于项目的TypeScript版本)
对我来说,总体上最好的答案隐藏在视线之外:
我现在基本上只使用一种类型
postCommand
和函数调用:
post
泛型现在扩展了export interface CommandMap {
[index: string]: { arguments: any; payload: any };
'create-group': { arguments: CreateGroupArguments; payload: CreateGroupPayload };
'create-post': { arguments: CreatePostArguments; payload: CreatePostPayload };
}
。由于public postCommand<T extends keyof CommandMap>(
commandName: T,
commandArguments: CommandMap[T]['arguments']
): Observable<CommandMap[T]['payload']> {
return this.httpClient.post<CommandMap[T]['payload']>(`/command/${commandName}`, commandArguments);
}
已经具有命令的所有定义,因此我不需要其他的keyof CommandMap
类型。
所有AutoSuggestion功能仍然可用,并且错误在正确的位置(接口定义本身,如果有的话)
答案 0 :(得分:2)
如果CommandMap
的键与AvailableCommands
的键不匹配时,如果您正在寻找编译器错误,则可以在单独的行中进行:
type VerifyCommandMap<
// if the next line is an error, CommandMap has extra keys
K extends AvailableCommands = keyof CommandMap,
// if the next line is an error, CommandMap is missing some keys
L extends keyof CommandMap = AvailableCommands
> = true;
基本上,您正在强迫默认参数满足仅在AvailableCommands
与keyof CommandMap
相同的情况下才满足的一般约束。
有帮助吗?