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

How to associate a function's input arguments with its return type to leverage autosuggest features

我正在尝试使用泛型 link 函数的参数类型和 return 类型。我已经找到了一个解决方案,但我发现它不是最优的,因为我觉得它可以做得更好。

基本上,我有以下功能:

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 是在对象的普通接口范围之外定义的。例如,对于 CreatePostArguments,键 groupIdbodyhttpClient 也在范围之外定义(它基本上是 Angular7 HTTP 客户端)

上面的代码允许我将函数 postCommand 与输入变量 commandName 的自动建议和基于我输入的 commandNamecommandArguments 的自动建议一起使用。当我订阅 returned observable.

时,也会推断出 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 };
}

现在,这看起来更具可读性并且具有相同的效果。我仍然有自我暗示。唯一需要注意的是:如果我从 CommandMap 接口中省略 create-post,编译器不会介意。这是上面两个实现的问题。

这主要是我想要的,除了我提到的警告。所以,我现在的问题是:

有没有办法让编译器注意到我没有为 CommandMap 中的所有 AvailableCommands 定义映射,并且仍然对参数和 return 进行自动建议postCommand?

的类型

(我用我的最新版本创建了一个 Playground,以及 post 的模拟实现和我在我的项目中使用的 TypeScript 版本)

编辑:

对我来说最好的整体答案隐藏在众目睽睽之下:

我现在基本上只用一种

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。因为 CommandMap 已经有了我的命令的所有定义,所以我不需要额外的 AvailableCommands 类型。

所有 AutoSuggestion 功能仍然可用,错误出现在正确的位置(接口定义本身,如果有的话)

如果 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 相同时它们才会满足这些约束。

有帮助吗?