【发布时间】:2021-11-17 03:01:27
【问题描述】:
我有一些输入有问题。我能够将问题减少到以下最小示例。我有一个函数,其中第一个参数是通用的,并且被限制为可序列化为 JSON 的东西。
type Json = string | number | boolean | null | Json[] | { [property: string]: Json };
type Foo<A extends Json> = (n: A) => boolean;
const a: Foo<number> = (n: number) => n + 1 > 0;
const b: Foo<string> = (n: string) => n.endsWith("a");
我需要这些函数数组的类型定义,其中泛型部分可以是 Foo 的泛型部分允许的任何内容。
我明白为什么这不起作用。
const foos: Foo<Json>[] = [a, b]; // Error
而且,any 太松散了。它允许Foos 无效的值,例如此示例,其中函数的参数是HTMLElement,不能分配给Json。
const foos: Foo<any>[] = [a, b, (n: HTMLElement) => true]; // Invalid, but no error
我需要的是这个数组中允许的Foos 的所有具体类型的联合。但是,该联合必须无限长才能覆盖任何可能等同于 Json 的内容。
type SomeFoo = Foo<string> | Foo<number> | ... | Foo<Record<string, Record<string, [number, string[]]>>> | ...;
const foos: SomeFoo[] = [a, b];
有没有办法使用泛型或其他机制来避免这种无限联合?
编辑:
如果有帮助,这里有一个稍微复杂一点的例子,它更接近于我试图输入的真实代码:playground。我有一个具有两个功能的对象。第一个函数的返回类型需要匹配第二个函数的第一个参数。
编辑 2:
根据来自 cmets 的反馈,对这个问题进行了重大重构,但它仍然提出相同的基本问题。
【问题讨论】:
-
通用函数参数之间有什么共同特征吗?在我看来,您应该只使用
any或{},因为除了const数组之外,您将无法确定成员函数接受的类型。也许你可以解释一下你打算如何使用它? -
不是typescript不能表达这个,而是这个type不太合理。想象一下你的沙箱中的代码可以工作,那么它会如何使用呢?例如我可以做
b['a'].bar(123),这应该可以工作,因为类型签名Record<string, MyType<Json>>告诉我b['a']是MyType<Json>类型,因此b['a'].bar是(n: Json) => boolean类型,所以我应该可以通过number 给它,但实际上它只需要一个字符串。这是您需要绕过的问题,而不是打字稿在争论 -
您的游乐场示例比您问题中的代码更令人回味;您可能希望将问题改写成这样。在任何情况下,您正在寻找的称为存在量化泛型(当您认为需要无限联合时,这就是您想要的)并且它们在 TS 中不受直接支持(请参阅ms/TS#14466 )。您可以模拟this 等类型。如果这符合您的需求,我可以写一个答案。
-
另外,当你说你正在描述一个现有的 JavaScript 库时,你可以改变存在模拟器以适应这种情况,因为普遍量化的泛型(TS 和大多数其他语言中的“正常”泛型) ) 当您切换数据的消费者和提供者时,看起来存在量化。据推测,任何消耗事物数组的函数都可以成为普遍量化的泛型,从而减轻对数组本身进行存在量化的需求。但我需要一些激励minimal reproducible example 来提出具体建议。
-
所以,就像this 一样,我可能会处理库类型;忘记尝试为数组类型编写无限联合,而让编译器推断出更强大的东西。然后以正常方式使
processMyTypeArray()泛型。让我知道您希望看到哪些版本的东西被写出来,最好是通过编辑问题中的代码来激发解决方案。一个玩具示例,但不是一个“最小”到无用的示例。
标签: typescript typescript-generics