Quelle est la syntaxe des fonctions fléchées Typescript avec des génériques?


194

Le manuel dactylographié n'a actuellement rien sur les fonctions fléchées. Les fonctions normales peuvent être typées de manière générique avec cette syntaxe: exemple:

function identity<T>(arg: T): T {
    return arg;
}

Quelle est la syntaxe des fonctions fléchées?

Réponses:


238

L'exemple complet expliquant la syntaxe référencée par Robin ... l'a ramené à la maison pour moi:

Fonctions génériques

Quelque chose comme ce qui suit fonctionne bien:

function foo<T>(x: T): T { return x; }

Cependant, l'utilisation d'une fonction générique de flèche ne:

const foo = <T>(x: T) => x; // ERROR : unclosed `T` tag

Solution: utilisez étend le paramètre générique pour indiquer au compilateur qu'il s'agit d'un générique, par exemple:

const foo = <T extends unknown>(x: T) => x;

1
serait-il possible de déclarer un type générique prédéfini pour const foo? c'est à dire type GenericFun<T, A=T> = (payload: A) => T;alors rendre const foo: GenericFunencore générique sans fournir de Ttype?
— ciekawy

@ciekawy, AFAIK, ce n'est pas le cas, et je pense que cela a du sens. Les génériques consistent en un sens à autoriser des types d'ordre supérieur. GenericFun<T, A=T>définit un ensemble qui comprend d' autres types GenericFun<number> et GenericFun<string>, entre autres. Permettre à des types génériques de constituer directement une annotation de type - par exemple const const foo: GenericFun- aurait cependant tendance à éroder la sécurité statique autrement fournie en exigeant une spécificité, par exemple const foo: GenericFun<string>ouconst foo: GenericFun<number>
— jbmilgrom

7
Votre deuxième exemple n'est qu'une erreur dans un .tsxfichier (TypeScript + JSX). Dans un .tsfichier, cela fonctionne très bien, comme vous pouvez le voir sur le terrain de jeu TypeScript .
— danvk

49
Les compilateurs dactylographiés plus récents prennent également en charge la virgule de fin const foo = <T,>(x: T) => x;pour éviter l'ambiguïté JSX.
— Thomas

1
@danvk Il vaut la peine de noter que cela n'est vrai que pour ceux qui ont interdit JSX dans les fichiers TS - si un projet est configuré pour autoriser JSX dans les fichiers TS, vous aurez toujours besoin du "extend" ou de la virgule de fin
— Matt

65

J'ai trouvé l'exemple ci-dessus déroutant. J'utilise React et JSX donc je pense que cela a compliqué le scénario.

J'ai obtenu des éclaircissements de TypeScript Deep Dive , qui indique pour les génériques de flèche:

Solution de contournement: utilisez étend le paramètre générique pour indiquer au compilateur qu'il s'agit d'un générique, cela provient d'un exemple plus simple qui m'a aidé.

    const identity = < T extends {} >(arg: T): T => { return arg; }

9
"T étend tout" supporterait alors le vide.
— Mohamed Fakhreddine

48

Si vous êtes dans un .tsxfichier, vous ne pouvez pas simplement écrire <T>, mais cela fonctionne:

const foo = <T, >(x: T) => x;

Contrairement au extends {}hack, ce hack préserve au moins l'intention.


Prévoient-ils de corriger ce problème?
— Alexander Kim

Je suppose qu'il n'y a pas grand-chose à faire à ce sujet ... les syntaxes génériques JSX et Typescript entrent en conflit ici ..
— mb21

1
Fantastique - c'est de loin la meilleure réponse: fonctionne parfaitement et ne modifie pas du tout le comportement!
— Geoff Davids le

1
Qu'en est-il du type de paramètre de type par défaut? const foo = <T = any,>(x: T) => xne fonctionne pas ...
— Mikhail Vasin

34

La spécification du langage dit à la p.64f

Une construction de la forme <T> (...) => {...} pourrait être analysée comme une expression de fonction de flèche avec un paramètre de type ou une assertion de type appliquée à une fonction de flèche sans paramètre de type. Il est résolu comme l'ancien [..]

exemple:

// helper function needed because Backbone-couchdb's sync does not return a jqxhr
let fetched = <
           R extends Backbone.Collection<any> >(c:R) => {
               return new Promise(function (fulfill, reject) {
                   c.fetch({reset: true, success: fulfill, error: reject})
               });
           };


3

alors que la réponse populaire avec extends {}fonctionne et vaut mieux que extends any, elle oblige Tà être un objet

const foo = <T extends {}>(x: T) => x;

pour éviter cela et préserver la sécurité de type, vous pouvez utiliser à la extends unknownplace

const foo = <T extends unknown>(x: T) => x;

1

J'utilise ce type de déclaration:

const identity: { <T>(arg: T): T } = (arg) => arg;

Il permet de définir des accessoires supplémentaires pour votre fonction si jamais vous en avez besoin et dans certains cas, cela aide à garder le corps de la fonction plus propre de la définition générique.

Si vous n'avez pas besoin des accessoires supplémentaires (une sorte d'espace de noms), cela peut être simplifié en:

const identity: <T>(arg: T) => T = (arg) => arg;

1

si tard, mais avec ES6 pas besoin d'étendre cela fonctionne toujours pour moi .... :)

let getArray = <T>(items: T[]): T[] => {
    return new Array<T>().concat(items)
}

let myNumArr = getArray<number>([100, 200, 300]);
let myStrArr = getArray<string>(["Hello", "World"]);
myNumArr.push(1)
console.log(myNumArr)

Cela ne fonctionne pas pour moi, je dois ajouter une virgule comme ceci: <T, >. comme décrit dans le commentaire de @Thomas sous la réponse de @jbmilgrom
— apollo

Vous devriez lire les autres solutions avant d'en publier une. Votre solution a déjà été publiée avec des explications. Cela ne fonctionne que dans un fichier .ts, pas dans un fichier .tsx.
— Isaac Pak le
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.