网站搜索

在 TypeScript 中是否可以为 1 到 N 个字符串的数组声明一个泛型类型


在标题中有点难以解释,但基本上,我希望能够声明一个长度固定的字符串类型数组,从1到N:

interface Command {
  [key: string]: (args: [string, ...string[]]) => boolean;
}

const cmd: Command = {
  TEST: (args: [string, string]) => args[0] === args[1],
  TEST2: (args: [string]) => args[0] === 'hello'
}

所以这不起作用,因为 [string, string] 与 string[] 不同:

Type '(args: [string]) => boolean' is not assignable to type '(args: [string, ...string[]]) => boolean'.

一种解决方案可能是像定义各种参数那样定义:

interface Command {
  [key: string]: (args: [string] | [string, string] | [string, string, string]) => boolean;
}

但对于简单的东西来说有点啰嗦(同意它可以封装在一个接口中),无论如何,有没有我没注意到的更优雅的解决方案?

谢谢,

答案#1

我怀疑你实际上需要 Command 是泛型,这样它的属性才能真正被使用。如果你有一个函数类型,比如 (arg: [string] | [string, string]) => boolean,那么你不能用 (arg: [string]) => boolean 或 (arg: [string, string]) => boolean 来实现它。函数类型在参数类型上是逆变的。你可以拓宽,但不能缩小函数参数的类型。除非你想要求类型为 Command 的所有方法都必须接受每一种可能的一项或多项字符串数组,否则你必须使用泛型。

这里是一个可能的 Command 类型,以及一个辅助函数 asCommand(),它可以让编译器在给定 Command<T> 类型的值时推断出 T 的正确值,而无需你自己写出它:

type Command<T> = { [K in keyof T]: (args: T[K]) => boolean }

const asCommand = <T extends Record<keyof T, [string, ...string[]]>>(
    cmd: Command<T>
) => cmd;

然后,你的 cmd 常量可以这样声明而不会有错误:

const cmd = asCommand({
    TEST: (args: [string, string]) => args[0] === args[1],
    TEST2: (args: [string]) => args[0] === 'hello'
});

编译器记住 cmd.TEST 接收一对,而 cmd.TEST2 接收一个单元组:

cmd.TEST(["", ""]); // okay
cmd.TEST([]); // error
cmd.TEST([""]); // error
cmd.TEST(["", "", ""]); // error

cmd.TEST2([""]); // okay
cmd.TEST2([]); // error
cmd.TEST2(["", ""]); // error
cmd.TEST2(["", "", ""]); // error

并且你不能提供只接受零长度元组的属性:

const badCmd = asCommand({
    OOPS: (args: []) => false, // error!
})

我希望这能给你一些方向;祝你好运!

代码的 Playground 链接