如何将函数的输入参数与其返回类型相关联以利用自动建议功能

时间:2019-02-26 00:52:38

标签: typescript typescript-generics

我试图使用泛型将函数的参数类型和返回类型链接在一起。我已经找到了解决方案,但是我发现它不是最优的,因为我觉得它可以做得更好。

基本上,我具有以下功能:

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的键bodyCreatePostArgumentshttpClient也在范围之外定义(基本上是Angular7 HTTP客户端)

上面的代码使我可以将函数postCommand与输入变量commandName一起使用自动建议,并根据输入的commandArgumentscommandName进行自动建议。{当我订阅返回的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功能仍然可用,并且错误在正确的位置(接口定义本身,如果有的话)

1 个答案:

答案 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;

基本上,您正在强迫默认参数满足仅在AvailableCommandskeyof CommandMap相同的情况下才满足的一般约束。

有帮助吗?