Memperoleh tipe kembalian dari suatu fungsi

107

Saya memiliki fungsi berikut:

function test(): number {
    return 42;
}

Saya bisa mendapatkan jenis fungsinya dengan menggunakan typeof:

type t = typeof test;

Di sini, takan () => number.

Apakah ada cara untuk mendapatkan tipe kembalian dari fungsi tersebut? Saya ingin tmenjadi numbergantinya () => number.

membersihkan
sumber
3
Tidak, tidak dengan tipe apapun. typeof hanya akan mengembalikan "fungsi" ( casing saya mungkin salah ).
Igor
Akan sangat bagus jika ini menjadi mungkin. Terkadang Anda menentukan tipe saat Anda mendorongnya keluar dari suatu fungsi, dan tidak ada cara (bagus) untuk menangkap struktur ini.
Jørgen Tvedt

Jawaban:

175

EDIT

Pada TypeScript 2.8, ini secara resmi dimungkinkan dengan 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[]

Lihat permintaan tarik ini ke Microsoft / TypeScript untuk detailnya.

TypeScript luar biasa!


Retas sekolah tua

Sayangnya, jawaban Ryan tidak berfungsi lagi. Tapi saya telah memodifikasinya dengan peretasan yang membuat saya sangat senang. Melihat:

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

Ia bekerja dengan mentransmisikan falseke nilai literal true, sehingga sistem tipe mengira nilai yang dikembalikan adalah jenis fungsinya, tetapi ketika Anda benar-benar menjalankan kode, itu korsleting false.

thedayturns
sumber
56
Astaga, itu mengerikan.
John Weisz
untuk aliran: const DUMMY = ((null: any): Object) && fn () export type Type = typeof DUMMY
David Xu
tahu mengapa ketika saya menginstal 2,8 dan mencoba saya mendapatkan error berikut: Cannot find name 'ReturnType'? menggunakan vscode
NSjonas
1
@NSjonas Anda perlu memberi tahu vscode untuk menggunakan versi lain. Saya pikir hanya status bar di kanan bawah.
Meirion Hughes
13
Saya harus melakukannya ReturnType<typeof fn>(jawabannya ReturnType<fn>sudah cukup).
spacek33z
52

Cara termudah di TypeScript 2.8:

const foo = (): FooReturnType => {
}

type returnType = ReturnType<typeof foo>;
// returnType = FooReturnType
Jaroslav
sumber
5

Kode di bawah ini berfungsi tanpa menjalankan fungsi. Ini dari perpustakaan react-redux-typescript ( 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;
eug
sumber
4

Tidak ada cara untuk melakukan ini (lihat https://github.com/Microsoft/TypeScript/issues/6606 untuk pelacakan item pekerjaan yang menambahkan ini).

Solusi umum adalah menulis sesuatu seperti:

var dummy = false && test();
type t2 = typeof dummy;
Ryan Cavanaugh
sumber
3
Saya tidak berpikir solusi ini akan berfungsi lagi - kemungkinan karena analisis tipe berbasis aliran kontrol.
JKillian
3
@JKillian Anda benar, contoh yang diusulkan tidak lagi berfungsi, meskipun Anda dapat dengan mudah mengelabui TypeScript dengan !1alih - alih false- typescriptlang.org/play/…
DanielM
3

Sunting: Ini tidak diperlukan lagi dengan TS 2.8! ReturnType<F>memberikan tipe pengembalian. Lihat jawaban yang diterima.


Varian pada beberapa jawaban sebelumnya yang saya gunakan, yang berfungsi strictNullChecksdan menyembunyikan sedikit senam inferensi:

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

Pemakaian:

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

Itu memang memanggil getReturnTypefungsi, tapi tidak memanggil fungsi aslinya. Anda dapat mencegah getReturnTypepanggilan menggunakan (false as true) && getReturnType(foo)tetapi IMO ini hanya membuatnya lebih membingungkan.

Saya baru saja menggunakan metode ini dengan beberapa regexp find / replace untuk memigrasi proyek Angular 1.x lama yang memiliki ~ 1500 fungsi pabrik yang ditulis seperti ini, awalnya di JS, dan menambahkan Foojenis dll ke semua penggunaan ... menakjubkan kode yang rusak satu akan menemukan. :)

Aaron Beall
sumber
Terima kasih! Meskipun menyedihkan dibutuhkan jumlah verbiage ini, setidaknya itu berhasil!
ecmanaut
@ecmanaut Kami tidak membutuhkan ini lagi! Di TS 2.8 Anda dapat menggunakan ReturnType<F>untuk mendapatkan tipe pengembalian fungsi. :)
Aaron Beall
Jawaban di sini sangat berbeda dari contoh substrat pertanyaan, sehingga sulit untuk mengetahui cara menghasilkan tipe yang merupakan tipe fungsi yang dikembalikan test. Jawaban ini adalah salah satu yang lebih berguna untuk hanya mengganti nama testmenjadi foo(dan diakui menghasilkan jenis pengembalian yang lebih kompleks, untuk iseng). Jawaban yang diterima dan komentar Anda mungkin bagus jika Anda sudah tahu bagaimana menerapkannya pada contoh aslinya. (Ini sudah larut malam di sini, dan tidak langsung jelas bagi saya seperti apa sintaks dari contoh ReturnType lengkap akan terlihat.)
ecmanaut
1

Jika fungsi tersebut adalah metode kelas yang ditetapkan pengguna, Anda dapat menggunakan metode dekorator dalam conjuction dengan Reflect Metadata untuk menentukan jenis kembali (konstruktor) di runtime (dan dengan itu, lakukan sesuai keinginan Anda).

Misalnya, Anda dapat memasukkannya ke konsol:

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

    console.log(returnType);
}

Cukup pasang dekorator metode ini pada metode pilihan Anda dan Anda memiliki referensi yang tepat ke fungsi konstruktor dari objek yang seharusnya dikembalikan dari pemanggilan metode.

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

Namun, ada beberapa batasan penting untuk pendekatan ini:

  • Anda perlu secara eksplisit mendefinisikan tipe kembalian pada metode yang didekorasi seperti itu, jika tidak Anda akan mendapatkan undefined from Reflect.getMetadata,
  • Anda hanya dapat mereferensikan tipe sebenarnya yang juga ada setelah kompilasi; artinya, tidak ada antarmuka atau obat generik

Selain itu, Anda harus menentukan argumen baris perintah berikut untuk compiler skrip ketikan, karena dekorator dan metadata mencerminkan adalah fitur eksperimental saat menulis posting ini:

--emitDecoratorMetadata --experimentalDecorators
John Weisz
sumber
0

Saya datang dengan yang berikut, yang tampaknya berfungsi dengan baik:

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
Werner de Groot
sumber
1
Menurut saya fungsi args tidak perlu generik, karena Anda tetap membuangnya. fn: (...args: any[]) => Zadalah semua yang Anda butuhkan.
Aaron Beall