我有一个类型MyMap,有2个不同的键,它采取一个数组的回调与不同的签名.

type MyMap = {
   type1: (() => void)[] 
   type2: ((data: string) => void)[]
}

我想创建一个接受键和回调的泛型函数,并将其添加到MyMap类型的对象中.

const map: MyMap = { type1: [], type2: [] };

function addToMap<T extends keyof MyMap>(k: T,  cb: MyMap[T][number]): void {
        map[k].push(cb);
}

TypeScript向我展示了当我try 在数组中推送回调时的错误:

Argument of type '(() => void) | ((data: string) => void)' is not assignable to parameter of type '() => void'.
  Type '(data: string) => void' is not assignable to type '() => void'.
    Target signature provides too few arguments. Expected 1 or more, but got 0.(2345)

有没有一种方法可以让TypeScript明白我提供的回调是正确的?

推荐答案

TypeScrip不能"看到"这样一个事实,即MyMap的每个键为K的成员都是一个数组,它允许您将MyMap[K][number]类型的值赋push到它上面.它可以为individual K验证,但当Kgeneric时,它不知道如何验证.它缺乏将事实列表自动转换为单一概括性的能力.因此,它不会将map[k].push视为单一的连贯方法;相反,它会将其视为union个方法,并且map[k]cb之间的任何关联都会丢失.


如果您希望编译器遵循您的逻辑,您需要以这样的通用性显式地表示操作.建议的方法见microsoft/TypeScript#47109. 从你的MyMap类型开始,

type MyMap = {
  type1: (() => void)[]
  type2: ((data: string) => void)[]
}
const map: MyMap = { type1: [], type2: [] };

我们可以创建该类型的"基本"版本,仅与数组的元素相对应:

type MyMapBase = { [K in keyof MyMap]: MyMap[K][number] }
    
/* type MyMapBase = {
    type1: () => void;
    type2: (data: string) => void;
} */

然后我们可以将addToMap()写为一个在MyMapBase上显式操作的泛型函数:

function addToMap<K extends keyof MyMap>(k: K, cb: MyMapBase[K]): void {
  const m: { [K in keyof MyMap]: MyMapBase[K][] } = map;
  m[k].push(cb);
}

这里,我们刚刚将map赋给了mapped type{[K in keyof MyMap]: MyMapBase[K][]}的变量m. 编译器对赋值很满意,因为它必须单独验证每个属性,而且它工作正常. 但是现在m被明确地写为一个一般抽象超过MyMapBase的类型. 所以m[k]属于单一泛型类型MyMapBase[K][].因此,你可以很容易地将类型MyMapBase[K]的值添加到它上.


请注意,如果我们一开始就用startedMyMapBase,这会更直观,因为这是您抽象的实际底层类型.但如果你已经在某个地方定义了MyMap,这里的代码显示你仍然可以从中计算出所需的东西.

Playground link to code

Typescript相关问答推荐

如何根据参数的值缩小函数内的签名范围?

Angular 17 -如何在for循环内创建一些加载?

如果存在ts—mock—imports,我们是否需要typescpt中的IoC容器

如何推断计算逻辑的类型

泛型类型联合将参数转换为Never

在类型脚本中的泛型类上扩展的可选参数

以Angular 自定义快捷菜单

如何在ANGLE中注册自定义验证器

TypeScrip:从对象中提取和处理特定类型的键

重载函数的T helper参数

如何从输入中删除值0

与toast.error一起react 中的Async TUNK错误消息

使用ngfor循环时,数据未绑定到表

声明遵循映射类型的接口

递归生成常量树形 struct 的键控类型

如何从抽象类的静态方法创建子类的实例?

映射类型不是数组类型

TypeScript:方法获胜';t接受较窄类型作为参数

如何提取具有索引签名的类型中定义的键

通过辅助函数获取嵌套属性时保留类型