Jaka jest składnia funkcji strzałek maszynopisu z rodzajami?


192

Podręcznik maszynopisu obecnie nie ma nic na temat funkcji strzałek. Zwykłe funkcje można ogólnie wpisywać za pomocą tej składni: przykład:

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

Jaka jest składnia funkcji strzałek?

Odpowiedzi:


235

Pełny przykład wyjaśniający składnię, do której odwołuje się Robin ... przyniósł mi ją do domu:

Funkcje ogólne

Coś takiego działa dobrze:

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

Jednak użycie ogólnej funkcji strzałki nie spowoduje:

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

Obejście: użycie rozszerza parametr ogólny, aby zasugerować kompilatorowi, że jest to rodzaj ogólny, np .:

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

1
czy można zadeklarować jakiś predefiniowany rodzaj ogólny const foo? tzn. czy w takim razie type GenericFun<T, A=T> = (payload: A) => T;uczynisz go const foo: GenericFunjeszcze bardziej ogólnym bez podania Ttypu?
ciekawy

@ciekawy, AFAIK, to nie jest i myślę, że to ma sens. Generics to w pewnym sensie dopuszczanie typów wyższych rzędów. GenericFun<T, A=T>definiuje zestaw, który obejmuje inne typy GenericFun<number> i GenericFun<string>, między innymi. Zezwalanie typom rodzajowym na bezpośrednie tworzenie adnotacji typu - np. Const const foo: GenericFun- miałoby jednak tendencję do niszczenia bezpieczeństwa statycznego zapewnianego w inny sposób przez wymaganie specyficzności, np. const foo: GenericFun<string>Lubconst foo: GenericFun<number>
jbmilgrom

7
Drugi przykład to tylko błąd w .tsxpliku (TypeScript + JSX). W .tspliku działa dobrze, jak widać na placu zabaw TypeScript .
danvk

47
Nowsze kompilatory maszynopisów obsługują także przecinek końcowy, const foo = <T,>(x: T) => x;aby uniknąć dwuznaczności JSX.
Thomas

1
@danvk Warto zauważyć, że dotyczy to tylko tych, którzy zabronili JSX w plikach TS - jeśli projekt jest skonfigurowany tak, aby zezwalał na JSX w plikach TS, nadal będziesz potrzebować „rozszerzeń” lub przecinka końcowego
Matt

65

Powyższy przykład jest mylący. Używam React i JSX, więc myślę, że to skomplikowało scenariusz.

Otrzymałem wyjaśnienie od TypeScript Deep Dive , które stanowi dla generycznych strzałek:

Obejście: Zastosowanie rozszerza parametr ogólny, aby zasugerować kompilatorowi, że jest to rodzaj ogólny, pochodzi on z prostszego przykładu, który mi pomógł.

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

9
„T rozszerza dowolne” wspierałoby wówczas pustkę.
Mohamed Fakhreddine

48

Jeśli jesteś w .tsxpliku, nie możesz po prostu pisać <T>, ale to działa:

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

W przeciwieństwie do extends {}hacka, ten hack przynajmniej zachowuje zamiar.


Czy planują naprawić to zachowanie?
Alexander Kim

Wydaje mi się, że niewiele można z tym zrobić ... JSX i typowe składnie maszynowe po prostu kolidują tutaj ...
mb21

1
Fantastycznie - to zdecydowanie najlepsza odpowiedź: działa idealnie i wcale nie zmienia zachowania!
Geoff Davids

1
Co z domyślnym typem parametru typu? const foo = <T = any,>(x: T) => xnie działa ...
Michaił Vasin

34

Specyfikacja języka mówi na str. 64f

Konstrukt w postaci <T> (...) => {...} może zostać przeanalizowany jako wyrażenie funkcji strzałki z parametrem typu lub asercja typu zastosowana do funkcji strzałki bez parametru typu. Jest rozwiązany jako poprzedni [..]

przykład:

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

popularna odpowiedź z extends {}działaniem i jest lepsza niż extends any, ale zmusza ją Tdo bycia przedmiotem

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

Aby tego uniknąć i zachować bezpieczeństwo typu, możesz użyć extends unknownzamiast tego

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

1

I użyć tego typu deklaracji:

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

Pozwala zdefiniować dodatkowe rekwizyty dla twojej funkcji, jeśli zajdzie taka potrzeba, a w niektórych przypadkach pomaga utrzymać ciało funkcji w czystości od definicji ogólnej.

Jeśli nie potrzebujesz dodatkowych rekwizytów (coś w rodzaju przestrzeni nazw), możesz to uprościć:

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

1

tak późno, ale z ES6 nie ma potrzeby przedłużania, nadal działa dla mnie .... :)

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)

To nie działa na mnie, muszę dodać przecinek tak: <T, >. jak opisano w komentarzu @Thomas pod odpowiedzią @jbmilgrom
apollo

Powinieneś przeczytać inne rozwiązania przed opublikowaniem jednego. Twoje rozwiązanie zostało już opublikowane z wyjaśnieniem. Działa tylko w pliku .ts, a nie w pliku .tsx.
Isaac Pak
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.