Каков синтаксис для функций стрелок Typescript с обобщениями?


194

В справочнике по машинописи в настоящее время нет ничего о функциях стрелок. Обычные функции могут быть типизированы в общем случае с помощью следующего синтаксиса: пример:

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

Каков синтаксис для функций стрелок?

Ответы:


238

Полный пример, объясняющий синтаксис, на который ссылается Робин ... принес мне его домой:

Общие функции

Что-то вроде следующего отлично работает:

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

Однако использование универсальной функции со стрелкой не будет:

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

Обходной путь: Используйте extends для универсального параметра, чтобы указать компилятору, что он является универсальным, например:

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

1
Можно ли было бы объявить некоторый предопределенный универсальный тип для const foo? то есть type GenericFun<T, A=T> = (payload: A) => T;тогда сделать const foo: GenericFunеще универсальный без предоставления Tтипа?
ciekawy

@ciekawy, AFAIK, это не так, и я думаю, что это имеет смысл. Дженерики в некотором смысле допускают использование типов более высокого порядка. GenericFun<T, A=T>определяет набор, который включает в себя другие типы GenericFun<number> и GenericFun<string>, среди прочего. Разрешение родовым типам напрямую составлять аннотацию типов - например, const const foo: GenericFun- может привести к ослаблению статической безопасности, в противном случае требуется специфичность, например, const foo: GenericFun<string>илиconst foo: GenericFun<number>
jbmilgrom

7
Ваш второй пример - только ошибка в .tsxфайле (TypeScript + JSX). В .tsфайле это работает нормально, как вы можете видеть на игровой площадке TypeScript .
Данвк

49
Новые компиляторы машинописи также поддерживают запятую, const foo = <T,>(x: T) => x;чтобы обойти неоднозначность JSX.
Томас,

1
@danvk Стоит отметить, что это справедливо только для тех, кто запретил JSX в файлах TS - если проект сконфигурирован для разрешения JSX в файлах TS, вам все равно понадобится "extends" или
Matt

65

Я нашел приведенный выше пример смущающим. Я использую React и JSX, поэтому я думаю, что это усложнило сценарий.

Я получил пояснение от TypeScript Deep Dive , в котором говорится об обобщениях стрелок:

Обходной путь: Используйте параметр extends для универсального параметра, чтобы намекнуть компилятору, что он является универсальным, это произошло из более простого примера, который мне помог.

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

9
«T расширяет любой» будет поддерживать void.
Мохамед Фахреддин

48

Если вы находитесь в .tsxфайле, вы не можете просто написать <T>, но это работает:

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

В отличие от extends {}взлома, этот взлом по крайней мере сохраняет намерение.


Они планируют исправить это поведение?
Александр Ким

Я полагаю, что с этим ничего не поделаешь ... Общие синтаксисы JSX и Typescript просто конфликтуют здесь ...
mb21

1
Фантастика - это, безусловно, лучший ответ: отлично работает и не меняет поведение вообще!
Джефф Дэвидс

1
А как насчет типа параметра по умолчанию? const foo = <T = any,>(x: T) => xне работает ...
Михаил Васин

34

Спецификация языка говорит на стр.64f

Конструкция вида <T> (...) => {...} может быть проанализирована как выражение функции стрелки с параметром типа или утверждением типа, примененным к функции стрелки без параметра типа. Это решено как бывший [..]

пример:

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

в то время как популярный ответ с extends {}работами и лучше extends any, он заставляет Tбыть объектом

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

чтобы избежать этого и сохранить безопасность типов, вы можете использовать extends unknownвместо

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

2

так поздно, но с ES6 нет необходимости продлевать, он все еще работает для меня .... :)

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)

Это не работает для меня, я должен добавить запятую вот так: <T, >. как описано в комментарии @Thomas под ответом @jbmilgrom
apollo

Вы должны прочитать другие решения, прежде чем публиковать одно. Ваше решение уже опубликовано с объяснением. Он работает только внутри файла .ts, а не файла .tsx.
Исаак Пак

1

Я использую этот тип объявления:

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

Это позволяет определить дополнительные реквизиты для вашей функции, если вам когда-либо понадобится, а в некоторых случаях помогает сохранить тело функции чистым от общего определения.

Если вам не нужны дополнительные реквизиты (что-то вроде пространства имен), их можно упростить до:

const identity: <T>(arg: T) => T = (arg) => arg;
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.