Який синтаксис функцій стрілок Typescript із дженериками?


192

У посібнику з машинопису в даний час немає нічого із функцій стрілки. Нормальні функції можна загалом набрати за допомогою цього синтаксису: приклад:

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

Що таке синтаксис функцій стрілки?

Відповіді:


235

Повний приклад, що пояснює синтаксис, на який посилається Робін ... приніс його додому:

Родові функції

Щось подібне працює чудово:

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

Однак використання загальної функції зі стрілкою не буде:

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

Обхід: Використовуйте розширення на загальний параметр, щоб натякнути компілятору, що він є загальним, наприклад:

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 .
— danvk

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

1
@danvk Варто зазначити, що це справедливо лише для тих, хто заборонив JSX у TS-файлах - якщо проект налаштований на дозвіл JSX у TS-файлах, вам все одно знадобляться "розширення" або кінцева кома
— Метт

65

Я знайшов приклад вище заплутаним. Я використовую React і JSX, тому я думаю, що це ускладнило сценарій.

Я отримав роз'яснення з TypeScript Deep Dive , де вказано дженерики стрілок:

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

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

9
Тоді "T подовжує будь-який" буде підтримувати ніщо.
— Мохамед Фахреддіне

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;

1

Я повинен використовувати такий тип декларації:

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

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

Якщо вам не потрібні додаткові реквізити (подібний предмет простору імен), його можна спростити до:

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

1

так пізно, але з 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
— аполлон

Ви повинні прочитати інші рішення, перш ніж публікувати одне. Ваше рішення вже розміщено з поясненнями. Він працює лише всередині .ts-файлу, а не .tsx-файлу.
— Ісаак Пак
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.