简体   繁体   English

如何在 Map 中使用 `(K | V)[][]` 作为 `ReadonlyArray<[K, V]>`?

[英]How to use `(K | V)[][]` as `ReadonlyArray<[K, V]>` in a Map?

I would like to use a (K | V)[][] as ReadonlyArray<[K, V]> in a Map constructor.我想在 Map 构造函数中使用(K | V)[][]作为ReadonlyArray<[K, V]> Lets say that our V is an interface IItem and K a basic number .假设我们的 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]));

By default, typescript will perceive the type of items as : (number | IItem)[][] .默认情况下,typescript 将感知项目类型为: (number | IItem)[][] This will not work and throw an error:这将不起作用并引发错误:

Argument of type '(number | { key: number; val: string; })[][]' is not assignable to parameter of type 'ReadonlyArray<[{}, {}]>'. '(number | { key: number; val: string; })[][]' 类型的参数不可分配给'ReadonlyArray<[{}, {}]>' 类型的参数。
Type '(number | { key: number; val: string; })[]' is missing the following properties from type '[{}, {}]': 0, 1类型 '(number | { key: number; val: string; })[]' 缺少来自类型 '[{}, {}]' 的以下属性:0, 1

Luckily for us we can force the type to Array<[number, IItem]> to please Map .幸运的是,我们可以强制类型为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]));

This work as expected.这项工作符合预期。 Alright, lets move to my problem.好吧,让我们转移到我的问题。 What if we cannot force the type?如果我们不能强制类型怎么办?

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]));

This will not work because here (number | { key: number; val: string; })[][] is not Array<[number, IItem]> .这不起作用,因为这里(number | { key: number; val: string; })[][]不是Array<[number, IItem]> So, how to use (K | V)[][] as ReadonlyArray<[K, V]> in a Map?那么,如何在 Map 中将(K | V)[][]用作ReadonlyArray<[K, V]>

You can try all the code online您可以在线尝试所有代码

I read both How to define Map with correlation between a key type and a value type, while they both are unions and Typescript Map<enum, set<enum>> "No overload matches this call", but I don't get why?我阅读了如何定义 Map 与键类型和值类型之间的相关性,虽然它们都是联合Typescript Map<enum, set<enum>> “没有重载匹配此调用”,但我不明白为什么? without finding the solution.没有找到解决方案。 I may missed the solution.我可能错过了解决方案。

I also read the MDN entry on Map which say that it should work as expected and if you try in vanilla JS it does work as expected:我还阅读了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])); 

Try it online! 在线尝试!

I feel that there may be a better way to achieve this but you can force the type by casting in the map routine:我觉得可能有更好的方法来实现这一点,但您可以通过在map例程中强制转换来强制类型:

const arr = [
    { key: 3, val: 'bar' },
    { key: 5, val: 'world' }
];
const result = arr.map(i => ([i.key, i] as [number, IItem])); // here come the trick
const ze_map3 = new Map(result);
console.log(JSON.stringify([...ze_map3]));

The as keyword is a Type Assertion which tells tsc to consider the object as another type (ie our [K, V] ) than the type the compiler infers the object to be (ie (K | V)[] ). as关键字是一个类型断言,它告诉 tsc 将 object 视为另一种类型(即我们的[K, V] ),而不是编译器推断 object 的类型(即(K | V)[] )。

The good news is that the as [K, V] is totally transparent in the js output:好消息是as [K, V]在 js output 中是完全透明的:

"use strict";
const arr = [
    { key: 3, val: 'bar' },
    { key: 5, val: 'world' }
];
const result = arr.map(i => [i.key, i]);
const ze_map3 = new Map(result);
console.log(JSON.stringify([...ze_map3]));

Try it Online 在线试用

source资源

The problem is the body of the .map callback.问题是.map回调的主体。 Whenever a function returns an array, Typescript will interpret the return type as a normal array type rather than a tuple type .每当 function 返回一个数组时, Typescript 会将返回类型解释为普通数组类型而不是元组类型

Avoid as避免as

The as assertion will work, but it can be dangerous because it tells Typescript "consider this type as [number, IItem] even if it really isn't" rather than telling Typescript "I expect this type to be [number, IItem] so please make sure that it is." as断言会起作用,但它可能很危险,因为它告诉 Typescript “即使它确实不是,也将这种类型视为[number, IItem] ”,而不是告诉 Typescript “我希望这种类型是[number, IItem]所以请确保它是。” In your situation the as is not required because it really is type [number, IItem] .在您的情况下, as不是必需的,因为它确实是类型[number, IItem] We just need to get typescript to interpret it that way.我们只需要让 typescript 以这种方式解释它。

Set a Generic设置泛型

The easiest way to do this is by setting the generic type parameter on the .map function.最简单的方法是在.map function 上设置泛型类型参数。 The generic variable here sets the return type of the callback, so we set it to [number, IItem] .此处的泛型变量设置回调的返回类型,因此我们将其设置为[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 :)

Externalize the Callback外部化回调

Another approach is to define your map callback i => [i.key, i] as its own function so that you can annotate the return type.另一种方法是将您的 map 回调i => [i.key, i]定义为它自己的 function 以便您可以注释返回类型。

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 :)

Typescript Playground Link Typescript 游乐场链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM