簡體   English   中英

可以在 TypeScript 中的元組上使用 Array.prototype.map() 同時保留返回類型中的元組長度嗎?

[英]Possible to use Array.prototype.map() on tuple in TypeScript while preserving tuple length in return type?

我希望如果我在 TypeScript 中對長度為 N 的元組使用內置的map函數,那么返回類型也將是一個長度為 N 的元組(根據傳遞給map的函數,元素的類型可能不同) . 相反,返回類型只是回調函數返回的任何類型的標准可變長度數組。 元組的長度丟失。 我寫了一個自定義函數來做我想做的事,但我想知道是否有更好的方法讓我望而卻步。 我正在努力提高我對 TypeScript 的理解。 我在代碼下方包含了一個 TypeScript Playground 鏈接(使用相同的代碼)。 謝謝你的幫助!

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 };
}

請參閱: TypeScript Playground

請嘗試下一步:


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'],但這是某種向前一步

我很樂意在這里看到其他解決方案。 很有趣的問題)

僅適用於 TS 4.1

更新

有可能創建您想要的類型:

// 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 }));

我不知道為什么,但是在函數內部聲明類型並不是最好的主意。 之后,我就可以在我的筆記本電腦上做燒烤了。

隨意使用Mapped type util 進行類型轉換。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM