使用 TypeScript 在数组上使用 map 返回元组数组

huangapple go评论92阅读模式
英文:

returning an Array of tuples using map on an Array in Typescript

问题

I have an Array const x:number[] = [1,2,3,4,5] and I'm trying to obtain an array of tuples containing the original value in x and a value obtained by mapping a function on it.
My attempt was something like this

pos = x.map(xi => (
    [xi, 3*xi]
))

This returns pos as of type number[][] but I want a [number, number] as a result.
Do you know how to fix this code?

英文:

I have an Array const x:number[] = [1,2,3,4,5] and I'm trying to obtain an array of tuples containing the original value in x and a value obtained by mapping a function on it.
My attempt was something like this

pos = x.map(xi => (
    [xi, 3*xi]
))

This returns pos as of type number[][] but I want a [number, number] as result.
Do you know how to fix this code?

答案1

得分: 1

默认情况下,TypeScript 将会看到像 [xi, 3 * xi] 这样的 数组字面量 并推断其类型为一个无序的 数组类型,长度可以是任意的,比如 number[],而不是一个 元组类型[number, number]。这通常是人们想要的,因为数组比元组更常见。当这种推断不适用时,你需要采取一些措施来告诉编译器做其他的事情。

有多种方法可以做到这一点。如果你明确想要 [number, number],那么最直接的方法是手动为 Array.prototype.map 方法 指定 U 类型参数,该方法的声明如下(在这里声明):

interface Array<T> {
    map<U>(cb: (v: T, i: number, a: T[]) => U, thisArg?: any): U[];
}

像这样:

const x: number[] = [1, 2, 3, 4, 5];
const pos = x.map<[number, number]>(xi => (
   [xi, 3 * xi]
));
// const pos: [number, number][]

但通常情况下,你可以要么手动注释你分配字面量的变量:

const pos = x.map(xi => {
   const ret: [number, number] = [xi, 3 * xi];
   return ret;
});
// const pos: [number, number][]

或者你可以使用 const 断言 来让编译器更倾向于使用字面量类型readonly 元组

const pos = x.map(xi => [xi, 3 * xi] as const);
// const pos: (readonly [number, number])[]

类型 readonly [number, number][number, number] 不完全相同,但根据你的用例,它可能足够使用。

英文:

By default, TypeScript will see an array literal like [xi, 3 * xi] and infer its type to be an unordered array type of arbitrary length like number[], and not a tuple type like [number, number]. That's often what people want, since arrays are more common than tuples. When such inference isn't suitable, you'll need to do something to ask the compiler to do something else.

There are a number of ways to do this. If you explicitly want [number, number], then the most straightforward approach is to manually specify the U type argument for the Array.prototype.map method which is declared like:

interface Array&lt;T&gt; {
    map&lt;U&gt;(cb: (v: T, i: number, a: T[]) =&gt; U, thisArg?: any): U[];
}

Like this:

const x: number[] = [1, 2, 3, 4, 5];
const pos = x.map&lt;[number, number]&gt;(xi =&gt; (
   [xi, 3 * xi]
));
// const pos: [number, number][]

But in general, you can either manually annotate the variable to which you're assigning the literal:

const pos = x.map(xi =&gt; {
   const ret: [number, number] = [xi, 3 * xi];
   return ret;
});
// const pos: [number, number][]

or you could use a const assertion to get the compiler to change its inference algorithm to prefer literal types and readonly tuples:

const pos = x.map(xi =&gt; [xi, 3 * xi] as const);
// const pos: (readonly [number, number])[]

The type readonly [number, number] is not identical to [number, number], but depending on your use case it might suffice.

Playground link to code

huangapple
  • 本文由 发表于 2023年3月21日 01:42:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/75793581-2.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定