javascript - Er det muligt at bruge Array.prototype.map() på en tuple i TypeScript, mens tuple-længden bevares i returtypen?


Jeg håbede, at hvis jeg bruger den indbyggede map-funktion på en tuple af længde N i TypeScript, så ville returtypen også være en tuple af længde N (måske med forskellig type for elementerne afhængigt af den funktion, der gives til map). I stedet er returtypen bare et standard array med variabel længde af hvilken som helst type, som callback-funktionen returnerer. Tuplets længde går tabt. Jeg skrev en brugerdefineret funktion, der gør, hvad jeg ønsker, men jeg spekulerer på, om der er en bedre måde, som undslipper mig. Jeg prøver at forbedre min forståelse af TypeScript. Jeg inkluderede et TypeScript Playground-link under koden (med den samme kode). Tak for din hjælp!

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

Se det på: TypeScript Playground

Svar #1

Prøv venligst næste:

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")[]

Jeg tror ikke, dette er den bedste løsning, fordi du stadig har ('3'|'2'|'1')[] og ikke ['1','2','3'], men dette er en form for skridt fremad

Jeg vil være glad for at se andre løsninger her. Meget interessant problem )

Fungerer kun med T.S 4.1

OPDATERING

Der er en mulighed for at skabe den type, du ønsker, med nogen hjælp fra overbelastning og variadiske tuple-typer

type Json =
    | string
    | number
    | boolean
    | { [prop: string]: Json }
    | Array<Json>

type CustomMap<T extends ReadonlyArray<Json>> = {
    [Index in keyof T]: { elem: T[Index] }
}

function map<Elem extends Json, List extends Elem[], Return>(list: [...List], callback: (value: Elem) => Return): CustomMap<List>
function map<Elem extends Json>(list: Elem[], callback: (elem: Elem) => unknown) {
    return list.map(callback)
}

// const result2: [{
//     elem: 1;
// }, {
//     elem: 2;
// }, {
//     elem: 3;
// }]
const result2 = map([1, 2, 3], elem => ({ elem }));

Legeplads

Relaterede artikler