首页 > 解决方案 > 如何在 Map 中使用 `(K | V)[][]` 作为 `ReadonlyArray<[K, V]>`?

问题描述

我想在 Map 构造函数中使用(K | V)[][]as 。ReadonlyArray<[K, V]>假设我们的 V 是一个接口IItem,而 K 是一个基本的number

interface IItem {
    key: number;
    val: string;
}

const items = [[1, {key: 1, val: "a"}]] 
const ze_map = new Map<number, IItem>(items);
console.log(JSON.stringify([...ze_map]));

默认情况下,typescript 会将项目的类型视为: (number | IItem)[][]. 这将不起作用并引发错误:

'(number | { key: number; val: string; })[][]' 类型的参数不能分配给'ReadonlyArray<[{}, {}]>' 类型的参数。
类型 '(number | { key: number; val: string; })[]' 缺少来自类型 '[{}, {}]' 的以下属性:0, 1

对我们来说幸运的是,我们可以强制类型Array<[number, IItem]>来取悦Map

const items2 : Array<[number, IItem]> = [[1, {key: 1, val: "a"}]] 
const ze_map2 = new Map<number, IItem>(items2);
console.log(JSON.stringify([...ze_map]));

这项工作符合预期。好吧,让我们转移到我的问题。如果我们不能强制类型怎么办?

const arr = [
    { key: 3, val: 'bar' },
    { key: 5, val: 'world' }
];
const result: Array<[number, IItem]> = arr.map(i => ([i.key, i]));

const ze_map3 = new Map(result);
console.log(JSON.stringify([...ze_map]));

这行不通,因为这里(number | { key: number; val: string; })[][]不是Array<[number, IItem]>。那么,如何在 Map中使用(K | V)[][]as呢?ReadonlyArray<[K, V]>

您可以在线尝试所有代码

我阅读了如何定义具有键类型和值类型之间相关性的 Map,虽然它们都是联合Typescript Map<enum, set<enum>>“没有重载匹配此调用”,但我不明白为什么?没有找到解决方案。我可能错过了解决方案。

我还阅读了Map 上的 MDN 条目,它说它应该按预期工作,如果你在 vanilla JS 中尝试它会按预期工作:

var arr = [
    { key: 3, val: 'bar' },
    { key: 5, val: 'world' }
];

var result = arr.map(i => [i.key, i]);
const ze_map = new Map(result);

console.log(JSON.stringify([...ze_map])); 

在线尝试!

标签: javascripttypescriptdictionary

解决方案


问题是.map回调的主体。每当函数返回一个数组时,Typescript 会将返回类型解释为普通数组类型而不是元组类型

避免as

as断言会起作用,但它可能很危险,因为它告诉 Typescript “[number, IItem]即使它真的不是,也要考虑这种类型”,而不是告诉 Typescript “我希望这种类型是这样[number, IItem],所以请确保它是。” 在您的情况下,as不需要,因为它确实是 type [number, IItem]。我们只需要让 typescript 以这种方式解释它。

设置泛型

最简单的方法是在.map函数上设置泛型类型参数。这里的泛型变量设置回调的返回类型,所以我们设置为[number, IItem].

const result = arr.map<[number, IItem]>(i => [i.key, i]); // result has type [number, IItem][]

const ze_map = new Map(result); // no errors :)

外部化回调

另一种方法是将地图回调定义i => [i.key, i]为自己的函数,以便您可以注释返回类型。

const toTuple = (i: IItem): [number, IItem] => [i.key, i];

const result = arr.map(toTuple); // result has type [number, IItem][]

const ze_map = new Map(result); // no errors :)

打字稿游乐场链接


推荐阅读