3 votes

TypeScript : Type générique des paramètres rest à utiliser dans le type de retour

J'ai une fonction pour une composition ZIP en TypeScript qui prend n'importe quel nombre de tableaux en paramètres et devrait renvoyer un nouveau tableau avec un élément de chaque tableau d'entrée. Le problème est le type du tableau retourné.

La fonction devrait avoir le même type de retour que la fonction zip en python ou dans lodash.

function zipComp(...arrs: T[][]) { ... }
zipComp([1, 2], [false, true]) // attendu: [number, boolean][], réel: unknown[]
zipComp(["a", "b", "c"], [1, 2, 3], [{}, {}, {}]) // attendu: [string, number, object][], réel: unknown[]

J'ai regardé les types de lodash pour voir comment ils ont fait.

// à partir de lodash (array.d.ts)
zip(arrays1: List, arrays2: List): Array<[T1, T2]>;
zip(arrays1: List, arrays2: List, arrays3: List): Array<[T1, T2, T3]>;
zip(arrays1: List, arrays2: List, arrays3: List, arrays4: List): Array<[T1, T2, T3, T4]>;
zip(arrays1: List, arrays2: List, arrays3: List, arrays4: List, arrays5: List): Array<[T1, T2, T3, T4, T5]>;
zip(...arrays: Array>): Array>; // => ici est le problème

Cette implémentation fonctionne d'une certaine manière, mais a le même problème s'il y a plus de 5 tableaux d'entrée.

Y a-t-il un moyen d'écrire ce type d'arguments de manière générale, sans avoir de cas pour n'importe quel nombre d'arguments?

2voto

jcalz Points 30410

Vous pourriez rendre la fonction générique en T, le type d'élément du tableau de sortie (donc si le tableau produit un [X, Y, Z][] alors T est [X, Y, Z]) et ensuite donner à la fonction un paramètre rest qui est d'un type tuple/tableau mappé sur T:

déclarez la fonction zipComp(
    ...args: { [I in keyof T]: T[I][] }
): T[]

Dans le type mappé { [I in keyof T]: T[I][] }, pour chaque index de type numérique I dans le tableau T, le type d'élément T[I] à cet index est mappé sur un tableau de ces éléments T[I][]. Donc si T est [X, Y, Z], alors le type mappé est [X[], Y[], Z[]].

Notez que parce que le type mappé en question est homomorphe (comme décrit dans Que signifie "homomorphic mapped type" ?) le compilateur est capable d'inférer T à partir de la valeur passée pour args. (Si ce n'était pas vrai, alors l'inférence pourrait échouer et nous devrions réécrire la signature de l'appel.)

Notez aussi que les types d'arguments rest tendent à être inférés comme des types tuple par le compilateur au lieu de simples types de tableau non ordonnés, ce que nous voulons.

Tout cela signifie que, si args est de type [X[], Y[], Z[]] alors la sortie sera de type [X, Y, Z][].


Ou du moins ça devrait l'être; essayons :

const nb = zipComp([1, 2], [false, true]);
// const nb: [number, boolean][];

const sno = zipComp(["a", "b", "c"], [1, 2, 3], [{}, {}, {}]);
// const sno: [string, number, {}][];

const hmm = zipComp([""], [1], [true], [new Date()], [null], 
   [undefined], [Symbol()], [() => 2]);
// const hmm: [string, number, boolean, Date, null, 
//   undefined, symbol, () => 2][]

Cela semble bon ! Le compilateur produit joyeusement le type de sortie à partir du type d'entrée même avec des entrées relativement longues.

Lien vers le playground du code

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X