Qual é a sintaxe das funções de seta Typescript com genéricos?


193

O manual de texto datilografado atualmente não possui nada sobre funções de seta. Funções normais podem ser digitadas genericamente com esta sintaxe: exemplo:

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

Qual é a sintaxe para as funções de seta?

Respostas:


237

O exemplo completo que explica a sintaxe mencionada por Robin ... trouxe para casa:

Funções genéricas

Algo como o seguinte funciona bem:

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

No entanto, o uso de uma função genérica de seta não:

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

Solução alternativa: use extends no parâmetro genérico para sugerir ao compilador que é um genérico, por exemplo:

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

1
seria possível declarar algum tipo genérico predefinido para const foo? ou seja type GenericFun<T, A=T> = (payload: A) => T;, tornar const foo: GenericFunainda genérico sem fornecer um Ttipo?
ciekawy

@ciekawy, AFAIK, não é, e acho que faz sentido. Os genéricos têm a ver com permitir tipos de pedidos mais altos em certo sentido. GenericFun<T, A=T>define um conjunto que inclui outros tipos GenericFun<number> e GenericFun<string>, entre outros. Permitindo tipos genéricos para constituir uma anotação de tipo diretamente - por exemplo, const const foo: GenericFun- no entanto, tende a corroer a segurança estática contrário, exigindo especificidade por exemplo, const foo: GenericFun<string>ouconst foo: GenericFun<number>
jbmilgrom

7
Seu segundo exemplo é apenas um erro em um .tsxarquivo (TypeScript + JSX). Em um .tsarquivo, ele funciona bem, como você pode ver no playground TypeScript .
Danvk 17/01/19

48
Os compiladores datilografados mais recentes também suportam vírgula const foo = <T,>(x: T) => x;à direita para evitar a ambiguidade do JSX.
Thomas

1
@danvk Vale notar isso só vale para aqueles que têm proibido JSX em arquivos TS - se um projeto é configurado para permitir JSX em TS arquivos você ainda vai precisar da "estende" ou a vírgula à direita
Matt

65

Achei o exemplo acima confuso. Estou usando React e JSX, então acho que complicou o cenário.

Eu recebi esclarecimentos do TypeScript Deep Dive , que afirma para os genéricos de seta:

Solução alternativa: o uso estende-se no parâmetro genérico para sugerir ao compilador que ele é genérico; isso veio de um exemplo mais simples que me ajudou.

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

9
"T estende qualquer" suportaria vazio.
Mohamed Fakhreddine

48

Se você está em um .tsxarquivo, não pode simplesmente escrever <T>, mas isso funciona:

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

Ao contrário do extends {}hack, esse hack preserva pelo menos a intenção.


Eles planejam corrigir esse comportamento?
Alexander Kim

Eu acho que não há muito que possa ser feito sobre isso ... os JSX e Dactilografado sintaxes genéricos apenas colidir aqui ..
MB21

1
Fantástico - esta é de longe a melhor resposta: funciona perfeitamente e não altera o comportamento!
Geoff Davids

1
E o tipo de parâmetro do tipo padrão? const foo = <T = any,>(x: T) => xnão funciona ...
Mikhail Vasin

34

A especificação da linguagem diz na p.64f

Uma construção do formato <T> (...) => {...} pode ser analisada como uma expressão de função de seta com um parâmetro de tipo ou uma declaração de tipo aplicada a uma função de seta sem parâmetro de tipo. É resolvido como o antigo [..]

exemplo:

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

enquanto a resposta popular com extends {}obras e é melhor do que extends any, força o Tser um objeto

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

para evitar isso e preservar a segurança-tipo, você pode usar extends unknownem vez

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

1

Eu para usar este tipo de declaração:

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

Ele permite definir adereços adicionais para sua função, se você precisar e, em alguns casos, ajuda a manter o corpo da função mais limpo da definição genérica.

Se você não precisar de objetos adicionais (tipo de espaço para nome), ele poderá ser simplificado para:

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

1

tão tarde, mas com o ES6 não há necessidade, ele ainda funciona para mim .... :)

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)

Isso não funciona para mim, eu tenho que adicionar uma vírgula assim: <T, >. como descrito em @Thomas comment sob @jbmilgrom 'resposta
apollo

Você deve ler as outras soluções antes de postar uma. Sua solução já foi publicada com explicações. Funciona apenas dentro de um arquivo .ts, não de um arquivo .tsx.
Isaac Pak
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.