¿Cuál es la sintaxis para las funciones de flecha mecanografiada con genéricos?


194

El manual mecanografiado actualmente no tiene nada sobre las funciones de flecha. Las funciones normales se pueden escribir genéricamente con esta sintaxis: ejemplo:

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

¿Cuál es la sintaxis para las funciones de flecha?

Respuestas:


238

El ejemplo completo que explica la sintaxis a la que hace referencia Robin ... me lo trajo a casa:

Funciones genéricas

Algo como lo siguiente funciona bien:

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

Sin embargo, el uso de una función genérica de flecha no:

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

Solución alternativa: el uso se extiende en el parámetro genérico para indicar al compilador que es un genérico, por ejemplo:

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

1
¿sería posible declarar algún tipo genérico predefinido para const foo? es decir type GenericFun<T, A=T> = (payload: A) => T;, ¿hacer const foo: GenericFunaún genérico sin proporcionar un Ttipo?
— ciekawy

@ciekawy, AFAIK, no lo es, y creo que tiene sentido. Los genéricos tratan de permitir tipos de orden superior en cierto sentido. GenericFun<T, A=T>define un conjunto que incluye otros tipos GenericFun<number> y GenericFun<string>, entre otros. const foo: GenericFunSin embargo, permitir que los tipos genéricos constituyan una anotación de tipo directamente, por ejemplo, const , tenderá a erosionar la seguridad estática que de otro modo se proporcionaría al requerir especificidad, por ejemplo, const foo: GenericFun<string>oconst foo: GenericFun<number>
— jbmilgrom

77
Su segundo ejemplo es solo un error en un .tsxarchivo (TypeScript + JSX). En un .tsarchivo funciona bien, como puede ver en el campo de juegos TypeScript .
— danvk

49
Los compiladores de mecanografiado más nuevos también admiten la coma final const foo = <T,>(x: T) => x;para evitar la ambigüedad de JSX.
— Thomas

1
@danvk Vale la pena señalar que esto solo es válido para aquellos que han prohibido JSX en archivos TS: si un proyecto está configurado para permitir JSX en archivos TS, aún necesitará la "extensión" o la coma final
— Matt

65

El ejemplo anterior me pareció confuso. Estoy usando React y JSX, así que creo que complicó el escenario.

Recibí una aclaración de TypeScript Deep Dive , que establece los genéricos de flecha:

Solución alternativa: el uso se extiende en el parámetro genérico para insinuar al compilador que es genérico, esto vino de un ejemplo más simple que me ayudó.

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

9
"T extiende cualquiera" apoyaría entonces el vacío.
— Mohamed Fakhreddine

48

Si está en un .tsxarchivo, no puede simplemente escribir <T>, pero esto funciona:

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

A diferencia del extends {}truco, este truco al menos conserva la intención.


¿Planean arreglar este comportamiento?
— Alexander Kim

Supongo que no se puede hacer mucho al respecto ... las sintaxis genéricas JSX y mecanografiada simplemente chocan aquí ...
— mb21

1
Fantástico: esta es la mejor respuesta: ¡funciona perfectamente y no altera el comportamiento en absoluto!
— Geoff Davids

1
¿Qué pasa con el tipo de parámetro de tipo predeterminado? const foo = <T = any,>(x: T) => xno funciona ...
— Mikhail Vasin

34

La especificación del lenguaje dice en p.64f

Una construcción de la forma <T> (...) => {...} podría analizarse como una expresión de función de flecha con un parámetro de tipo o una aserción de tipo aplicada a una función de flecha sin parámetro de tipo. Se resuelve como el anterior [..]

ejemplo:

// 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

mientras que la respuesta popular con extends {}funciona y es mejor que extends any, obliga Ta ser un objeto

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

para evitar esto y preservar la seguridad de tipos, puede usar extends unknownen su lugar

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

1

Para usar este tipo de declaración:

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

Permite definir accesorios adicionales para su función si alguna vez lo necesita y, en algunos casos, ayuda a mantener el cuerpo de la función más limpio de la definición genérica.

Si no necesita accesorios adicionales (tipo de espacio de nombres), puede simplificarse para:

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

1

tan tarde, pero con ES6 no es necesario extenderlo todavía funciona para mí ... :)

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)

Esto no funciona para mí, tengo que añadir una coma, así: <T, >. como se describe en el comentario @Thomas bajo @jbmilgrom' respuesta
— Apolo

Debe leer las otras soluciones antes de publicar una. Su solución ya ha sido publicada con explicación. Funciona solo dentro de un archivo .ts, no en un archivo .tsx.
— Isaac Pak
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.