2016-03-15 17 views
19

Mam następującą funkcję:Uzyskanie typ zwracanej przez funkcję

function test(): number { 
    return 42; 
} 

mogę uzyskać rodzaj funkcji za pomocą typeof:

type t = typeof test; 

Tutaj t będzie () => number.

Czy istnieje sposób na uzyskanie typu zwrotu funkcji? Chciałbym t być number zamiast () => number.

+1

Nie, nie tak czy inaczej z typeof. typeof zwróci tylko "funkcję" (* moja obudowa może być niepoprawna *). – Igor

+0

Byłoby wspaniale, gdyby stało się to możliwe. Czasami definiujesz typy, kiedy wypychasz je z funkcji i nie ma (miłego) sposobu na uchwycenie tej struktury. –

Odpowiedz

20

EDIT

na Typescript 2.8 (nie wypuszczać poza nocnych kanałów aktualnie) jest to możliwe urzędowo ReturnType<T>.

type T10 = ReturnType<() => string>; // string 
type T11 = ReturnType<(s: string) => void>; // void 
type T12 = ReturnType<(<T>() => T)>; // {} 
type T13 = ReturnType<(<T extends U, U extends number[]>() => T)>; // number[] 

Aby uzyskać szczegółowe informacje, patrz here.

TypeScript jest niesamowity!


Old-school włamać

odpowiedź Ryana już nie działa, niestety. Ale zmodyfikowałem go za pomocą hack'a, z którego jestem niesłychanie zadowolony. Oto:

const fnReturnType = (false as true) && fn(); 

Działa poprzez odlewanie fałszywe dosłownym wartość prawda, tak, że system typu myśli, zwracana jest wartość typu funkcji, ale jeśli faktycznie prowadzą kodu, zwarcie na false .

TypeScript jest najlepszy. : D

+8

Jezu, to jest okropne. –

+0

dla przepływu: const DUMMY = ((null: any): Object) && fn() typ eksportu Typ = typeof DUMMY –

+0

jakikolwiek pomysł dlaczego instaluję 2.8 i spróbuj uzyskać następujący błąd: "Nie można znaleźć nazwy" ReturnType "? używając vscode – NSjonas

4

Nie ma sposobu, aby to zrobić (patrz: https://github.com/Microsoft/TypeScript/issues/6606, aby śledzić element pracy, dodając to).

Częstym obejście jest napisać coś takiego:

var dummy = false && test(); 
type t2 = typeof dummy; 
+1

Nie sądzę, aby to obejście już działa - prawdopodobnie z powodu analizy typu opartej na przepływie sterowania. – JKillian

+1

@JKillian masz rację, proponowany przykład już nie działa, chociaż możesz łatwo oszukać TypeScript z '! 1' zamiast' false' - http://www.typescriptlang.org/play/#src=function%20f()% 3A% 20 łańcuch% 20% 7B% 0A% 20return% 20% 2742% 27% 0A% 7D% 0A% 0Alet% 20a% 20% 3D% 20! 1% 20% 26% 26% 20f()% 0Alet % 20b% 3A% 20typeof% 20a% 20% 3D% 20% 27abc% 27 – DanielM

+0

Zauważyłem, że muszę użyć '(false as true) && test()', aby uruchomić go ... – Aaron

1

nie ma mowy o pobieraniu typ zwracany funkcji bez wykonywania go smutkiem. Dzieje się tak, ponieważ gdy TypeScript zostanie wkompilowany w JS, tracisz wszystkie informacje dotyczące typu.

+1

Chociaż jest to technicznie prawdą, że TS traci informacje o typie w czasie wykonywania, fakt ten nie jest istotny, ponieważ OP chciałby określić typ funkcji podczas kompilacji. Jest to tym bardziej oczywiste, że w TypeScript zastosowano 'ReturnType '. – thedayturns

1

Jeśli funkcja w pytaniu jest metodą klasy zdefiniowane przez użytkownika, można użyć method decorators w połączeniu z Reflect Metadata określić typ zwracanej (konstruktor) w wykonywania (a wraz z nim zrobić, jak uważasz) .

Na przykład, można go zalogować do konsoli:

function logReturnType(
    target: Object | Function, 
    key: string, 
    descriptor: PropertyDescriptor 
): PropertyDescriptor | void { 
    var returnType = Reflect.getMetadata("design:returntype", target, key); 

    console.log(returnType); 
} 

Wystarczy zatrzasnąć tej metody dekorator na metodzie wyboru i trzeba dokładnie odwołanie do funkcji konstruktora obiektu, który jest rzekomo zwróconego z wywołania metody.

class TestClass { 
    @logReturnType // logs Number (a string representation) 
    public test(): number { 
     return 42; 
    } 
} 

Istnieje kilka znaczących ograniczenia tego podejścia, jednak:

  • trzeba jednoznacznie określić typ zwracanej na metodzie zdobione jako takie, inaczej dostaniesz niezdefiniowany od Reflect.getMetadata,
  • można odwoływać się tylko do rzeczywistych typów, które istnieją również po kompilacji; czyli nie ma interfejsy lub leki generyczne

Ponadto musisz określić następujące argumenty wiersza poleceń dla kompilatora maszynopis, ponieważ obie dekoratorzy i odzwierciedlają metadane są funkcje eksperymentalne w momencie pisania tego posta:

--emitDecoratorMetadata --experimentalDecorators 
0

wymyśliłem następujące elementy, które wydaje się działać dobrze:

function returnType<A, B, Z>(fn: (a: A, b: B) => Z): Z 
function returnType<A, Z>(fn: (a: A) => Z): Z 
function returnType<Z>(fn:() => Z): Z 
function returnType(): any { 
    throw "Nooooo" 
} 

function complicated(value: number): { kind: 'complicated', value: number } { 
    return { kind: 'complicated', value: value } 
} 

const dummy = (false as true) && returnType(complicated) 
type Z = typeof dummy 
+1

Nie sądzę, że argumenty funkcji muszą być ogólne, ponieważ i tak je wyrzucasz. 'fn: (... args: any []) => Z' to wszystko, czego potrzebujesz. – Aaron

3

Poniższy kod działa bez wykonywania funkcji. To z biblioteki reagować-Redux-maszynopis (https://github.com/alexzywiak/react-redux-typescript/blob/master/utils/redux/typeUtils.ts)

interface Func<T> { 
    ([...args]: any, args2?: any): T; 
} 
export function returnType<T>(func: Func<T>) { 
    return {} as T; 
} 


function mapDispatchToProps(dispatch: RootDispatch, props:OwnProps) { 
    return { 
    onFinished() { 
     dispatch(action(props.id)); 
    } 
    } 
} 

const dispatchGeneric = returnType(mapDispatchToProps); 
type DispatchProps = typeof dispatchGeneric; 
1

Wariant na niektóre z poprzednich odpowiedzi, które używam, która działa w strictNullChecks i ukrywa gimnastyka wnioskowania trochę:

function getReturnType<R>(fn: (...args: any[]) => R): R { 
    return {} as R; 
} 

Zastosowanie:

function foo() { 
    return { 
    name: "", 
    bar(s: string) { // doesn't have to be shorthand, could be `bar: barFn` 
     return 123; 
    } 
    } 
} 

const _fooReturnType = getReturnType(foo); 
export type Foo = typeof _fooReturnType; // type Foo = { name: string; bar(s: string): number; } 

to robi wywołanie funkcji getReturnType, ale nie wywołuje funkcji. Użytkownik może uniemożliwić wywołanie getReturnType przy użyciu (false as true) && getReturnType(foo), ale IMO sprawia, że ​​jest to bardziej mylące.

Po prostu użyłem tej metody z pewnym regexp find/replace, aby przeprowadzić migrację starego projektu Angular 1.x, który miał ~ 1500 funkcji fabrycznych napisanych w ten sposób, oryginalnie w JS, i dodał do wszystkich rodzajów plików etc etc. niesamowity złamany kod znajdzie. :)

Powiązane problemy