Bir işlevin dönüş türünü elde etmek


101

Aşağıdaki işleve sahibim:

function test(): number {
    return 42;
}

İşlevin türünü şu şekilde elde edebilirim typeof:

type t = typeof test;

Burada tolacak () => number.

İşlevin dönüş türünü elde etmenin bir yolu var mı? Ben istiyorum tolmak numberyerine () => number.


2
Hayır, en azından typeof ile değil. typeof yalnızca "function" döndürür (büyük / küçük harf durumum yanlış olabilir ).
Igor

Bu mümkün olsaydı harika olurdu. Bazen türleri bir işlevden çıkarırken tanımlarsınız ve bu yapıyı yakalamanın (güzel) bir yolu yoktur.
Jørgen Tvedt

Yanıtlar:


164

DÜZENLE

TypeScript 2.8'den itibaren bu resmi olarak mümkündür 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[]

Ayrıntılar için buraya bakın.

TypeScript harika!


Eski usul hack

Ryan'ın cevabı maalesef artık işe yaramıyor. Ama mantıksız bir şekilde mutlu olduğum bir hack ile değiştirdim. Seyretmek:

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

Bu, yanlış değerini true değerine çevirerek çalışır, böylece tür sistemi, dönüş değerinin işlevin türü olduğunu düşünür, ancak kodu gerçekten çalıştırdığınızda, yanlışta kısa devre yapar.

TypeScript en iyisidir. : D


53
Tanrım, bu korkunç.
John Weisz

akış için: const DUMMY = ((null: herhangi): Object) && fn () dışa aktarma türü Tür = typeof DUMMY
David Xu

bir fikrin neden zaman ben 2.8 yükleyip aşağıdaki hatayı alıyorum denemek: Cannot find name 'ReturnType'? vscode kullanma
NSjonas

1
@NSjonas vscode'un diğer sürümü kullanmasını söylemeniz gerekir. Sanırım sağ alttaki durum çubuğunda.
Meirion Hughes

12
Yapmam gereken ReturnType<typeof fn>(cevap ima ReturnType<fn>yeterlidir).
spacek33z

49

TypeScript 2.8'deki en kolay yol:

const foo = (): FooReturnType => {
}

type returnType = ReturnType<typeof foo>;
// returnType = FooReturnType

5

Aşağıdaki kod, işlevi çalıştırmadan çalışır. React-redux-typescript kitaplığından ( 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;

4

Bunu yapmanın bir yolu yoktur (bunu ekleyen iş öğesi izleme için bkz. Https://github.com/Microsoft/TypeScript/issues/6606 ).

Yaygın bir çözüm şuna benzer bir şey yazmaktır:

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

3
Bu geçici çözümün artık işe yarayacağını sanmıyorum - muhtemelen kontrol akışı tabanlı tip analizi nedeniyle.
JKillian

3
Haklısın @JKillian kolayca ile typescript kandırmak rağmen, örneğin, artık işleri önerdi !1yerine false- typescriptlang.org/play/...
DanielM

3

Düzenleme: TS 2.8 ile artık buna gerek yok! ReturnType<F>dönüş türünü verir. Kabul edilen cevaba bakın.


Kullandığım önceki cevaplardan bazılarının bir varyantı strictNullChecks, çıkarım jimnastiğinde işe yarayan ve biraz gizleyen bir varyant :

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

Kullanım:

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; }

Bu mu diyoruz getReturnTypefonksiyonu, ancak vermez orijinal işlevini çağırın. Sen olabilir önlemek getReturnTypekullanarak çağrı (false as true) && getReturnType(foo)ama IMO bu sadece daha kafa karıştırıcı hale getirir.

Bu yöntemi, orijinal olarak JS'de ~ 1500 fabrika işlevi bu şekilde yazılmış ve Fooetc türlerini tüm kullanımlara ekleyen eski bir Angular 1.x projesini taşımak için bazı regexp bul / değiştir ile kullandım. bulacak. :)


Teşekkürler! Üzücü olsa da, bu miktarda laf kalabalığı en azından işe yarar!
ecmanaut

@ecmanaut Artık buna ihtiyacımız yok! TS 2.8'de ReturnType<F>bir fonksiyon dönüş türü elde etmek için kullanabilirsiniz . :)
Aaron Beall

Buradaki cevaplar, sorunun örnek alt tabakasından büyük ölçüde farklıdır ve bu, fonksiyonun döndürülen türü olan bir türün nasıl üretileceğini anlamayı zorlaştırır test. Bu cevap, sadece yeniden adlandırmak testiçin foo(ve hiç şüphesiz tekmeler için daha karmaşık bir dönüş türü üretmek için) daha kullanışlı olanlardan biriydi . Orijinal örneğe nasıl uygulanacağını zaten biliyorsanız, hem kabul edilen yanıt hem de yorumunuz muhtemelen harikadır. (Burada gece geç saat oldu ve tam bir ReturnType örneğinin sözdiziminin nasıl görüneceği bana hemen belli olmuyor.)
ecmanaut

1

Söz konusu işlev, kullanıcı tanımlı bir sınıfın bir yöntemiyse , çalışma zamanında dönüş türünü (yapıcı işlevi) belirlemek için (ve bununla uygun gördüğünüz gibi yapın) yöntem dekoratörlerini Reflect Metadata ile birlikte kullanabilirsiniz .

Örneğin, konsola kaydedebilirsiniz:

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

    console.log(returnType);
}

Sadece bu yöntem dekoratörünü seçtiğiniz bir yönteme yapıştırın ve sözde yöntem çağrısından döndürülen nesnenin yapıcı işlevine tam referansa sahip olursunuz.

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

Bununla birlikte, bu yaklaşımın birkaç önemli sınırlaması vardır:

  • Bu şekilde dekore edilmiş bir yöntemde dönüş türünü açıkça tanımlamanız gerekir, aksi takdirde tanımsız olursunuz Reflect.getMetadata,
  • yalnızca derlemeden sonra var olan gerçek türlere başvurabilirsiniz; yani, arayüz veya jenerik yok

Ayrıca, daktilo derleyicisi için aşağıdaki komut satırı bağımsız değişkenlerini belirtmeniz gerekir, çünkü hem dekoratörler hem de meta verileri yansıtmak, bu yazıyı yazarken deneysel özelliklerdir:

--emitDecoratorMetadata --experimentalDecorators

0

Ne yazık ki çalıştırmadan işlevin dönüş türünü almanın bir yolu yoktur. Bunun nedeni, TypeScript'in JS'ye derlendiğinde, türle ilgili tüm bilgileri kaybetmenizdir.


3
TS'nin çalışma zamanı sırasında tür bilgilerini kaybettiği teknik olarak doğru olsa da, bu gerçek ilgili değildir çünkü OP derleme zamanında işlevin türünü belirlemek ister. Bu, ReturnType<T>TypeScript'te uygulanmış olan şimdi daha da belirgindir .
thedayturns

0

Aşağıdakileri buldum, bu güzel çalışıyor gibi görünüyor:

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
Zaten onları çöpe attığınızdan, işlev argümanlarının genel olması gerektiğini düşünmüyorum. fn: (...args: any[]) => Ztüm ihtiyacın olan.
Aaron Beall
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.