可以在TypeScript的元组上使用Array.prototype.map(),同时保留返回类型中的元组长度吗? [英] Possible to use Array.prototype.map() on tuple in TypeScript while preserving tuple length in return type?

查看:111
本文介绍了可以在TypeScript的元组上使用Array.prototype.map(),同时保留返回类型中的元组长度吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望,如果我对TypeScript中长度为N的元组使用内置的 map 函数,则返回类型也将为长度为N的元组(也许元素,具体取决于传递给 map 的函数).相反,返回类型只是回调函数返回的任何类型的标准可变长度数组.元组的长度丢失.我写了一个自定义函数来实现我想要的功能,但是我想知道是否有更好的方法可以让我逃避.我正在努力增进对TypeScript的理解.我在代码下方包含了TypeScript Playground链接(具有相同的代码).感谢您的帮助!

I was hoping that if I use the built-in map function on a tuple of length N in TypeScript then the return type would also be a tuple of length N (perhaps with different type for the elements depending on the function being passed to map). Instead, the return type is just a standard, variable-length array of whatever type the callback function returns. The tuple's length is lost. I wrote a custom function that does what I want, but I'm wondering if there's a better way that is eluding me. I'm trying to improve my understanding of TypeScript. I included a TypeScript Playground link below the code (with the same code). Thanks for your help!

const nums = [1, 2, 3] as const;

// result1 type is string[]
const result1 = nums.map((num) => num.toString());

// so this won't compile
const result2: [string, string, string] = nums.map((num) => num.toString());

// a type assertion would work, but I'd rather not use one...
const result3 = nums.map((num) => num.toString()) as [string, string, string];

// ...because this also compiles yet the type of result4 doesn't match its value
const result4 = nums.map((num) => num.toString()) as [string, boolean, number, symbol, string, number];

// result5 type is [string, string, string]
const result5 = map(nums, (num) => num.toString());

// custom map function that yields the correct return type when used on a tuple
function map<A extends readonly [...any[]], B>(values: A, func: (value: A[number]) => B): { [K in keyof A]: B } {
    return values.map(func) as unknown as { [K in keyof A]: B };
}

请参阅上:打字稿游乐场

推荐答案

请尝试下一个:


type A = readonly [1, 2, 3]

const nums: A = [1, 2, 3];
const toStr = <T extends number>(num: T) => num.toString() as `${T}`

const result2 = nums.map(toStr); // ("3" | "1" | "2")[]

我认为这不是最佳解决方案,因为您仍然具有('3'|'2'|'1')[]而不是['1','2','3'],但这是某种进步

I believe this is not the best solution, because you still have ('3'|'2'|'1')[] and not ['1','2','3'], but this is some kind of step forward

我很高兴在这里看到其他解决方案.非常有趣的问题)

I will happy to see other solutions here. Very interesting problem )

仅适用于T.S 4.1

更新

可以根据需要创建类型:

There is a possibility to create type what you want:

// https://stackoverflow.com/questions/50374908/transform-union-type-to-intersection-type/50375286#50375286
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
    k: infer I
) => void
    ? I
    : never;

// https://github.com/microsoft/TypeScript/issues/13298#issuecomment-468114901
type UnionToOvlds<U> = UnionToIntersection<
    U extends any ? (f: U) => void : never
>;

//https://github.com/microsoft/TypeScript/issues/13298#issuecomment-468114901
type PopUnion<U> = UnionToOvlds<U> extends (a: infer A) => void ? A : never;

// https://stackoverflow.com/questions/53953814/typescript-check-if-a-type-is-a-union#comment-94748994
type IsUnion<T> = [T] extends [UnionToIntersection<T>] ? false : true;

// https://catchts.com/union-array
type UnionToArray<T, A extends unknown[] = []> = IsUnion<T> extends true
    ? UnionToArray<Exclude<T, PopUnion<T>>, [PopUnion<T>, ...A]>
    : [T, ...A];


const map = <A extends readonly [...any[]]>(values: A) =>
    <T,>(func: (value: A[number]) => T) => {
        type Mapped<
            Arr extends Array<unknown>,
            Result extends Array<unknown> = [],
            > = Arr extends []
            ? []
            : Arr extends [infer H]
            ? [...Result, T]
            : Arr extends [infer Head, ...infer Tail]
            ? Mapped<[...Tail], [...Result, T]>
            : Readonly<Result>;

        return (values.map<T>(func)) as Mapped<UnionToArray<A[number]>>
    }

const arr = [1, 2, 3] as const;
type Arr = typeof arr;

const result2 = map([1, 2, 3] as const)<{ elem: Arr[number] }>(elem => ({ elem }));

我不知道为什么,但是在函数内部声明类型不是最好的主意.之后,我就可以在笔记本电脑上烧烤了.

I don't know why, but declaring type inside function is not the best idea. After that, I was able to make BBQ on my laptop.

随意使用 Mapped type util进行类型转换.

Feel free to use Mapped type util for type castings.

这篇关于可以在TypeScript的元组上使用Array.prototype.map(),同时保留返回类型中的元组长度吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆