Typescript: Proposal: Dapatkan jenis ekspresi apa pun dengan typeof

Dibuat pada 25 Jan 2016  ·  157Komentar  ·  Sumber: microsoft/TypeScript

Implementasi Kerja untuk Proposal ini

Cobalah: npm install yortus-typescript-typeof

Lihat perbedaannya: di sini .

Skenario Masalah

Inferensi tipe TypeScript mencakup sebagian besar kasus dengan sangat baik. Namun tetap ada beberapa situasi di mana tidak ada cara yang jelas untuk mereferensikan tipe anonim, meskipun kompiler dapat menyimpulkannya. Beberapa contoh:

Saya memiliki koleksi yang sangat diketik tetapi tipe elemennya anonim/tidak diketahui, bagaimana saya bisa mereferensikan tipe elemen? (#3749)
// Mapping to a complex anonymous type. How to reference the element type?
var data = [1, 2, 3].map(v => ({ raw: v, square: v * v }));
function checkItem(item: typeof data[0] /* ERROR */) {...}

// A statically-typed dictionary. How to reference a property type?
var things = { 'thing-1': 'baz', 'thing-2': 42, ... };
type Thing2Type = typeof things['thing-2']; // ERROR

// A strongly-typed collection with special indexer syntax. How to reference the element type?
var nodes = document.getElementsByTagName('li');
type ItemType = typeof nodes.item(0); // ERROR
Suatu fungsi mengembalikan tipe lokal/anonim/tidak dapat diakses, bagaimana saya bisa mereferensikan tipe pengembalian ini? (#4233, #6179, #6239)
// A factory function that returns an instance of a local class
function myAPIFactory($http: HttpSvc, id: number) {
    class MyAPI {
        constructor(http) {...}
        foo() {...}
        bar() {...}
        static id = id;
    }
    return new MyAPI($http);
}
function augmentAPI(api: MyAPI /* ERROR */) {...}
Saya memiliki antarmuka dengan bentuk anonim yang kompleks, bagaimana saya bisa merujuk ke jenis properti dan sub-propertinya? (#4555, #4640)
// Declare an interface DRY-ly and without introducing extra type names
interface MyInterface {
    prop1: {
        big: {
            complex: {
                anonymous: { type: {} }
            }
        }
    },

    // prop2 shares some structure with prop1
    prop2: typeof MyInterface.prop1.big.complex; // ERROR
}

Mengapa kita perlu Referensi Jenis Anonim/Tersimpul?

Salah satu contohnya adalah mendeklarasikan fungsi yang menggunakan tipe anonim sebagai parameter. Kita perlu mereferensikan tipe entah bagaimana dalam anotasi tipe parameter, jika tidak, parameter harus diketik sebagai any .

Solusi Saat Ini

Deklarasikan variabel dummy dengan penginisialisasi yang menyimpulkan tipe yang diinginkan tanpa mengevaluasi ekspresi (ini penting karena kita tidak ingin efek samping runtime, cukup ketik inferensi). Sebagai contoh:

let dummyReturnVal = null && someFunction(0, ''); // NB: someFunction is never called!
let ReturnType = typeof dummyReturnVal;           // Now we have a reference to the return type

Solusi ini memiliki beberapa kelemahan:

  • sangat jelas kludge, tidak jelas bagi pembaca
  • polusi pengenal (harus memperkenalkan variabel seperti dummyReturnValue )
  • tidak berfungsi dalam konteks ambien, karena memerlukan pernyataan imperatif

Solusi yang Diusulkan

_(NB: Solusi ini sudah disarankan di #4233, tetapi masalah itu ditandai 'Memerlukan Proposal', dan ada beberapa masalah lain yang terkait erat, oleh karena itu masalah terpisah ini.)_

Izinkan operan typeof menjadi ekspresi arbitrer. Ini sudah diperbolehkan untuk typeof expr dalam posisi nilai seperti if (typeof foo() === 'string') . Tetapi proposal ini juga mengizinkan ekspresi arbitrer ketika typeof digunakan dalam posisi tipe sebagai kueri tipe, misalnya type ElemType = typeof list[0] .

Proposal ini sudah sangat selaras dengan kata-kata spesifikasi saat ini:

Kueri tipe berguna untuk menangkap tipe anonim yang dihasilkan oleh berbagai konstruksi seperti literal objek, deklarasi fungsi, dan deklarasi namespace.

Jadi proposal ini hanya memperluas kegunaan itu untuk situasi yang saat ini belum terlayani seperti pada contoh di atas.

Sintaks dan Semantik

Semantik persis seperti yang telah dinyatakan dalam spesifikasi 4.18.6 :

Operator 'typeof' mengambil operan jenis apa pun dan menghasilkan nilai tipe primitif String. Di posisi di mana tipe diharapkan, 'typeof' juga dapat digunakan dalam kueri tipe (bagian 3.8.10) untuk menghasilkan tipe ekspresi.

Perbedaan yang diusulkan berkaitan dengan bagian 3.8.10 yang dikutip di bawah ini, di mana teks yang dicoret akan dihapus dan teks yang dicetak tebal ditambahkan:

Kueri tipe terdiri dari kata kunci typeof diikuti oleh ekspresi. Ekspresi diproses sebagai ekspresi pengenal (bagian 4.3) atau ekspresi akses properti (bagian 4.13) sebuah ekspresi unary , tipe melebar (bagian 3.12) yang menjadi hasilnya. Mirip dengan konstruksi pengetikan statis lainnya, kueri tipe dihapus dari kode JavaScript yang dihasilkan dan tidak menambahkan overhead run-time.

Poin yang harus ditekankan (yang saya pikir juga ada dalam spesifikasi tetapi tidak dapat menemukannya) adalah bahwa kueri tipe tidak mengevaluasi operan mereka. Itu benar saat ini dan akan tetap benar untuk ekspresi yang lebih kompleks.

Proposal ini tidak memperkenalkan sintaks baru apa pun, itu hanya membuat typeof tidak terlalu membatasi jenis ekspresi yang dapat dikuerinya.

Contoh

// Mapping to a complex anonymous type. How to reference the element type?
var data = [1, 2, 3].map(v => ({ raw: v, square: v * v }));
function checkItem(item: typeof data[0]) {...} // OK: item type is {raw:number, square:number}

// A statically-typed dictionary. How to reference a property type?
var things = { 'thing-1': 'baz', 'thing-2': 42, ... };
type Thing2Type = typeof things['thing-2']; // OK: Thing2Type is number

// A strongly-typed collection with special indexer syntax. How to reference the element type?
var nodes = document.getElementsByTagName('li');
type ItemType = typeof nodes.item(0); // OK: ItemType is HTMLLIElement

// A factory function that returns an instance of a local class
function myAPIFactory($http: HttpSvc, id: number) {
    class MyAPI {
        constructor(http) {...}
        foo() {...}
        bar() {...}
        static id = id;
    }
    return new MyAPI($http);
}
type MyAPI = typeof myAPIFactory(null, 0); // OK: MyAPI is myAPIFactory's return type
function augmentAPI(api: MyAPI) {...} // OK

// Declare an interface DRY-ly and without introducing extra type names
interface MyInterface {
    prop1: {
        big: {
            complex: {
                anonymous: { type: {} }
            }
        }
    },

    // prop2 shares some structure with prop1
    prop2: typeof (<MyInterface>null).prop1.big.complex; // OK: prop2 type is {anonymous: {type: {}}}
}

Diskusi Pro/Kontra

Melawan : Estetika sintaks yang buruk. Sintaks alternatif yang menangani kasus individual telah disarankan di #6179, #6239, #4555 dan #4640.

Untuk : Sintaks lain mungkin terlihat lebih baik untuk kasus spesifiknya, tetapi semuanya berbeda satu sama lain dan masing-masing hanya menyelesaikan satu masalah tertentu. Proposal ini memecahkan masalah yang muncul dalam semua masalah tersebut, dan pengembang tidak perlu mempelajari sintaks baru apa pun.

Melawan : Ekspresi dalam posisi tipe membingungkan.

Untuk : TypeScript sudah membebani typeof dengan dua arti, sebagai kueri tipe ia sudah menerima ekspresi dalam posisi tipe dan mendapatkan tipenya tanpa mengevaluasinya. Ini hanya mengendurkan batasan pada ekspresi apa itu sehingga dapat memecahkan masalah yang diangkat dalam masalah ini.

Melawan : Ini dapat disalahgunakan untuk menulis kueri jenis multi-baris yang sangat panjang.

Untuk : Tidak ada alasan bagus untuk melakukannya dalam kueri tipe, tetapi ada alasan bagus untuk mengizinkan ekspresi yang lebih kompleks. Ini pada dasarnya adalah mengaktifkan vs mengarahkan Martin Fowler.

Dampak Desain, Pertanyaan, dan Pekerjaan Lebih Lanjut

Kesesuaian

Ini adalah perubahan yang sepenuhnya kompatibel ke belakang. Semua kode yang ada tidak terpengaruh. Menggunakan kemampuan tambahan typeof adalah keikutsertaan.

Pertunjukan

Melihat perbedaannya, Anda dapat melihat perubahan yang sangat kecil. Kompiler sudah mengetahui jenis yang ditanyakan, ini hanya memunculkannya ke pengembang. Saya mengharapkan dampak kinerja yang dapat diabaikan, tetapi saya tidak tahu bagaimana menguji ini.

Perkakas

Saya telah menyiapkan Kode VS untuk menggunakan versi TypeScript dengan proposal ini diimplementasikan sebagai layanan bahasanya, dan semua penyorotan sintaks dan intellisense sempurna sejauh yang saya uji.

Ekspresi kompleks dapat terjadi di file .d.ts

Operand typeof dapat berupa ekspresi apa pun, termasuk IIFE, atau ekspresi kelas lengkap dengan badan metode, dll. Saya tidak dapat memikirkan alasan apa pun untuk melakukan itu, hanya saja bukan lagi kesalahan, bahkan di dalam file .d.ts ( typeof dapat digunakan - dan berguna - dalam konteks sekitar). Jadi konsekuensi dari proposal ini adalah bahwa "pernyataan tidak dapat muncul dalam konteks ambien" tidak lagi sepenuhnya benar.

Jenis rekursif ditangani dengan kuat

Kompiler tampaknya sudah memiliki semua logika yang diperlukan untuk menangani hal-hal seperti ini:

function foo<X,Y>(x: X, y: Y) {
    var result: typeof foo(x, y); // ERROR: 'result' is referenced in its own type annotation
    return result;
}
Dapat menanyakan jenis pengembalian fungsi yang kelebihan beban

Ini tidak ambigu; itu mengambil kelebihan yang cocok dengan ekspresi kueri:

declare function foo(a: boolean): string;
declare function foo(a: number): any[];
type P = typeof foo(0);    // P is any[]
type Q = typeof foo(true); // Q is string
Suggestion Too Complex

Komentar yang paling membantu

Membuka PR di #17961.

Semua 157 komentar

Bagi siapa pun yang menginginkan cara cepat untuk bermain dengan ini di VS Code dengan intellisense dll, ini adalah taman bermain repo .

tipe P = tipe foo(0); // P adalah sembarang[]
tipe Q = tipe foo(benar); // Q adalah string

Saya pikir menggunakan tipe sebagai argumen alih-alih nilai adalah sintaks yang lebih valid.

type P = typeof foo(number);    // P is any[]
type Q = typeof foo(boolean); // Q is string

Lebih jelas bahwa fungsi tidak dipanggil, karena Anda memberikan tipe dan bukan nilai sebagai argumen. Poin lainnya, adalah kurang ambigu. Beberapa orang akan menggunakan typeof foo(false) , sedangkan beberapa orang akan menggunakan typeof foo(true) . Jika Anda memiliki tipe sebagai argumen, orang hanya dapat menulis typeof foo(boolean) .

@tinganho tepatnya!
Meskipun kita masih bisa menulis typeof foo("abc") dengan #5185
di sini "abc" adalah tipe string tunggal

@tinganho Saya telah memikirkan ide Anda dan saya melihat beberapa hal yang saya sukai tentang proposal ini, dan hal lain yang saya sukai tentang saran Anda. Saran Anda bagus untuk alasan yang Anda berikan (sintaksis lebih sederhana, lebih jelas, kurang ambigu, kurang terlihat seperti panggilan fungsi). Hal yang saya sukai dari proposal saya adalah proposal ini tidak memperkenalkan sintaks baru apa pun, jadi tidak menambahkan komplikasi apa pun ke parser/pemeriksa, dan juga mendukung skenario yang lebih kompleks di mana Anda tidak memiliki nama tipe sederhana untuk argumen.

Saya berpikir, bagaimana jika ada cara yang sangat singkat untuk menulis sesuatu seperti sintaks foo(number) Anda tetapi menggunakan mekanisme penguraian ekspresi yang ada? Jadi sebagai percobaan saya telah memperkenalkan ekspresi baru: _unary as_. Anda cukup menulis as T dan itu adalah singkatan dari (null as T) . Anda pada dasarnya mengatakan, _'Saya tidak peduli dengan nilainya tetapi saya ingin ekspresi memiliki tipe X'_.

Dengan perubahan ini (yang telah saya terapkan di taman bermain repo ), Anda dapat menulis sesuatu yang lebih dekat dengan sintaks yang Anda sarankan, tetapi masih diuraikan sebagai ekspresi biasa:

type P = typeof foo(as number);    // P is any[]
type Q = typeof foo(as boolean); // Q is string

let prop2: typeof (as MyInterface).prop1.big.complex; // prop2 type is {anonymous: {type: {}}}

Ini hanya percobaan cepat. Sintaks yang setara bisa jadi (tapi saya belum mengimplementasikan ini):

type P = typeof foo(<number>);    // P is any[]
type Q = typeof foo(<boolean>); // Q is string

let prop2: typeof (<MyInterface>).prop1.big.complex; // prop2 type is {anonymous: {type: {}}}

Sintaks kedua mungkin disebut _nullary type assertion_, dengan ekspresi <T> menjadi singkatan untuk (<T> null) .

@yortus , kami menghabiskan beberapa waktu minggu lalu berbicara tentang proposal ini. maaf karena tidak memposting sebelumnya. konsensusnya adalah 1. kami memiliki masalah karena tidak dapat merujuk ke beberapa tipe, misalnya pengembalian fungsi atau tipe instans dari ekspresi kelas. dan 2. menambahkan ekspresi dalam posisi tipe bukanlah sesuatu yang nyaman bagi kita.

Usulan @tinganho adalah salah satu yang kami bicarakan juga. saya pikir ini lebih enak, meskipun mungkin akan lebih rumit untuk diterapkan. Menambahkan operator unary baru atau menggunakan sintaksis cast tidak terlalu elegan karena hanya menggunakan nama tipe.

Dibahas cukup lama di slog hari ini. "Menerima PR" disini adalah "Menerima PR dengan asumsi pelaksanaannya ternyata tidak terlalu gila"

Proposal @tinganho terlihat cukup bagus (setidaknya relatif terhadap opsi lain) dan kami ingin melihat PR tentatif yang mengimplementasikan ini.

Hal yang rumit adalah bahwa kita tidak ingin memiliki jalur kode yang benar-benar terpisah untuk menyelesaikan tipe kembalian f(number) vs f(0) , tetapi algoritma resolusi kelebihan sepenuhnya dimasukkan dengan asumsi bahwa itu bekerja dengan satu set _expressions_ daripada satu set _types_. Tapi kami pikir dengan sedikit tipu daya, ini seharusnya mudah.

Rencana dasar serangan adalah:

  • Perluas tata bahasa saat mengurai typeof untuk mengizinkan hal-hal yang terlihat seperti panggilan fungsi, akses properti, dan akses properti yang diindeks
  • Saat menguraikan argumen ke panggilan fungsi dalam kueri tipe (sebut saja _psuedocalls_ / _psuedoarguments_), gunakan fungsi parseType . Ini akan membuat TypeNode , tetapi atur flag pada node yang menunjukkan bahwa itu adalah tipe yang diuraikan dalam konteks kueri tipe
  • Di pemeriksa, checkExpression memeriksa tanda ini dan memanggil getTypeFromTypeNode alih-alih pemrosesan ekspresi normal

@mhegazy , @RyanCavanaugh tidak yakin berapa banyak kasus sudut yang dibahas tim, jadi bisakah saya mengemukakan beberapa di sini untuk klarifikasi? Saya telah mencantumkan banyak contoh di bawah ini dan mengomentari masing-masing dengan apa yang menurut saya seharusnya merupakan hasil dari operasi typeof , dengan tanda tanya pada kasus yang meragukan.


Notasi pengindeks
var data = [1, 2, 3];
const LOBOUND = 0;
type Elem1 = typeof data[0];        // number
type Elem2 = typeof data[999999];   // number or ERROR?
type Elem3 = typeof data[1+2];      // ERROR or number?
type Elem4 = typeof data[LOBOUND];  // ERROR or number?

var tuple: [number, string] = [123, 'abc'];
type Elem4 = typeof tuple[0];       // number or number|string?
type Elem5 = typeof tuple[1];       // string or number|string?
type Elem6 = typeof tuple[999999];  // number|string or ERROR?

const ABC: 'a-b-c' = 'a-b-c';
let dict = { 'a-b-c': 123, 'd-e-f': true };
type Prop1 = typeof dict['a-b-c'];  // number
type Prop2 = typeof dict['d-e-f'];  // boolean
type Prop3 = typeof dict[ABC];      // ERROR or number or any?

Notasi tipe pengembalian fungsi
// A simple function
declare function f1(n: number): string[];
type Ret1 = typeof f1(number);      // string[]
type Ret2 = typeof f1(0);           // ERROR or string[]?

// An asynchronous function that either accepts a callback or returns a Promise
declare function f2(n: number): Promise<string[]>;
declare function f2(n: number, cb: (err?: any, result?: string[]) => void): void;
type Ret3 = typeof f2(number);                    // Promise<string[]>
type Ret4 = typeof f2(number, any);               // void
type Ret5 = typeof f2(number, Function);          // ERROR: Function not assignable to callback
type Ret6 = typeof f2(number, (err: any, result: string[]) => void); // void
type Ret7 = typeof f2(number, (...args) => any);  // void

// A special function-like object
interface Receiver {
    (data: string[]): void;
    transmogrify(): number[];
}
declare function f3(n: number, receiver: Receiver): Promise<void>;
declare function f3(n: number, callback: (err?: any, result?: string[]) => void): void;
type Ret8 = typeof f3(number, Receiver);           // Promise<void>
type Ret9 = typeof f3(number, any);                // ambiguous? or picks first overload?
type Ret10 = typeof f3(number, Function);          // ERROR
type Ret11 = typeof f3(number, (...args) => any);  // void since not assignable to Receiver

// A function with parameter destructuring
interface CartesianCoordinate {/***/}
interface PolarCoordinate {/***/}
declare function f4({ x: number, y: number }): CartesianCoordinate;
declare function f4({ r: number, t: number }): PolarCoordinate;
type Ret12 = typeof f4(any);        // ambiguous? or picks first overload?
type Ret13 = typeof f4({x;y});      // CartesianCoordinate
type Ret14 = typeof f4({r;t});      // PolarCoordinate
type Ret15 = typeof f4({x;r;t;y});  // ambiguous? or picks first overload?

// Type-ception: is there anything wrong with typeof-in-typeof?
declare function f5(n: number, receiver: Receiver): Promise<void>;
declare function f5(n: number, callback: (err?: any, result?: string[]) => void): void;
function myCallback(err, result) {/***/}
var myReceiver: Receiver;
type Ret16 = typeof f5(number, typeof myReceiver); // Promise<void>
type Ret17 = typeof f5(number, typeof myCallback); // void


Mengekstrak bagian dari tipe kelas/antarmuka

Yaitu, contoh typeof (<MyInterface> null).prop1.big.complex; di atas.

Saya mengambilnya dari komentar di atas bahwa ini di luar cakupan dan tidak akan didukung. Apakah itu benar?

Notasi pengindeks
var data = [1, 2, 3];
const LOBOUND = 0;
type Elem1 = typeof data[0];        // number
type Elem2 = typeof data[999999];   // number
type Elem3 = typeof data[1+2];      // ERROR, only literals allowed here
type Elem4 = typeof data[LOBOUND];  // number when const resolution is done, otherwise any

var tuple: [number, string] = [123, 'abc'];
type Elem4 = typeof tuple[0];       // number
type Elem5 = typeof tuple[1];       // string 
type Elem6 = typeof tuple[999999];  // number|string

const ABC: 'a-b-c' = 'a-b-c';
let dict = { 'a-b-c': 123, 'd-e-f': true };
type Prop1 = typeof dict['a-b-c'];  // number
type Prop2 = typeof dict['d-e-f'];  // boolean
type Prop3 = typeof dict[ABC];      // number when const resolution work is done, otherwise any

Notasi tipe pengembalian fungsi
// A simple function
declare function f1(n: number): string[];
type Ret1 = typeof f1(number);      // string[]
type Ret2 = typeof f1(0);           // error, 0 is not a type

// An asynchronous function that either accepts a callback or returns a Promise
declare function f2(n: number): Promise<string[]>;
declare function f2(n: number, cb: (err?: any, result?: string[]) => void): void;
type Ret3 = typeof f2(number);                    // Promise<string[]>
type Ret4 = typeof f2(number, any);               // void
type Ret5 = typeof f2(number, Function);          // ERROR: Function not assignable to callback
type Ret6 = typeof f2(number, (err: any, result: string[]) => void); // void
type Ret7 = typeof f2(number, (...args) => any);  // void

// A special function-like object
interface Receiver {
    (data: string[]): void;
    transmogrify(): number[];
}
declare function f3(n: number, receiver: Receiver): Promise<void>;
declare function f3(n: number, callback: (err?: any, result?: string[]) => void): void;
type Ret8 = typeof f3(number, Receiver);           // Promise<void>
type Ret9 = typeof f3(number, any);                // picks first overload
type Ret10 = typeof f3(number, Function);          // ERROR
type Ret11 = typeof f3(number, (...args) => any);  // void since not assignable to Receiver

// A function with parameter destructuring
interface CartesianCoordinate {/***/}
interface PolarCoordinate {/***/}
declare function f4({ x: number, y: number }): CartesianCoordinate;
declare function f4({ r: number, t: number }): PolarCoordinate;
type Ret12 = typeof f4(any);        // picks first overload
type Ret13 = typeof f4({x;y});      // CartesianCoordinate
type Ret14 = typeof f4({r;t});      // PolarCoordinate
type Ret15 = typeof f4({x;r;t;y});  // picks first overload

// Type-ception: is there anything wrong with typeof-in-typeof?
declare function f5(n: number, receiver: Receiver): Promise<void>;
declare function f5(n: number, callback: (err?: any, result?: string[]) => void): void;
function myCallback(err, result) {/***/}
var myReceiver: Receiver;
type Ret16 = typeof f5(number, typeof myReceiver); // Promise<void>
type Ret17 = typeof f5(number, typeof myCallback); // void

Saya ingin tahu apa yang terjadi di sini:

const number = "number";
type Ret3 = typeof f2(number); // What happens here?

@SaschaNaz pertanyaan bagus. Situasi serupa:

class MyClass { foo; bar; }
declare function f(inst: MyClass): number;
type Ret = typeof f(MyClass);     // number (presumably)

Dalam hal ini masuk akal dalam typeof f(MyClass) untuk MyClass _type_ untuk dipertimbangkan sebelum MyClass _value_ (yaitu fungsi konstruktor). Yang pertama mengarah ke Ret = number , yang terakhir akan mengarah ke sesuatu seperti error: MyClass is not a type .

Akankah logika yang sama berlaku untuk nama yang merujuk ke _type_ dan _const value_? Dalam contoh Anda, itu berarti tipe number akan selalu diutamakan daripada nilai const number . Ada pendapat @RyanCavanaugh?

Benar, kami akan menyelesaikan ini di bawah semantik biasa dari ekspresi tipe (seolah-olah Anda telah menulis var x: [whatever] ). Jadi Anda bisa memiliki typeof f(MyClass) mengacu pada pemanggilan f dengan sisi instance, dan typeof f(typeof MyClass) mengacu pada pemanggilan f dengan fungsi konstruktor.

Jadi contoh @SaschaNaz dengan jelas merujuk ke number sebagai _type_, bukan sebagai _const value_, bukan?

const number = "number";
type Ret3 = typeof f2(number); // Promise<string[]>

@RyanCavanaugh dapatkah Anda mengonfirmasi bahwa grup kasus penggunaan ketiga berada di luar cakupan? misalnya dari OP:

// Declare an interface DRY-ly and without introducing extra type names
interface MyInterface {
    prop1: {
        big: {
            complex: {
                anonymous: { type: {} }
            }
        }
    },

    // prop2 shares some structure with prop1
    prop2: typeof (<MyInterface>null).prop1.big.complex; // OK: prop2 type is {anonymous: {type: {}}}
}

Kasus penggunaan ini (dengan sintaks apa pun) tidak akan didukung saat ini, benar?

Saya pikir ini harus ditutupi dengan mengizinkan ekspresi this .

   prop2: typeof this.prop1.big.complex;

Saya pikir hal const harus diselesaikan dengan typeof lain.

type Ret3 = typeof f2(typeof number); // typeof number is string so error here

... sementara ini akan memblokir typeof data[LOBOUND] .

@mhegazy itu ide bagus untuk typeof this . Saya baru menyadari ini sudah berfungsi dalam implementasi bercabang yang saya buat untuk proposal ini. Yah, itu berfungsi untuk kelas. Untuk antarmuka tidak ada kesalahan, tetapi tipe this selalu disimpulkan sebagai any . Output saat ini dari impl bercabang:

class MyClass {
    prop1: {
        big: {
            complex: {
                anonymous: { type: {} }
            }
        }
    };

    prop2: typeof this.prop1.big.complex; // prop2 type is {anonymous: {type: {}}}
}

interface MyInterface {
    prop1: {
        big: {
            complex: {
                anonymous: { type: {} }
            }
        }
    };

    prop2: typeof this.prop1.big.complex; // prop2 type is any
}

Apakah kemungkinan menyimpulkan this di dalam deklarasi antarmuka atau akankah fitur ini tetap terbatas pada kelas?

Saya ingin membuat dua poin tentang #6179 dan Angular.

// A factory function that returns an instance of a local class
function myAPIFactory($http: HttpSvc, id: number) {
    class MyAPI {
        constructor(token: string) {...}
        foo() {...}
        bar() {...}
        static id = id;
    }
    return MyAPI;
}
type MyAPIConstructor = typeof myAPIFactory(null, 0); // OK: MyAPI is myAPIFactory's return type
function augmentAPI(api: MyAPIConstructor) {...} // OK
  1. Jumlah parameternya bisa besar. Katakanlah 15 parameter. Pada saat yang sama tidak ada kelebihan beban, dan hanya kelebihan beban yang diperlukan untuk parameter di typeof . Jadi untuk kasus ini, bisakah kita memikirkan sintaks seperti berikut?

    type MyAPI = typeof myAPIFactory(...);
    
  2. Fungsi pabrik biasanya tidak ditetapkan ke variabel global sendiri. Ekspresi fungsi digunakan:

    angular.module('app').factory('MyAPI', function($http: HttpSvc, id: number) { /*...*/ });
    

    Seperti itulah biasanya. Seperti yang dapat dilihat, typeof tidak dapat digunakan di sini sama sekali.

@thron0 intuisi saya adalah bahwa sesuatu yang memiliki lebih dari beberapa parameter tetapi _also_ mengembalikan tipe anonim akan menjadi sangat langka. Bagaimana menurutmu?

Ini akan menjadi langka ketika Angular 1 menjadi langka, tidak lebih awal.

Pada dasarnya, apa yang Anda gambarkan adalah _factory_ Angular yang khas:

sesuatu yang memiliki lebih dari beberapa parameter tetapi juga mengembalikan tipe anonim

Ini adalah fungsi yang menggunakan dependensi sebagai parameternya dan membuat instance dari _service_. Anda mungkin berpikir bahwa sejumlah besar parameter bisa merepotkan, tetapi masalahnya adalah pabrik-pabrik itu tidak pernah dipanggil secara langsung. Wadah DI memanggil mereka. Pabrik dipanggil ketika sesuatu membutuhkan nilai pengembaliannya (layanan) sebagai ketergantungan. Dan itu disebut hanya sekali karena layanan selalu lajang di Angular. Namun, layanan dapat berupa apa saja, jadi jika kita membutuhkan perilaku non-tunggal, pabrik dapat mengembalikan konstruktor (atau fungsi pabrik). Sama seperti dalam contoh kode ini:

angular.module('app').factory('MyAPI', function($http: HttpSvc, id: number) {
    class MyAPI {
        constructor(token: string) {...}
        foo() {...}
        bar() {...}
        static id = id;
    }
    return MyAPI;
});

Seperti itulah solusi saya saat ini untuk situasi ini:

class MyAPI {
    // dependencies (1)
    protected $http: HttpSvc;
    protected id: number;

    constructor(token: string) {...}
    foo() {...}
    bar() {...}
    // Static properties cannot be used with this approach because:
    // 1) this class is a global variable so it can be shared by different 
    // instances of the DI container,
    // 2) the id property isn't available here as it's initialized in the subclass
    //// static id0 = id;
}

angular.module('app').factory('MyAPI', function($http: HttpSvc, id: number) { // (2)
    return class extends MyAPI {
        $http = $http; // (3)
        id = id;
    };
});

// this type is needed for type annotations in the services that require MyAPI as a dependency
type MyAPIConstructor = typeof MyAPI;

angular.module('app').factory('someOtherService', function(MyAPI: MyAPIConstructor) {
    var api = new MyAPI('qwerty');
    /* ... */
});

Seperti yang Anda lihat, itu benar-benar jelek dan menyakitkan. Saya harus membuat daftar dependensi tiga kali. Jika saya memiliki lebih banyak dependensi, terkadang lebih mudah untuk menulis kelas dengan cara biasa (di dalam fungsi pabrik) dan mendeklarasikan antarmuka untuk konsumennya.

Hai, saya ingin mengusulkan untuk membiarkan tipe sintaks/semantik tetap utuh dan sebagai gantinya menerapkan aljabar akses tipe sederhana:

Mari kita bayangkan ekstensi sintaks tipe:

type          ::=  ... |  literalType | type typeAccess | guarded
guarded    ::= "type" id
literalType   ::= stringLiteral | numberLiteral | symLiteral | booleanLiteral 
typeAccess    ::= typeField | typeSubscript | typeCall
typeField     ::= "." id
typeSubscript ::= "[" type "]"
typeCall      ::= "(" [type {"," type}] ")"

Setiap pengenal dalam ruang lingkup dapat secara bersamaan terikat ke dua entitas:

  • kompilasi jenis waktu
  • nilai waktu berjalan

penjaga tipe hanya mengekstrak yang pertama. Dengan demikian

class A {}
var a: A;

setara dengan

class A {}
var a: type A;

dalam hal ini jika kita memiliki kelas

class ABC {
    a: number;
    b(x: number): string;
    c:string[];
    d:[number, string];
}

maka kita bisa menulis

var a: (type ABC).a; // number
var b: (type ABC).b(number); // string
var c: (type ABC).c[number]; // string
var d: (type ABC).c[0]; // number

Ini juga mencakup penggabungan entitas _type_ dan entitas _value_ di bawah pengenal yang sama dalam lingkup leksikal.

interface SAME {
    x: number;
}
namespace SAME: {
    export type x = boolean;
    export var x: string;
}

var a: SAME.x   // boolean
var b: (type SAME).x  // number
var b: (typeof SAME).x  // string

@RyanCavanaugh , @sandersn dapatkah Anda memeriksa ide ini?

Bagaimana Anda menangkap jenis ekspresi dengan proposal Anda?

Pada Kamis, 19 Mei 2016 12:26 Anatoly Ressin, [email protected] menulis:

Hai, saya ingin mengusulkan untuk membiarkan tipe sintaks/semantik tetap utuh dan
alih-alih menerapkan aljabar akses tipe sederhana:

Mari kita bayangkan ekstensi sintaks tipe:

ketik ::= ... | tipe literal | ketik typeAccess | dijaga
dijaga ::= "ketik" id
literalType ::= stringLiteral | angkaLiteral | symLiteral | booleanSarapan
typeAccess ::= typeField | ketikSubskrip | ketikPanggilan
typeField ::= "." pengenal
typeSubscript ::= "[" ketik "]"
typeCall ::= "(" [ketik {"," jenis}] ")"

Setiap pengidentifikasi dalam ruang lingkup dapat secara bersamaan terikat pada keduanya
entitas:

  • kompilasi jenis waktu
  • nilai waktu berjalan

penjaga _type_ hanya mengekstrak yang pertama. Dengan demikian

kelas A {}
A A

setara dengan

kelas A {}
a: tipe A

dalam hal ini jika kita memiliki kelas

kelas ABC {
sebuah angka;
b(x: angka): string;
c:string[];
d:[angka, string];
}

maka kita bisa menulis

var a: (tipe ABC).a; // numbervar b: (ketik ABC).b(angka); // stringvar c: (ketik ABC).c[angka]; // stringvar d: (ketik ABC).c[0]; // nomor

Ini juga mencakup penggabungan entitas _type_ dan entitas _value_ di bawah
pengenal yang sama dalam lingkup leksikal.

antarmuka SAMA {
x: nomor;
}ruang nama SAMA: {
jenis ekspor x = boolean;
ekspor var x: string;
}
var a: SAMA.x // booleanvar b: (ketik SAMA).x // numbervar b: (tipe SAMA).x // string

@RyanCavanaugh https://github.com/RyanCavanaugh , @sandersn
https://github.com/sandersn dapatkah Anda memeriksa ide ini?


Anda menerima ini karena Anda berlangganan utas ini.
Balas email ini secara langsung atau lihat di GitHub
https://github.com/Microsoft/TypeScript/issues/6606#issuecomment -220378019

Contoh Anda mengingatkan saya bahwa tipe dari ekspresi bertitik dan tipe properti tipe #1295 adalah proposal yang sangat mirip. Kecuali satu lebih statis yang lain lebih dinamis.

Saya suka ide melewatkan kata kunci typeof juga.

   prop2: this.prop1.big.complex;

@mhegazy dalam contoh Anda, Anda menggunakan typeof dalam this-expression :

prop2: typeof this.prop1.big.complex;

Karena kita bisa melakukan ini hari ini:

someProp: this;

Dalam pandangan saya, ekstrapolasi sintaks di atas untuk properti yang lebih bertitik adalah:

someProp: this.prop1.prop2.prop3;

Dan tidak:

someProp: typeof this.prop1.prop2.prop3;

Dan untuk melanjutkan ekstrapolasi — mengapa tidak melewatkan typeof bersama-sama?

someProps: foo(number)

mengapa tidak melewatkan typeof bersama-sama?

Salah satu alasan yang dapat saya pikirkan adalah bahwa itu akan menjadi ambigu, apakah Anda mereferensikan sisi instance kelas atau sisi statis:

class C {}

// Does 'x' have the instance type of 'C',
// or does it have the shape of its constructor?
let x: C;

Saya juga ingin merujuk jenis properti di antarmuka dalam antarmuka lain. Memposting contoh saya sebagai #10588 yang sekarang ditutup demi tiket ini.

Akan lebih baik untuk menulis sesuatu seperti itu:

interface Foo {
  bar: string;
}

interface Box<T> {
  container: T;
}

interface FooWithBoxedProps {
  bar: Box<Foo.bar>; // OR: bar: Box<typeof Foo.bar>;
}

yang hanya akan menerjemahkan ini:

interface Foo {
  bar: string;
}

interface Box<T> {
  container: T;
}

interface FooWithBoxedProps {
  bar: Box<string>;
}

Nah, beberapa skenario yang disebutkan sebelumnya sekarang dapat diekspresikan dengan _tipe akses terindeks_ (#11929). Cukup gabungkan fitur ini dengan ekspresi typeof standar dan Anda bisa mendapatkan sesuatu seperti ini:

interface MyInterface {
    prop1: {
        big: {
            complex: {
                anonymous: { type: {} }
            }
        }
    },

    // prop2 shares some structure with prop1
    prop2: MyInterface["prop1"]["big"]["complex"];
}

dan itu berhasil! (saat ini dalam TypeScript@next)

Saya pikir hal alami berikutnya adalah memiliki sesuatu yang serupa tetapi untuk fungsi, beberapa "tipe panggilan fungsi" seperti ini:

interface FunctionLike{
    (arg1 : number, arg2 : string) : {a : number; b : string;}
}

type ReturnType = FunctionLike(number, string); // {a : number; b : string;} 

jadi kita bisa menggabungkannya secara alami dengan operator typeof

interface BigThing {
    testString: string;
    testNumber: number;
    testBoolean: boolean;
}

function getSmallThing(thing:BigThing){
    return {
        testString : thing.testString,
        testBoolean : thing.testBoolean
    }
}

type SmallThing = typeof getSmallThing(BigThing) // {testString: string; testBoolean : boolean}

Dan sepertinya ini adalah sintaks yang sudah diusulkan di awal utas ini (@tinganho). :)
Tapi itu akan menjadi fitur yang lebih umum yang dibuat dengan baik dengan operator typeof saat ini, seperti yang dilakukan _tipe akses terindeks_ sekarang.

Namun beberapa pertanyaan/keraguan masih tetap ada:

  • Apakah fitur ini akan menangani beberapa skenario lain seperti yang dilakukan _tipe akses terindeks_? Jika tidak, apakah layak untuk diterapkan?
  • Saya berasumsi dalam kebanyakan kasus akan lebih baik untuk tidak perlu menentukan jenis argumen sama sekali, karena skenario yang paling umum adalah "Saya hanya ingin mendapatkan jenis fungsi yang satu ini yang tidak memiliki kelebihan lainnya'. Jadi mungkin memperkenalkan beberapa operator khusus baru untuk ini akan baik-baik saja (misalnya typeofreturn atau returnof )

Sepertinya ini layak secara teknis. Ini mungkin memerlukan refactoring resolusi kelebihan / inferensi argumen tipe sehingga Anda tidak perlu menyelesaikan argumen. Saya tidak berpikir obat generik menimbulkan terlalu banyak masalah tambahan. Satu pertanyaan untuk ditanyakan adalah apa yang terjadi jika tidak ada kelebihan beban yang cocok dengan panggilan tersebut.

Saya pikir tidak jelas apakah fitur ini diinginkan. Jika suatu fungsi mengembalikan jenis anonim yang sangat rumit, bukankah akan lebih membantu sebagai penulis untuk memberi nama jenisnya, sehingga tidak harus direferensikan dengan cara ini? Saya akui ini hanya berdasarkan preferensi gaya. Saya kira saya tidak yakin seberapa nyaman ini akan digunakan dalam praktik. Saya pikir itu baik-baik saja untuk akses elemen.

tipe P = tipe foo(0); // P adalah sembarang[]
tipe Q = tipe foo(benar); // Q adalah string

Dalam diskusi ini tentang mengizinkan parameter untuk dirujuk oleh tipenya, saya akan menyarankan untuk mengizinkan hal yang sama untuk fungsi sebenarnya juga.
Konteks: Saya ingin mengetikkan fungsi ke map di atas objek:
function map<T, F extends Function>(fn: F, obj: T): { [P in keyof T]: typeof F(T[P]) }
Alasan saya ingin dapat merujuk ke fungsi berdasarkan tipenya F (selain hanya namanya fn ) adalah untuk kasus di mana namanya mungkin tidak tersedia, mis. saya ingin bisa mengatakan type MapFn<T, F extends Function> = { [P in keyof T]: typeof F(T[P]) } .

Sesuatu yang mungkin akan jauh lebih mudah untuk diterapkan (dan masih sangat berguna) adalah:

const myFunc = () => ({ x: 10 });
type myType = returnof myFunc;    // { x: number; }

Secara teori, mereka juga bisa dirantai jika Anda ingin membuat tipe ini beberapa level. Ada pikiran?

Sunting: Baru sadar ini disebutkan di atas oleh @mpawelski

@dehli Itu tidak akan berfungsi untuk fungsi yang kelebihan beban. Atau fungsi generik di mana parameter tipe digunakan dalam tipe kembalian.

@JsonFreeman Tidak bisakah fungsi kelebihan beban hanya OR berbagai jenis pengembalian? Dan fungsi generik dapat meminta Anda untuk menentukan jenisnya?

Itu mungkin, tetapi saya tidak yakin seberapa berguna itu. Sepertinya orang menginginkan sesuatu yang lebih canggih.

Saya sangat berharap ini akan segera dilaksanakan karena ini benar-benar menyakitkan.

Solusi

Karena solusi terakhir tidak berfungsi lagi, saya saat ini menggunakan ini:

// 0 argument function
export default function returnof<T>(fn: () => T): T;

// 1 argument function
// If no ambiguity simply infer the return type
export default function returnof<A, T>(fn: (a: A) => T): T;

// 1 argument function, with possible overload for the argument type.
// Explicitly set the type and use the correct overload
export default function returnof<A, T>(fn: (a: A) => T, a: A): T;

// ...

Anda tidak perlu menentukan argumen jika tidak ada kelebihan fungsi Anda.

const hello = (arg: number) => 'World'

const helloReturnValue = returnof(hello)
type helloReturnType = typeof helloReturnValue // string

Fungsi kelebihan beban

declare function hello(): void;
declare function hello(a: number): number;

const helloReturnValue = returnof(hello)
type helloReturnType = typeof helloReturnValue // void

const helloReturnValue = returnof(hello, 42)
type helloReturnType = typeof helloReturnValue // number

Semua definisi ada di sini:
https://github.com/kube/returnof/blob/master/lib/returnof.d.ts

Saya baru saja membuat paket NPM kecil untuk menggabungkannya dengan mudah tanpa mencemari proyek Anda.

Jika Optional Generics ditambahkan seperti yang diusulkan di https://github.com/Microsoft/TypeScript/issues/2175 , itu akan memungkinkan solusi sederhana hanya deklaratif:

type Return<T extends () => S, S> = S

Dan gunakan seperti ini:

type helloReturnType = Return<typeof hello>

@mhegazy @JsonFreeman Ada rencana untuk fitur ini?

Halo semuanya,
Saya memiliki kemungkinan solusi alternatif untuk masalah ini (maaf jika sudah disarankan dan saya melewatkannya) - Saya mengusulkannya di edisi #13949. Saya tidak tahu tentang jenis operator pada tahap itu, tetapi saya pikir solusi saya lebih umum. Pada dasarnya, perkenalkan sintaks baru seperti =MyType , yang dapat digunakan di mana pun Anda akan menggunakan MyType , tetapi alih-alih mendeklarasikan objek bertipe MyType , itu membuat ketik bernama MyType dari tipe objek yang disimpulkan. Misalnya ini adalah bagaimana saya akan menggunakannya dalam membuat komponen Vue.

function createData(){
  return <=MyData>{
    dataProp1: <string>null
  }
}

function method1(){
  let self: MyComponent = this;
  console.log(self.dataProp1);
  self.method2();
}

function method2(){
  let self: MyComponent = this;
  //dostuff
}

type MyComponent = MyData & MyMethods;

let componentOptions = {
  data: createData,
  methods: <=MyMethods>{method1, method2}
}
//todo: register component...

Perhatikan fungsi createData juga dapat ditulis

function createData(): =MyData {
  return {
    dataProp1: <string>null
  }
}

Saya menemukan pekerjaan yang sangat menyenangkan yang menggeneralisasi ekspresi apa pun:

const varWithRightType = (false as true) && some.deep.access()
type MyType = typeof varWithRightType;

EDIT: BERHENTI TERtawa PADA SAYA INI ADALAH TYPESCRIPT SERIUS

@johnfn ini terlihat menyenangkan untuk kasus sederhana :), tetapi jika fungsi Anda memerlukan beberapa params, Anda harus melakukan pekerjaan tambahan seperti ini:

const stateProps = (false as true) && mapStateToProps({} as any);

Saya menggunakan pekerjaan yang berbeda, yang sangat berguna untuk React & Redux, ketika mencoba menggunakan inferensi tipe untuk mendapatkan tipe props dari fungsi mapStateToProps . Maka tidak perlu lagi mendeklarasikan dan memelihara antarmuka Props yang disuntikkan oleh Redux connect secara manual yang melelahkan untuk dipelihara dan rawan kesalahan.
Penyelesaian ini juga akan menangani dengan baik setiap tanda tangan fungsi yang diberikan untuk kasus penggunaan lainnya.

Contoh kasus penggunaan operator typeof "Bereaksi & Redux":

Contoh di bawah ini mencoba menunjukkan kasus penggunaan proyek nyata yang umum untuk penggunaan operator typeof untuk mendapatkan tipe dari deklarasi fungsi yang akan sangat berguna jika ditambahkan ke TypeScript.

import { returntypeof } from 'react-redux-typescript';
import { RootState } from '../../store';
...

const mapStateToProps = (state: RootState) => ({
  currencies: CurrencyRatesSelectors.getCurrencies(state),
  currencyConverter: storeState.currencyConverter,
});

const stateProps = returntypeof(mapStateToProps); 
type Props = typeof stateProps & typeof dispatchToProps;
type State = {};

class CurrencyConverterContainer extends React.Component<Props, State> {
...

Sumber: https://github.com/piotrwitek/react-redux-typescript-patterns#react -connected-components

Solusi @kube dan paketnya https://github.com/kube/returnof tampaknya berfungsi seperti yang diharapkan! 👍

Hm. Saya berharap returnof akan membantu mengetikkan map() berbasis Tuple dari file .d.ts , tetapi mengingat ketergantungannya pada bahasa ekspresi ( const tidak dapat digunakan dalam konteks sekitar ), Saya khawatir kasus penggunaan saya dengan itu setidaknya akan lebih rumit.

Sunting: tampaknya generik opsional telah digabungkan sekarang, artinya versi bahasa tipe deklaratif @kube saja ( type Return<T extends () => S, S> = S -> type helloReturnType = Return<typeof hello> ) akan menjadi layak. :D

Koreksi: tidak, dukungan gabungan untuk nilai generik default tampaknya tidak mengizinkan penetapan bagian dari generik sementara membiarkan yang lain kembali ke nilai yang disimpulkannya; Return<typeof hello> hanya menghasilkan kesalahan Generic type 'Return' requires 2 type argument(s). .

@tycho01 Ya saya kecewa karena tidak menyelesaikan masalah.

Saya sudah membuka masalah terkait tentang Optional Generic Types Inference :

14400

Anda biasanya bisa mendapatkan tipe kembalian suatu fungsi melalui kombinasi fungsi ambient dummy dan inferensi tipe:

sekitar function returnTypeOf<RT>(fn:(...rest:any[])=>RT):RT {return void 0};
lokal var r = returnTypeOf(someFunction); mendapat nilai undefined

Tetapi jika Anda ingin menggunakan kembali tipe pengembalian itu, maka Anda harus menangkapnya ... jadi kita kembali ke awal:

type RT = typeof r;

Akan jauh lebih mudah jika pada awalnya kita memperluas konsep typeof untuk mengizinkan returntypeof atau bahkan lebih baik menyimpulkan penggunaan ini dari typeof fn(a,b,c,...) , yang kemudian dapat menangkap jenis pengembalian tanda tangan yang berbeda . Pemahaman tipe kembali ini sudah dilakukan oleh TS secara internal.

typeofexpression () akan menjadi semacam rekursi tipe kembali yang dicampur dengan operasi tipe: mis

type E = typeofexpression (f(1) + g("x"))

adalah

type E = typecomprehensionof (typeof f(1) + typeof g("x"))

yang mungkin dipahami seperti salah satunya

type E = typecomprehensionof (string + string) yaitu string
type E = typecomprehensionof (string + number) yaitu string
type E = typecomprehensionof (number + number) yaitu number

Betapa sulitnya ini secara internal dan biaya kinerjanya tidak saya ketahui.

EDIT -------------------------------------------------- ------------

Saya bermaksud menambahkan ... ini sangat penting bagi siapa saja yang harus menggunakan Function.bind, Function.apply, Function.call karena ini saat ini mengembalikan tipe any dan ini berarti bahwa mereka harus terus-menerus mengetik- dijelaskan untuk memastikan bahwa mereka tidak keluar dari proses pengecekan tipe.

Mampu mereferensikan tipe kembalinya argumen fungsi adalah ... kebahagiaan ...

@poseidonCore

Mampu mereferensikan tipe kembalinya argumen fungsi adalah ... kebahagiaan ...

Saya lebih menyukai typeof <expression> saat ini, dan masalah tipe pengembalian sudah tercakup dalam #12342.

Saya hanya menggunakan typeofexpression dan typecomprehensionof sebagai cara untuk membedakan penggunaan tersebut dalam penjelasan. Saya lebih suka typeof (expression) sebagai sintaks yang sebenarnya.

Maksud saya adalah bahwa memahami jenis ekspresi akan membutuhkan pemahaman jenis kembalian dari suatu fungsi ... f(1) juga merupakan ekspresi. #12342 terkait dengan cara ini. Mereka tidak saling eksklusif.

Kita sudah bisa melakukan typeof untuk variabel dan karena ekspresi adalah operasi atas variabel dan fungsi, persyaratan selanjutnya adalah mampu mengembalikan tipe fungsi ... dan kemudian memahami hasilnya berdasarkan aturan tipe .

Sebenarnya, poin yang bagus. Masalah yang diinginkan #12342 adalah cara untuk mengakses tipe pengembalian sebagai semacam properti untuk tipe generik, jadi saya salah memahami hubungannya.

Bagaimana dengan menggunakan ekspresi untuk panggilan fungsi typeof , seperti dalam proposal, tetapi menggunakan tipe secara langsung sebagai pengganti parameter?

Misalnya

function myFunction<T>(param1: T, param2: string, param3: number): T & {test: string} {
  // ...
}

type ResultType = typeof myFunction({hello: string}, string, number)
// ResultType is: {hello: string} & {test: string}

Perhatikan bahwa ini tidak menghentikan siapa pun untuk menggunakan jenis variabel cakupan lokal, dengan menggunakan typeof dalam panggilan, yaitu:

type ResultType = typeof myFunction(typeof obj, string, number)
// ResultType is: typeof obj & {test: string} 

Ini tampaknya sedikit lebih baik daripada proposal asli, karena ini bekerja dalam konteks sekitar dan tampaknya lebih fleksibel secara umum. Bagi saya, ini juga memperjelas bahwa kita sebenarnya tidak memanggil fungsi tersebut, hanya mencoba mengembalikan tipe dari nilai pengembaliannya.

Bagaimana saya melakukannya untuk kasus sederhana saya:

interface IAlertMessage {
  addedAt: number;
  text: string;
  type: "error" | "warning" | "info" | "success";
}

declare let alertMessageInterface: IAlertMessage;

const messages: IAlertMessage[] = [];

function addMessage(text: string, type: typeof alertMessageInterface.type): void {
  messages.push({addedAt: new Date().getTime(), text, type});
}

addMessage("something", "info"); // <- OK - and also has intellisense for the second parameter (after typing first " of the second parameter, press Ctrl+Space in VSCode)
addMessage("something", "fatal"); // <- Compilation error : error TS2345: Argument of type '"fatal"' is not assignable to parameter of type '"error" | "warning" | "info" | "success"'.

Di atas juga berfungsi dengan anggota antarmuka itu sendiri:

declare let myIface: MyInterface;

interface MyInterface {
    prop1: {
        big: {
            complex: {
                anonymous: { type: {} }
            }
        }
    },
    prop2: typeof myIface.prop1.big.complex.anonymous;
}

Keuntungan menggunakan declare let ... daripada type ... bagi saya adalah tidak menghasilkan apa pun di file .js keluaran.

@varadero ini tidak memecahkan masalah yang dihadapi, yang mengambil kembali jenis panggilan fungsi. Yang Anda lakukan hanyalah mengambil tipe properti dari suatu tipe, yang merupakan fitur yang didukung sejak TypeScript 2.2, saya yakin.

Itu sebenarnya jenis properti dengan nilai (palsu). Kompiler tidak mengizinkan Anda menggunakan typeof dengan tipe atau antarmuka "murni", ini hanya berfungsi pada nilai.

Ini akan bagus untuk dimiliki!

karena type KEYOF<T extends any[]> = keyof T[0] sudah ada, typeof T[0]('something') akan bekerja dengan baik dengan implementasi ini, dengan asumsi function myFunc<T, K extends KEYOF<T>>(type: K)>{ } dan myFunc([(r: string) => r]) (akan mengembalikan string untuk typeof T[0]('something') ) ?

Ini akan menjadi kuat bersama dengan polimorfik this .

Saatnya hal ini terjadi!

Membaca ulang utas ini, saya mencoba memahami apa yang kami lakukan dengan sintaks typeof dan mengapa.

Terbukti, dalam kasus typeof foo sepele yang sudah diterapkan, ia memberikan tipe foo . (Saya membayangkan pet is Fish kebal untuk menggunakan sintaks khusus.)

Dalam bacaan saya saat ini tentang apa yang harus dilakukan kata kunci ini dalam proposal saat ini, kata kunci itu sendiri tidak lebih dari apa yang dilakukannya sekarang, dan proposal saat ini sebenarnya tidak terkait dengan kata kunci typeof .

Saya mengemukakan ini karena itu penting dalam kasus yang saya sebutkan di atas, di mana kami menerapkan fungsi yang disimpan sebagai tipe (baik melalui type atau sebagai generik), daripada sebagai nilai.

Mengingat itu, saya menganggap bahwa, sementara typeof fn<A>(string) membutuhkan typeof untuk mengangkat variabel tingkat ekspresi fn untuk digunakan pada tingkat tipe, Fn<A>(string) di sisi lain , dengan Fn sebagai generik yang berisi fungsi, tidak memerlukan itu, dan karenanya dapat 'diterapkan' untuk mendapatkan tipe pengembalian yang sesuai di sini tanpa perlu typeof .

Dalam interpretasi ini, kami akan memeriksa panggilan fungsi setelah jenis fungsi potensial apa pun: selain typeof fn(...) / typeof fn<...>(...) juga Fn(...) / Fn<...>(...) , jika tidak berfungsi literal ((foo: Foo) => Bar)(Baz) / + generik. Jika tidak, rencana serangan harus tetap utuh.

Mungkin saya salah mengartikan bagaimana kalian melihat ini, mungkin fungsi yang disimpan dalam tipe bahkan belum dipertimbangkan (karena saya tidak menyebutkannya). Either way, saya pikir itu akan layak dikonfirmasi.

Jika mengumumkan aplikasi fungsi menjadi kelebihan semantik lain untuk typeof , selain disambiguasi konstruktor kelas dan mengangkat variabel level ekspresi ke level tipe (selain dari level ekspresi typeof ), banyak hal tampaknya secara bertahap lebih berbelit-belit, seperti yang telah ditunjukkan oleh beberapa pertanyaan sebelumnya di utas ini.

Sunting: tipe generik sudah dapat mengembalikan tipe fungsi, yang juga bisa memiliki generik. Ini berarti permutasi lain akan menjadi GetFn<A><B>() , dengan set generik pertama milik pemanggilan tipe generik, yang terakhir milik pemanggilan fungsi. Tidak ada GetFn<A><B><C>() , meskipun GetFn<A><B>()<C>() juga sah. Kesimpulan sebelumnya tetap tidak berubah: semua jenis dapat berisi fungsi dan oleh karena itu (berpotensi) diterapkan sebagai fungsi.

Sunting 2: Saya baru menyadari akan ada ambiguitas yang tidak menguntungkan dalam X<Y>() . Sekarang, X akan menjadi tipe yang mengembalikan tipe fungsi.

  • Jika X tidak generik, ini jelas -- <Y> termasuk dalam pemanggilan fungsi.
  • Jika X bersifat generik dan memerlukan parameter, ini juga jelas -- ini milik tipe, dan fungsi tidak melewati parameter tipe apa pun.
  • Namun, jika X memiliki generik opsional, ini menjadi ambigu.

    • Dalam satu interpretasi, X dilewatkan parameter tipe, sedangkan fungsinya tidak.

    • Dalam interpretasi lain, X dibiarkan menggunakan param tipe defaultnya, sedangkan <Y> akan membuat parameter pemanggilan fungsi sebagai gantinya.

Saya belum yakin apa yang akan menjadi solusi terbaik di sini.

  • Salah satu opsi adalah memaksa pemanggilan tipe sepenuhnya bergantung pada parameter tipe default untuk secara eksplisit menggunakan <> kosong daripada juga mengizinkan untuk melewati ini. Namun, ini akan menjadi perubahan yang menghancurkan.
  • Alternatifnya adalah memaksakan pembatasan seperti itu pada pemanggilan fungsi tingkat tipe sebagai gantinya - karena ini baru, tidak ada perubahan yang melanggar yang akan diperkenalkan. Namun, pendekatan ini akan menjadi tidak elegan karena akan memperkenalkan asimetri antara sintaks untuk pemanggilan fungsi antara ekspresi dan bahasa tipe, yang mungkin membuatnya kurang intuitif.

@icholy : ya, suntingan ini hanya menambah poin Anda, saya tahu.

Sunting 3: oke, fitur ini sekarang mengalahkan bagian atas daftar keinginan saya. Tidak ada peningkatan lain yang datang bahkan dari jarak jauh dalam hal dampak untuk inferensi.

wat

@icholy : singkatnya, jika 'fungsi' dalam tipe/generik, apakah kita masih akan menulis typeof ?

Jika ini dilakukan sehingga berinteraksi dengan kelebihan beban dengan benar, itu sebenarnya memberi Anda fungsi "variadik" secara gratis (mereka hanya kari alih-alih memiliki> 1 arity), karena Anda dapat secara rekursif mendefinisikan tipe kembalian suatu fungsi dalam hal penerapan salah satu kelebihannya, dengan beberapa kelebihan beban dasar. Beginilah cara melakukan sesuatu di Haskell, dan itu akan menjadi fitur yang luar biasa untuk dimiliki di TypeScript.

@masaeedu : kedengarannya menarik. Dan ya, kelebihan pastilah yang membuat proposal ini sangat menarik -- mereka dapat digunakan untuk mencocokkan pola pada opsi yang berbeda, termasuk any fallback. Pengecekan tipe seperti itu sejauh ini belum memungkinkan pada level tipe.

Saya akui saya lebih sering menggunakan Ramda daripada Haskell. Tapi dari latar belakang itu, saya pikir kari biasanya tidak cocok dengan fungsi variadik, karena kari perlu 'tahu' apakah akan mengembalikan hasil atau fungsi lain untuk menangani argumen tambahan.

Bisakah Anda menunjukkan beberapa kode semu tentang bagaimana Anda melihat ide ini bekerja untuk fungsi variadik seperti Object.assign (melewati detail seperti & vs. Overwrite seperti yang saya gunakan di PoC saya untuk R.mergeAll )?

@tycho01 Saya telah bermain-main dengan kode seperti ini:

interface Pop<TVarStack extends VarStack> {
    (a: TVarStack["head"]): Pop<TVarStack["tail"]>
}

interface VarStack<THead = any, TTail extends (void | VarStack) = any> {
    head: THead
    tail: TTail
    <TNew>(a: TNew): VarStack<TNew, this>
    (): (a: Pop<this>) => any
}

// Figure out implementation later :)
let stack: VarStack<void, void>;
const pop = stack(new Date())(10)("Bob's")("your")("uncle")()

// a, b, c, d, and e are well-typed
pop(a => b => c => d => e => {
    // Need this because can't figure out how to encode base-case in Pop recursion
    return stack
})

VarStack adalah tipe fungsi "variadik" dalam arti tertentu, di mana Anda dapat memberikan sejumlah argumen heterogen yang berubah-ubah dan itu akan dengan setia merekam informasi tipe terkait menggunakan rekursi pada tingkat tipe. Sayangnya TypeScript tidak memiliki dukungan yang baik untuk transformasi dan pencocokan pola pada tingkat tipe, dengan cara yang sama seperti Haskell.

Jika kami memiliki akses ke typeof , saya akan dapat memecahkan masalah kasus dasar untuk Pop , karena returnof(overloadedFunction(T)) pada dasarnya akan memberi saya cara untuk melakukan pola- cocok pada tingkat tipe.

@tycho01 Saya tidak dapat melakukannya dengan tepat untuk Object.assign , karena seperti yang saya katakan, ini hanya berfungsi untuk fungsi kari, tetapi saya akan mencoba membuat fungsi kari assign untuk Anda yang berfungsi secara kasar cara yang sama.

Saya berharap mereka akan menggeneralisasi jenis aljabar sedikit, sehingga misalnya (a: string) => (b: number) => void hanya gula untuk Func<string, Func<number, void>> , dan (a: string, b: number) => void hanya gula untuk Arity<number, Arity<string, void>> , atau sesuatu seperti itu. Kemudian kita hanya perlu alat tujuan umum untuk mengubah tipe untuk mendapatkan banyak fitur menarik secara darurat.

Maaf semuanya untuk triple post. @tycho01 Inilah kari, "variadik" assign :

interface Assign<TCurr extends {} = {}> {
    <TAdd extends {}>(a: TAdd): Assign<TCurr & TAdd>
    (): TCurr
}

let assign: Assign = undefined // implement somehow
const result = assign({ foo: "bar" })({ baz: 42 })()

@masaeedu : Hah, jadi saya kira itu seperti memiliki fungsi peredam :), meskipun bagian itu mungkin bisa lebih sulit jika ada argumen non-variadik (?) juga.

Aku suka gagasan itu; Saya pasti belum terlalu banyak berpikir ke arah antarmuka.
Jika kita dapat mengubah JS untuk pengetikan, mungkin saya akan meminta TC39 untuk mengubah Object.assign menjadi non-variadic mergeAll . :D

Tidak seperti iterasi berbasis kenaikan saya seperti itu benar-benar bekerja dengan fungsi dalam praktik sejauh ini (#17086) ... tapi bagaimanapun saya dengan Anda proposal ini akan memiliki dampak yang lebih besar daripada yang lain yang pernah saya lihat.

@ tycho01 Saya pikir Anda tidak akan mendapatkan banyak daya tarik jika Anda meminta fungsi variadic diubah menjadi fungsi arity tetap, karena ada biaya runtime untuk fungsi kari atau aplikasi berulang yang JS (atau setidaknya mesin dan kode JS yang ada) mungkin tidak terlalu dioptimalkan untuk. Saya pikir apa yang kita butuhkan sebagai gantinya adalah TypeScript untuk membuatnya sama mudahnya untuk membangun dan mendekonstruksi tipe untuk arity> 1 fungsi secara rekursif. Saya kira ini semua kembali ke #5453.

Orang lain mungkin tertarik mengetahui bahwa c++ memiliki fitur yang sangat mirip: decltype(expression)

Apakah fitur ini akan berhasil mencapai TypeScript?
Saya sangat lebih suka bentuk asli yang diusulkan di mana argumennya adalah ekspresi, tetapi niat tim TS untuk memiliki sintaksis khusus dengan tipe dalam posisi ekspresi hanya memperumit segalanya dan menunda untuk memasukkannya ke dalam bahasa.
Kita perlu menjaga topik ini.

Saya akan sangat senang jika kami dapat mengimplementasikan sebagian dari proposal ini. Yaitu, yang satu ini:
type A = typeof anotherVariable . Saya bisa melakukan segalanya hanya dengan satu baris ini. interface B extends A , <B & A> : B & A , dll.

Saya melihat banyak kasus penggunaan untuk hal semacam ini dengan React. Saat Anda membuat komponen tingkat tinggi, akan lebih sulit untuk memberi petunjuk ke deklarasi kelas komponen bahwa saat ini adalah HOC.

Apa yang akhirnya saya lakukan adalah membuat dua kelas. Satu kelas memperluas React.Component dan menambahkan semua metode tambahan, sedangkan kelas di dalam fungsi HOC memanipulasi render() .

@blindbox type A = typeof anotherVariable sudah berfungsi:

var data = [1, 2, 3].map(v => ({ raw: v, square: v * v }));

function checkItem(item: typeof data) {
    // item is Array<{ raw: number; square: number; }>
}

@Igorbek Wow, Anda benar.

Oke, saya tahu mengapa itu tidak berhasil di pihak saya.
Ini tidak akan berhasil

interface B {
  thisIsB: boolean
}

const typeTest = (type: any) => {
  return 1 as any as App & B
}
type A = typeof typeTest(1)
declare const aVal: A; // aVal's type isn't of type App & B, but something else.

Namun, ini akan berhasil.

interface B {
  thisIsB: boolean
}

const typeTest = (type: any) => {
  return 1 as any as App & B
}
const typeTestVal = typeTest(1)
type A = typeof typeTestVal
declare const aVal: A; // aVal's type is of type App & B!

Ya, jadi masalahnya adalah itu tidak dapat digunakan dalam kasus penggunaan tertentu, seperti ketika Anda perlu menggunakan argumen tipe generik.

function someFunctionWithComplexReturnTypeThatUsesGenerics<T>(item: T) { ... }

// it is impossible to work this around because there's no chance to create a fake variable that would be parameterized by T
function use<T>(item: typeof someFunctionWithComplexReturnTypeThatUsesGenerics<T>(item)) { ... }

@Igorbek :

Saya sangat lebih suka bentuk asli yang diusulkan di mana argumennya adalah ekspresi, tetapi niat tim TS untuk memiliki sintaksis khusus dengan tipe dalam posisi ekspresi hanya memperumit segalanya dan menunda untuk memasukkannya ke dalam bahasa.

Jadi sejak mereka menulis itu, kami mendapatkan literal untuk string dan angka, artinya segalanya menjadi sedikit lebih kabur -- keduanya akan bekerja dengan cara apa pun. Array dan objek literal terlihat mirip dengan notasi tipenya, sejauh ini bagus.

Jadi kami juga memiliki nilai/tipe yang disimpan, misalnya variabel, generik, tipe lainnya. Agar hal-hal menjadi berguna, kita tidak hanya harus puas dengan sebagian dari ini di sini.
Apa yang membuat argumen yang baik untuk pendekatan berbasis tipe mereka di sini adalah bahwa memungkinkan jenis campuran ini datang secara gratis di tingkat tipe. Artinya, pada level tipe kita sudah dapat mengatakan typeof myVar , artinya jika fungsi 'aplikasi' ini ditambahkan ke level tipe, kita akan secara otomatis dapat memasukkan tipe tersimpan dan variabel reguler .

Saya akan tertarik untuk melihat pemikiran Anda lebih lanjut tentang pendekatan yang awalnya disarankan. Memang, itu mungkin berfungsi untuk mengekspos sedikit lebih banyak ke level tipe: Operator berbasis JS (pikirkan ! && || + - * / instanceof ) serta operator khusus TypeScript (operator pernyataan ! ).
Hal tentang operator JS itu seperti ... -level 1 + 1 hanya menghasilkan tipe number , dan serupa untuk yang lain.
Dengan pemikiran ini, saya sendiri agak kecewa dengan proposal berbasis tipe mereka.

Apakah fitur ini akan berhasil mencapai TypeScript? [...] Kita perlu mempertahankan topik ini.

Saya menyarankan proposal ini sebagai solusi yang lebih umum untuk gejala yang lebih kecil di sini , meskipun keberhasilannya terbatas.

@tycho01 Argumen saya untuk variasi berbasis ekspresi typeof hampir sama dengan yang dinyatakan oleh @yortus :

  • konsistensi : typeof yang ada (dalam posisi tipe) sudah bekerja dengan ekspresi, namun dibatasi untuk menerima satu simbol. Sehingga menerima tipe atau panggilan semu akan memperkenalkan sintaks yang jauh lebih rumit yang lebih merupakan cabang sintaks lainnya (selain ekspresi dan tipe).

    • pengguna tidak perlu mempelajari sintaks baru

  • kesederhanaan : TypeScript tampaknya sudah memiliki semua fasilitas yang diperlukan untuk mengimplementasikan fitur ini dengan cara yang tidak mencolok (sudah dibuktikan oleh PR)
  • kelengkapan : ekspresi setidaknya memiliki ekspresi yang sama dengan tipe, karena kita selalu dapat menggunakan pernyataan tipe ( (undefined as any as <any arbitrary type can be here>) ), tetapi terkadang sistem tipe tidak memiliki tipe yang dapat diekspresikan dalam ekspresi (tipe spread dan rest diperkenalkan setelah ekspresi yang sesuai telah mendarat).

Terima kasih @tycho01 bahwa Anda membawa poin itu ke promised PR (Saya benar-benar datang ke sini setelah komentar Anda di sana), yang benar-benar menunjukkan bagaimana fitur yang sederhana dan umum dapat mencakup skenario yang jauh lebih kompleks dengan cara yang sangat elegan tanpa dipanggang dalam perilaku yang sangat spesifik ke dalam bahasa.

Saya melihat typeof yang diperluas sebagai pengubah permainan nyata untuk ekspresi sistem tipe, paling seperti tipe yang dipetakan/ keyof melakukan itu.

@Igorbek : Terima kasih telah menjelaskan, saya mengerti dari mana Anda berasal. Mungkin kedua pendekatan ini melayani kasus penggunaan yang berbeda.

Saya pikir Anda lebih baik menunjukkan nilai hari ini dari proposal asli daripada posting asli hari ini, karena TS saat ini (atau, yah, 2.3.3 Playground) dapat membuat banyak skenario asli sudah berfungsi:

// I have a strongly-typed collection but the element type is anonymous/unknown, how can I reference the element type? (#3749)

// Mapping to a complex anonymous type. How to reference the element type?
var data = [1, 2, 3].map(v => ({ raw: v, square: v * v }));
declare function checkItem(item: typeof data[0]): any
// ^ no longer errors, needs no further change

// A statically-typed dictionary. How to reference a property type?
var things = { 'thing-1': 'baz', 'thing-2': 42 };
type Thing2Type = typeof things['thing-2'];
// ^ no longer errors, needs no further change

// A strongly-typed collection with special indexer syntax. How to reference the element type?
var nodes = document.getElementsByTagName('li');
type ItemType = typeof nodes.item(0);
// ^ the `.item` access works, but function applications still errors, would be fixed by either version of the proposal.

// A function returns a local/anonymous/inaccessible type, how can I reference this return type? (#4233, #6179, #6239)

// A factory function that returns an instance of a local class
function myAPIFactory($http: HttpSvc, id: number) {
  class MyAPI {
    constructor(http) {}
    foo() {}
    bar() {}
    static id = id;
  }
  return new MyAPI($http);
}
declare function augmentAPI(api: typeof myAPIFactory(HttpSvc, number) /* ERROR */): any
// ^ function applications errors, would be fixed by either version of the proposal, if using slightly different syntax.

// I have an interface with a complex anonymous shape, how can I refer to the types of its properties and sub- properties ? (#4555, #4640)

// Declare an interface DRY-ly and without introducing extra type names
type MyInterface = {
  prop1: {
    big: {
      complex: {
        anonymous: { type: {} }
      }
    }
  },
  // prop2 shares some structure with prop1
  prop2: MyInterface['prop1']['big']['complex'];
}
// ^ no longer errors after swapping `.k` access to `['k']`. `typeof` was unnecessary and counter-productive -- MyInterface isn't exposed on the expression level.

Saya kira contoh-contoh ini tidak lagi membuat banyak kasus baik untuk ekspresi atau pendekatan tipe - sebagian besar sudah usang, dan aplikasi fungsi sepele akan diaktifkan oleh keduanya.

Diakui aplikasi fungsi berbasis level tipe, seperti yang difokuskan oleh tinganho, tim TS dan saya sendiri dapat mengekspos variabel ekspresi melalui typeof , meskipun seperti yang Anda catat, level tipe memang cenderung tertinggal di belakang fungsionalitas yang diekspos pada ekspresi tingkat. Aplikasi fungsi ini sendiri serta sintaks penyebaran yang Anda sebutkan jelas merupakan contoh utama, dan saya pasti akan senang melihatnya ditangani. Mungkin sebagian besar kesenjangan saat ini bahkan dapat diatasi di sini.

Demikian pula, pendekatan ekspresi-pertama juga akan memungkinkan tipe penyuntikan menggunakan <MyType> whatever atau whatever as MyType , meskipun sebagai typeof dalam pendekatan tipe, itu juga akan tampak seperti renungan: pertanyaan tentang penerapan fungsi yang disimpan dalam tipe/generik tetap ada, yang kemungkinan merupakan sebagian besar nilai tambah aktual dari pendekatan berbasis tipe itu (meskipun tidak disebutkan di sana) - inferensi akurat untuk fungsi tingkat tinggi, serta katakan persyaratan berbasis tipe seperti di utas promised itu.* Lebih buruk lagi, tidak seperti kasus penggunaan OP, ini tidak memiliki solusi.

Saya pikir lihat di mana ide berbenturan -- proposal saat ini memiliki pandangan yang kontradiktif tentang apakah kata kunci typeof akan mengalihkan ekspresinya ke tingkat nilai, vs. (dalam interpretasi saya) membiarkan typeof apa adanya , tetapi mengekspos fungsi sintaks aplikasi pada tingkat tipe.

Dalam pandangan saya, kontradiksi ini agak kebetulan. Saya tidak akan mengabaikan legitimasi dari kedua kasus penggunaan; jika keduanya diterapkan, saya bisa melihat kata kunci tambahan menghindari bentrokan semantik. Sejujurnya saya tidak peduli apakah kata kunci akan berakhir dengan satu atau lain cara -- saya hanya ingin mengetik sial.

*: Saya baru menyadari Anda mungkin akan menggunakan fungsi tingkat tinggi dengan mereferensikan fungsi dalam parameter dengan nama parameternya, daripada dengan menangkap tipenya dalam generik.
Sepertinya hal-hal sebenarnya dapat dikonversi di kedua arah: typeof membantu mengangkat dari tingkat nilai ke tingkat jenis, sementara declare let y: x; membantu mengangkat sesuatu dari tingkat nilai ke tingkat jenis. Tidak elegan seperti solusi OP, tapi ya.
Jika tipe fungsi dimasukkan melalui misalnya generik eksplisit, saya kira ini tidak akan membantu -- mereka akan berada di level tipe tanpa cara untuk memindahkannya.
Jika kedengarannya seperti saya sebelumnya berharap untuk membahas dasar-dasar fungsionalitas, itu mungkin karena banyak kemajuan saya pada tantangan pengetikan yang belum terpecahkan diblokir pada pengaya yang satu ini (dengan menyebutkan 5453). Tapi jika kita bisa mencari tahu hal-hal ini, itu akan sia-sia.

Sunting: Saya telah memikirkan beberapa kasus teoretis yang mungkin dikecualikan dalam pendekatan berbasis ekspresi sekarang, meskipun belum ada kejadian praktis yang terlintas dalam pikiran:

  • fungsi yang diteruskan melalui generik yang disediakan secara eksplisit.
  • fungsi yang dikembalikan oleh fungsi parameter, untuk ditangkap secara umum, yaitu (menggunakan sintaks proposal berbasis ekspresi) declare function f<G extends (...args: any[]) => R, R extends <T>(foo: T) => Bar<T>>(g: G): typeof R(baz); // error following the expression-based proposal: R is not exposed at the expression level . Ini tentu saja dapat dihitung jika Anda mengetahui dan dapat memberikan tipe parameter G , tetapi afaik sejauh ini tidak ada cara untuk mendapatkan info ini (kecuali mungkin #14400?). Saya kira kategori ini akan mencakup fungsi-fungsi yang beroperasi pada fungsi-fungsi pabrik yang digunakan dalam AngularJS yang disebutkan di atas.

@Igorbek Saya tidak mengerti apa yang Anda harapkan dengan cuplikan ini:

function use<T>(item: typeof someFunctionWithComplexReturnTypeThatUsesGenerics<T>(item)) { ... }

Sepertinya definisi melingkar.

@masaeedu maaf, maksud saya:

(beralih ke f alih-alih someFunctionWithComplexReturnTypeThatUsesGenerics )

function use<T>(item: typeof f<T>(undefined as any as T)) { ... }

Untuk menguraikan, itulah yang saat ini tidak dapat diselesaikan dengan memperkenalkan variabel palsu:

const _typeofItem = f<T>(undefined as any as T); // no T here
function use<T>(item: typeof _typeofItem) { ... }

BTW, baru menyadari bahwa proposal saat ini bertentangan dengan operator jenis kueri tipe anggota T[K] karena typeof memiliki prioritas lebih tinggi:

  • typeof x[K] saat ini berarti (typeof x)[K] di mana K _adalah_ sebuah tipe
  • jika variasi berbasis ekspresi diambil, ambiguitas akan diperkenalkan:

    • typeof x[k] berarti (typeof x)[k] di mana k adalah tipe; atau

    • typeof x[k] berarti typeof (x[k]) di mana k adalah ekspresi

Saya sebenarnya lebih suka typeof (x[k]) karena secara semantik setara, tetapi merupakan perubahan yang pasti.

Ketika saya menggunakan alat pengembang Chrome, saya mendapatkan bahwa operator anggota memiliki prioritas yang lebih tinggi. Di mana Anda menemukan itu?

image

@Igorbek : ya, sepertinya itu sebabnya type Thing2Type = typeof things['thing-2']; dari pos asli sudah berfungsi.

@dehli : Anda membandingkan level ekspresi JS dengan level tipe TS -- keduanya tidak sama. Satu berjalan di browser/node, yang lain di kompiler TS.

Saya pribadi merasa aneh bahwa TypeScript menyelesaikan tingkat tipe typeof dengan prioritas lebih tinggi daripada akses tipe yang diindeks. Ini hampir terlihat seperti bug bagi saya, jujur. (Saya ingin tahu apakah kasing itu benar-benar diuji.)

@tycho01 Gotcha. Saya berasumsi TS akan menggunakan urutan prioritas yang sama dengan JS.

@dehli level ekspresinya sama ya. Pada level tipe itu adalah hal lain dengan sintaks yang serupa, tetapi mengembalikan tipe alih-alih string.

Saya menduga prioritas mungkin merupakan hasil dari keterbatasan yang mereka bayangkan. Saya akui pertimbangan itu mungkin tidak lagi masuk akal jika fungsinya diperluas.

Pada catatan lain, jika kedua versi proposal akan diimplementasikan, tanda kurung akan berfungsi sebagai cara yang efektif untuk membuatnya tetap eksplisit di level mana kita akan berada untuk itu juga. Saya kesulitan menemukan hal-hal yang tidak terdengar seperti kompromi, tapi ya.

@Igorbek Apakah ini kasus penggunaan utama yang tersisa untuk typeof pada ekspresi arbitrer? Jika kita mendapatkan #14400, itu akan memberi kita returnof menggunakan tipe yang ditentukan pengguna biasa jika saya tidak salah.

14400 tidak akan memberi kita returnof dalam bentuk generiknya:

type Return<T extends () => R, R = any> = R;

// overloaded function
declare function f(item: number): number;
declare function f(item: string): boolean;

type fType1 = Return<typeof f>; // ??
type fType2 = typeof f(1); // number
type fType3 = typeof f('a'); // boolean

// generic function
declare function g<T>(item: T): T;

type gType1 = Return<typeof g>; // ??
type gType2 = Return<typeof g<number>>; // not supported syntax
type gType3 = typeof g(1); // number
type gType4 = typeof g<number>(1); // number
type gType5 = typeof g('a' as 'a'); // 'a'

Saya tidak mengetahui semua kasus penggunaan yang tersisa, sepertinya belum ditemukan, tetapi yang paling tidak terkalahkan dan menarik bagi saya adalah:

  • _jenis yang dipetakan bersyarat_ yang jauh lebih kuat yang bisa kita dapatkan dengan #12424, karena typeof akan menggunakan resolusi kelebihan beban standar; @tycho01 telah menunjukkan contoh elegan di promised mengetik PR #17077
  • mendapatkan jenis fungsi/metode kembali dalam konteks jenis generik (seperti yang saya tunjukkan sebelumnya)

@masaeedu : Pertanyaan bagus. Versi singkatnya kurang lebih seperti yang dikatakan @Igorbek ; untuk sedikit lebih detail dari tantangan konkret yang dipecahkan, Anda akan menemukan daftar singkat di daftar 'fitur teratas yang dibutuhkan' di #16392, di mana saya telah mencoba mengaitkan tantangan yang belum terpecahkan dengan proposal yang memungkinkannya.

Ini termasuk:

  • fungsi pabrik seperti yang digunakan oleh misalnya AngularJS, lihat tiga utas yang ditautkan di pos asli di sini. - secara realistis ini akan baik-baik saja mengingat ReturnType yang baru dibangun.
  • Dengan input yang diketahui, Anda tiba-tiba dapat menghitung jenis pengembalian yang tepat untuk hal-hal seperti reduce / map / filter / find -- apa pun yang melibatkan iterasi dan pemeriksaan. stdlib.d.ts akan menguntungkan di sana, dan begitu juga lib FP seperti Ramda/Lodash. Dalam praktiknya tidak semua input dapat diketahui (lebih banyak array seperti daftar daripada tupel), tetapi operasi map dan filter pada objek dapat diketik lebih baik daripada hanya Partial juga. Ini diperlukan untuk mengetik lebih baik perpustakaan manajemen negara seperti redux (lihat https://github.com/piotrwitek/react-redux-typescript/issues/1) dan ngrx Angular, yang sampai saat itu dipaksa menjaga API pengguna mereka tingkat rendah karena tanpa operasi map yang diketik dengan baik tidak ada cara bagi mereka untuk menghitung hasil yang diinginkan pada level tipe dari data input DRY'er.
  • Saat ini pengetikan komposisi fungsi juga tidak dapat mempertimbangkan jenis pengembalian yang bergantung pada input.
  • Tiba-tiba juga akan membuatnya layak untuk mulai mengetik hal-hal seperti lensa. - Saya kira secara teknis ini hanya masih diblokir di sini pada operasi edit dengan tipe pengembalian yang bergantung pada input. yang benar-benar mewah. mengubah tupel masih membutuhkan (bagian dari) #5453.
    Ini juga memungkinkan:
  • beroperasi pada literal boolean pada level tipe, yang sejauh ini tidak mungkin
  • membuka jenis untuk flatMap -operasi seperti seperti proposal promised itu
  • menambahkan batasan pada input tipe/fungsi, topik #15347 dan ide yang saya dapatkan dari buku 'Pengembangan berbasis tipe dengan Idris' pada tipe dependen. Ini bisa membantu untuk mengatakan disallow 0 pembagi -- pada dasarnya adalah peretasan untuk mengurangi tipe, topik #4183. Saya yakin saya belum membayangkan sebagian besar kasus penggunaan di sana, tetapi itu bisa dilakukan dengan ini, misalnya function div<B extends number, NotZero = { (v: '1') => 'whatever'; }({ (v: 0) => '0'; (v: number) => '1'; }(B))>(a: number, b: B) . Biasanya bahasa Idris diiklankan dengan operasi vektor/matriks panjang-aman, tetapi itu hanya membutuhkan ini untuk fungsi tingkat tinggi.
  • Cara untuk mengekspresikan batasan Union<T> / NonUnion<T> pada tipe input menggunakan batasan di atas + IsUnion . -- tipe itu agak rusak dan saya tidak yakin bagaimana melakukannya lagi.
  • diberikan #5453 juga, ini juga membantu mengetikkan fungsi seperti curry , Function.prototype.bind , Promise.all (bukan daftar lengkap, hanya beberapa fungsi yang dianggap menantang oleh orang lain).
  • Logika switch diusulkan di #13500, di sini diselesaikan melalui pencocokan pola kelebihan fungsi
  • tipe kondisional yang dipetakan yang merupakan topik #12424 dan #17077 ( promised ), alias pemeriksaan tipe level. Namun, itu tidak akan menyelesaikannya -- bahkan jika itu dapat digunakan untuk menghasilkan literal boolean di mana saja hari ini, hingga lahan #6606 ini kami masih belum memiliki cara untuk mengoperasikan / melakukan pengkondisian berdasarkan literal boolean tersebut pada level tipe omong-omong.
  • memeriksa keberadaan indeks string pada tipe objek seperti untuk mempertahankan ini di seluruh operasi (seperti Omit , Overwrite dari #12215), lihat komentar saya di #12424
  • Berdasarkan pemeriksaan indeks string di atas, cara untuk memperbaiki akses properti tipe objek sedemikian rupa sehingga memeriksa string yang cocok dengan nama metode prototipe Objek (misalnya toString , toLocaleString ) akan diselesaikan ke indeks string bukan daripada metode prototipe, berpotensi memperbaiki gangguan yang terkait dengan akses objek toString di semua jenis operasi lain yang sebelumnya bergantung pada akses properti objek bawaan yang bermasalah.

edit: mencoret kasus penggunaan yang diisi oleh tipe kondisional (#21496) sejak saat penulisan.

Usulan @Igorbek tentang pendekatan ekspresi-pertama akan (dengan biaya mungkin melanggar beberapa aplikasi yang tidak diketahui dari bagian daftar di atas - saya belum memikirkan sesuatu yang konkret meskipun saya takut dicat ke sudut ) tambahan berjanji untuk menutup kesenjangan antara nilai dan tingkat jenis, yang saat ini mencakup hal-hal seperti aplikasi fungsi ini (ini #6606), spread/rest (#5453), operator pernyataan ! (#17370), jenis serikat pengurangan melalui penjaga tipe (#4183, jika tidak dapat dicapai melalui batasan yang disebutkan di atas), dan mungkin lebih banyak lagi yang tidak dapat saya pikirkan.

Saya kira seluruh pertukaran ini mungkin menimbulkan beberapa pertanyaan seperti mengapa membuat semua fungsi yang sama dapat diakses dalam dua cara (tingkat tipe aktual, tingkat ekspresi diakses tertanam di tingkat tipe).

Sunting: perbarui komentar perbandingan saya di atas dengan tantangan potensial ekstra.

Sunting 2:

Sangat disayangkan di sini adalah bahwa posting asli akan membuat Anda percaya ini hanya akan memungkinkan penulisan beberapa kasus tepi tanpa beberapa solusi null & , sementara pada kenyataannya ini adalah fitur penting yang menahan TS sekarang tanpa solusi yang diketahui untuk konteks sekitar .

Itu berarti itu mempengaruhi TS yang ditulis dalam file .d.ts terpisah, yang merupakan norma tidak hanya untuk stdlib.d.ts , tetapi juga untuk semua pengetikan DT yang ditulis terpisah dari proyek JS asli mereka, yang kemungkinan kecil akan berubah sementara JS libs ingin tetap terbuka untuk alternatif seperti Flow juga.

(Ini tidak jauh lebih baik untuk pengetikan .ts , dalam jenis yang menggunakan 'solusi' OP tidak dapat diparameterisasi seperti untuk menulis ke dalam jenis yang dapat digunakan kembali tingkat yang lebih tinggi tanpa mempengaruhi tingkat ekspresi.)

@tycho01 Terima kasih untuk daftarnya; ada banyak tautan di sana yang perlu saya cerna. Saya benar-benar ingin pencocokan pola pada tipe juga, dan #6606 adalah solusi pragmatis yang baik untuk masalah tersebut. Namun bagi saya tampaknya ada kebingungan yang meningkat antara hal-hal pada tingkat nilai dan tipe, dan #6606 tidak akan meningkatkan hal-hal di area ini.

Alasan fitur ini diperlukan adalah karena kami tidak memiliki cara untuk membuat ekspresi tipe yang sesuai dengan:

  • tipe kembalinya tipe fungsi, bila diterapkan dengan argumen tipe tertentu
  • (sebelum typeof K[L] ) tipe properti pada tipe objek yang sesuai dengan kunci tipe literal string
  • (mungkin yang lain, saya perlu memeriksa daftar Anda lebih dekat)

Saya merasa sebenarnya mungkin untuk membangun ekspresi level tipe murni untuk ini, tanpa menggunakan pencampuran bersama konstruksi level nilai dan ekspresi level tipe. Akan lebih baik jika tipe seperti (a: A) => B atau A | B adalah gula pada tipe parametrized sederhana seperti Func<A, B> , Union<A, B> , dan kami memiliki alat umum untuk memanipulasi tipe parametrized (HKT, fundep atau keluarga tipe, dll.).

Saya tahu fokus berlebihan pada kesehatan bukanlah salah satu tujuan TypeScript, tetapi sekarang ada banyak konsep tingkat tipe yang berinteraksi bersama dengan cara yang tidak jelas. Beberapa jenis formalisasi tentang apa itu tipe, dan cara menetapkan aturan mekanis tentang bagaimana suatu tipe berinteraksi dengan tipe lain (subtipe, destrukturisasi, dll.) akan sangat membantu.

Saya merasa sebenarnya mungkin untuk membangun ekspresi level tipe murni untuk ini, tanpa menggunakan pencampuran bersama konstruksi level nilai dan ekspresi level tipe.

Oh, ya, saya pribadi tidak benar-benar berniat menggunakan konstruksi tingkat nilai sama sekali -- hanya ini yang saya coba. Jika tingkat nilai sangat diperlukan, saya mungkin berada di kamp berbasis ekspresi di sini sendiri. :P

Saya kira kesenjangan antara nilai dan tingkat tipe sebagian besar diperkirakan akan menyempit (apakah TC39 bergerak lebih cepat daripada TS?), dan afaik kesenjangan tersebut sudah memiliki proposal tingkat tipe yang luar biasa (lihat bagian bawah posting saya sebelumnya).

Heck, saya menyadari membangun pengetikan untuk beberapa fungsi akan berada di luar pengalaman sebagian besar pengguna.
Cara saya melihat ini adalah, saya ingin perpustakaan standar dan lib FP diketik dengan sangat baik sehingga pengguna TS bisa menulisnya dan inferensi secara otomatis diurus untuk mereka.
TS hanya memiliki beberapa operator tingkat tipe, tetapi memecahkan masalah aktual dengan mereka (contoh utama adalah Overwrite ## Overwrite / Omit ) mungkin tampak sedikit kekurangan ilmu roket untuk pengembang web biasa. Heck, itu membawa kami sampai saat ini juga, dan mereka bahkan belum prototipe / indeks / simbol - bukti.

Alangkah baiknya jika tipe seperti (a: A) => B atau A | B adalah gula pada tipe parametrized sederhana seperti Func , Union

Kita bisa membaliknya dan membuat tipe parameter sebagai alias / konstruktor tipe. Untuk operasi tipe yang mengambil Foo<Bar> , tidak masalah apakah hal Anda disederhanakan menjadi satu - itu hanya memeriksa apakah itu sesuai dengan deskripsi.
Ini hampir sama dengan apa yang dilakukan stdlib.d.ts -- Anda memiliki foo[] , tetapi ini memenuhi deskripsi Array<Foo> dan oleh karena itu berfungsi dengan pengetikan Array.prototype .
Tidak seperti itu sebenarnya membantu:

type Union2<A, B> = A | B;
type TuplizeA<Tpl extends Union2<any, any>, A, B> = [Tpl[0], Tpl[1]];
// ^ sorry, no way to access union members through e.g. [0]
// type a = TuplizeA<1 | 2>;
type TuplizeB<Tpl extends any | any> = [Tpl[0], Tpl[1]];
// ^ sorry, no way to access union members through e.g. [0]
// type b = TuplizeB<1 | 2>;
type TuplizeC<Tpl extends Union2<A, B>, A, B> = [A, B];
type c = TuplizeC<1 | 2>;
// ^ need 3 arguments, maybe fixable with #14400
type TuplizeD<Tpl extends A | B, A, B> = [A, B];
// ^ need 3 arguments, maybe fixable with #14400
type d = TuplizeD<1 | 2>;

Jadi um ya, belum menyelesaikannya tetapi baru menyadari #14400 kube sebenarnya bisa membantu di sana. Dan saya baru saja melihat Anda di sana hari ini juga!
Hal yang sama untuk fungsi sebenarnya -- yang merupakan pengingat yang baik bahwa #14400 mungkin tidak hanya melakukan tipe pengembalian fungsi di sana tetapi juga tipe param.

Pendekatan ini untuk saat ini perlu menggunakan kelebihan beban, yang sangat disayangkan karena tidak berskala, tapi ya. Untuk membuatnya generik lagi, kami pada dasarnya akan menggunakan kelebihan pencocokan pola #6606 ini untuk memicu opsi yang tepat untuk aritas yang berbeda.
Saya kira orang dapat menggunakannya untuk secara umum mengubahnya menjadi tipe Tuple, kemudian mengulanginya menggunakan pendekatan kenaikan saya untuk mengoperasikannya entah bagaimana.
Untuk serikat pekerja, saya mengharapkan cara yang lebih indah untuk mengonversi ke tipe Tuple. Itu akan menambahkan perintah sewenang-wenang, dan juga tidak bisa memikirkan sintaks/kata kunci yang cantik.

Sunting: untuk membahas kesenjangan fungsionalitas level ekspresi/tipe itu lagi:

  • aplikasi fungsi: ini #6606
  • operator pernyataan ! (#17370): setelah ini #6606 ini diselesaikan
  • pengurangan tipe serikat melalui penjaga tipe (#4183): hanya kasus umum ! di atas, dengan #6606 ini juga dapat dicapai melalui batasan (misalnya NotZero di atas).
  • spread/rest (#5453) -- bahkan jika tupel tidak dapat dimanipulasi sampai itu mendarat, ArrayLike yang serupa dapat, lihat operasi List di my Gist . dengan #6606 ini saya sekarang berpikir kita dapat mengekstrak params dari fungsi yang tidak diterapkan, meskipun mengekstraknya pada saat aplikasi (yaitu untuk mendapatkan nilai input yang tepat), masih membutuhkan 5453.

Singkatnya, jika proposal ini mendarat, saya akan mengatakan kesenjangan fungsionalitas antara level ekspresi dan tipe tidak akan membuat argumen yang sangat besar terhadap proposal rasa ekspresi di sini. Jika 5453 juga masuk, saya tidak bisa lagi memikirkan siapa pun di sana. Dan perhatikan bahwa untuk pengecualian yang jarang terjadi, solusi yang dicatat dalam posting asli di sini akan tetap valid.

Sekarang, argumen yang masih dapat dengan mudah dibuat untuk itu adalah bahwa dengan varian rasa ekspresi, bahkan sebelum level tipe mengejar operator mirroring, fungsi ini akan diekspos di bawah sintaks yang sama seperti di JS (tanpa solusi), mengurangi kurva belajar.

Sunting 2:

Saya baru menyadari bahwa trik proposal ekspresi untuk membawa tipe ke level ekspresi, 1 as any as MyType , secara logis juga berfungsi untuk fungsi itu sendiri.

Ini mungkin berarti bahwa fungsionalitas aktual yang diaktifkan oleh kedua rasa tampaknya agak mirip, perbedaan luar sebagian besar terdiri dari typeof myVar (jenis rasa) vs myVar (rasa ekspresi) untuk menggunakan variabel dalam aplikasi fungsi; untuk menggunakan jenis di dalamnya MyType (jenis rasa) vs. 1 as any as MyType (rasa ekspresi, alternatif declare let a: any; lalu <MyType>a ).

Perubahan AST dari keduanya tampaknya cukup dapat dikelola juga. Rasa ekspresi hanya membutuhkan konjungsi typeof untuk menunjuk ke ekspresi nilai sebagai gantinya; type flavor akan menyalin sintaks aplikasi fungsi yang ada ( fn<T>(arg) ) dari ekspresi ke level tipe, menghubungkan ke implementasi yang ada seperti yang diusulkan oleh Ryan di atas.

Saya pikir itu kemudian turun ke yang berikut:

Kasus untuk ekspresi rasa:

  • typeof expr dengan sintaks JS tanpa solusi sebelum dukungan tingkat tipe TS

Kasus untuk jenis rasa:

  • tidak ada perubahan yang melanggar prioritas
  • ekspresi nilai masih dapat ditangkap melalui solusi (atau melalui TS jika di bawah sintaks yang berbeda sampai operator mengejar)
  • MyType alih-alih 1 as any as MyType : tidak ada level tipe di level ekspresi Anda di level tipe di level ekspresi Anda.

Satu topik terkait yang sejauh ini tidak tersentuh di sini adalah bagaimana menyediakan this binding dalam 'aplikasi' fungsi tingkat tipe ini. Sekarang, delegasi JS menimpa ini ke metode Function.prototype , tetapi sebagaimana adanya, mereka tidak memiliki cara untuk menangani ini pada tingkat tipe.

Contoh sintaks acak, diberikan tipe fungsi F (this: Foo, a: number, b: string) => SomeReturnType yang mungkin telah dipanggil sebagai F(MyA, MyB) : F(this: MyFoo, MyA, MyB) .

Menghitung tipe pengembalian tanpa menimpa pengikatan this akan tetap seperti F(MyA, MyB) , mencerminkan bagaimana argumen tingkat tipe this biasanya diabaikan jika Anda mencoba menggunakan fungsi seperti itu di tingkat ekspresi.

Kelebihan dari contoh sintaks ini:

  • mencerminkan sintaks deklarasi

Kontra dari contoh sintaks ini:

  • mencerminkan sintaks deklarasi

Jadi, ternyata, ini sudah dalam bahasa!

Jangan terlalu bersemangat.

@DanielRosenwasser baru saja mengarahkan saya ke #12146, bug yang memungkinkan pemanggilan fungsi pada literal digunakan sebagai pengganti tipe.

_5 menit kemudian_

Ta dah! Hal jahat yang mengerikan yang seharusnya tidak pernah kita gunakan dalam produksi. Tapi menggoda...

interface String {
    passthrough<T>(v: T): T;
}

// All work
type ANumber = "".passthrough(10 * 10);
type AString = "".passthrough("hello" + "world");
type AHelloWorld = "".passthrough("hello world");
type AnArbitraryThing = "".passthrough(Object.assign({hello: "world"}, {foo: "bar"}));
type ThisCraziness = "".passthrough((() => "cows are big dogs"));

~Itu membuat Effort: Difficult pada edisi ini terlihat sedikit meragukan, sepertinya mereka melakukannya secara tidak sengaja di sana.~ Saya membaca lebih banyak dan merasa konyol, _is_ ini sulit untuk dilakukan dengan baik.

Selamat bersenang-senang @tycho01.

@TheOtherSamP Saya mencoba ini dengan TypeScript 2.4.2 dan semua tipe tersebut disimpulkan sebagai any .

@pelotom Huh, ini berfungsi di sini di 2.4.2 dan 2.5.0-dev.20170803. Targetkan es6 dan dan mode ketat.

image

Sepertinya mereka baru saja memperbaikinya, saya khawatir itu mungkin salah saya. #17628

@TheOtherSamP Tidak, tidak ada dadu. Baiklah.

@pelotom Itu aneh, ini bekerja dalam proyek yang benar-benar baru untuk saya, saya tidak tahu apa yang akan berbeda dengan pengaturan kami.

@TheOtherSamP : haha, itu cukup lucu.
Dari segi waktu sepertinya mereka memulai perbaikan di sana sedikit sebelum komentar Anda. Baiklah.

@pelotom :

Saya mencoba ini dengan TypeScript 2.4.2 dan semua tipe tersebut disimpulkan sebagai apa saja.

Cuplikannya tampaknya berfungsi di Playground (2.3.2). Pada versi terbaru di luarnya ( ^2.5.0-dev.20170626 ) saya juga mengalami kesulitan mereproduksi.

Itu membuat Effort: Difficult pada masalah ini terlihat sedikit meragukan, sepertinya mereka melakukannya secara tidak sengaja di sana.

Mereka mengacu pada implementasi berbasis tipe, yang berarti beberapa perubahan, sementara ini tampaknya menggunakan bahasa ekspresi (-> + , Object.assign , panggilan fungsi lebih lanjut).

@tycho01 Saya _think_ semuanya dimulai dengan menarik perhatian saya di #17618. Oh well, itu akan mengajari saya untuk setengah serius mempertimbangkan menggunakannya dalam produksi.

Mereka mengacu pada implementasi berbasis tipe, yang berarti beberapa perubahan, sementara ini tampaknya menggunakan bahasa ekspresi (-> +, Object.assign, panggilan fungsi lebih lanjut).

Ya, saya adalah seorang idiot dan tidak membaca seluruh masalah ini sampai setelah mengatakan itu. Sayang sekali, itu mungkin versi fitur yang lebih baik, tapi saya berharap kita bisa memilikinya sekarang. Saya banyak mendorong batas sistem tipe, dan ini atau #12424 akan membuka begitu banyak opsi.

Membuka PR di #17961.

@yortus Apakah ini mencakup kasus 'typeof literal'?
TypeScript hari ini tidak memungkinkan untuk menulis "const x: typeof 1 = 1;"

@NN--- proposal asli mencakup semua ekspresi, tetapi seperti yang saya pahami, bagian 'disetujui' hanya mencakup akses properti dan tipe pengembalian fungsi.

Bahkan jika typeof 1 diizinkan, saya tidak yakin apakah itu akan memberikan tipe literal ( 1 ) atau tipe yang lebih luas ( number ).

TypeScript hari ini tidak memungkinkan untuk menulis "const x: typeof 1 = 1;"

Mengapa tidak const x: 1 = 1; ?

@SaschaNaz Saya ingin menulis sesuatu seperti

const a = {q:1};
const b = {q:1};
const x: ReadonlyArray<typeof a> = [a,b];

Tetapi serupa tidak berfungsi dengan literal:

const x: ReadonlyArray<typeof 1> = [1,2,3];

@yortus Poin bagus tentang tipe yang tepat. Tidak memikirkan tipe literal..

@NN---: Saya yakin contoh Anda sudah berfungsi.

@tycho01 Flow sekarang memiliki tipe $Call untuk mendapatkan tipe pengembalian fungsi https://github.com/facebook/flow/commit/ac7d9ac68acc555973d495f0a3f1f97758eeedb4

Bukankah hanya mengizinkan typeof fn(...) sama dengan mengizinkan typeof dari ekspresi arbitrer?

function fn() {
  return /** whatever expression you like */;
}
type a = typeof fn();

kecuali Anda sekarang membuat fungsi runtime tanpa tujuan lain selain untuk mengetahui jenisnya?

Tidak juga. Anda sedang melakukan evaluasi tipe ekspresi, bukan eksekusi ekspresi.

@dyst5422 typeof fn() tidak akan benar-benar mengevaluasi ekspresi, itu hanya akan memberi Anda tipe pengembalian.

EDIT: mungkin itu yang Anda coba katakan, tidak yakin. Tapi saya pikir @sky87 berbicara tentang _mendefinisikan_ fungsi tanpa tujuan kecuali menggunakannya dalam ekspresi tipe, bukan _mengevaluasi_ itu.

@dyst5422 , seperti yang dikatakan @pelotom , saya tidak bermaksud bahwa Anda akan menjalankan fungsinya. Untuk menguraikan lebih lanjut: jika Anda tidak mengizinkan typeof ekspresi arbitrer tetapi Anda mengizinkan typeof dari tipe kembalinya suatu fungsi, apa yang akan saya lakukan untuk mengetahui tipe yang lebih rumit ekspresi adalah untuk membungkusnya dalam suatu fungsi sehingga saya dapat meminta tipe pengembaliannya. Itu menciptakan fungsi saat runtime meskipun hanya untuk mendapatkan tipe pengembaliannya, dan lebih banyak boilerplate untuk ditulis.

EDIT: Anda sebenarnya sudah bisa mengetahui jenis ekspresi arbitrer, ini jelek tapi berhasil

const dummy = (false as true) && /* arbitrary exp */;
type TypeOfExp = typeof dummy;

Sejujurnya saya tidak tahu peretasan mana yang lebih saya sukai. Saya pikir hal terbaik adalah dapat langsung menanyakan jenisnya menggunakan typeof .

Ah, saya mengikuti sekarang. Ya, saya pikir cara yang lebih baik adalah dapat menggunakannya sebagai

type TypeOfExp = typeof (
  false &
  "false" &
  0
)

untuk dapat secara sewenang-wenang melakukan evaluasi tipe ekspresi

Apakah mungkin untuk menanyakan jenis pengembalian dari permintaan new ? Kasus penggunaan saya: Saya ingin menulis anotasi tipe untuk fungsi yang menerima referensi ke implementasi PromiseConstructorLike (misalnya $q atau Bluebird) dan mengembalikan Janji yang dibuat oleh implementasi itu.

declare function wait<P extends PromiseConstructorLike>(time: number, implementation: P): typeof new implementation<void>((res: any, rej: any) => void);

const bluebirdPromise = wait(1e3, Bluebird);
// typeof bluebirdPromise should be instance of Bluebird

Apakah mungkin untuk menanyakan tipe pengembalian tanpa typeof , atau apakah kita harus null as FnType ?

interface Fn {
    (a: string): string;
    (a: number): boolean;
}
type Ret = Fn(number); // Ret = boolean
type Ret = typeof (null as Fn)(number);

Maaf jika pertanyaan ini sudah dijawab; Saya tidak dapat menemukan mereka.

Apa gunanya new di sini, bukankah Anda hanya ingin typeof implementation() ?

Tidak, karena implementation() bukan panggilan yang valid. PromiseConstructorLike hanya dapat dipanggil melalui new , per deklarasi tipenya. typeof implementation() adalah kesalahan jenis, sama seperti (typeof implementation)['foobar'] akan menjadi kesalahan jenis.

tautan taman bermain

Apakah mungkin untuk memperkenalkan tipe generik yang dapat disimpulkan seperti yang dilakukan FlowType? Setidaknya, dapat memecahkan masalah untuk mendapatkan jenis nilai kembalian fungsi.

type _ExtractReturn<B, F: (...args: any[]) => B> = B;
type ExtractReturn<F> = _ExtractReturn<*, F>;

@Cryrivers : lihat #14400 untuk pendekatan itu. Itu tidak benar-benar menyelesaikan masalah di mana jenis output tergantung pada input.

Akhirnya membutuhkan ini lagi hari ini untuk mengisyaratkan panggilan ke suatu fungsi yang akan kembali secara dinamis, tentu berharap itu akan mendapatkan prioritas.

Operator ReturnType<T> sedang ditambahkan ke lib.d.ts di TS 2.8, didukung oleh tipe kondisional.

Karena ReturnType<T> belum memperhitungkan tipe pengembalian yang bergantung pada tipe argumen, sebagai referensi, berikut adalah implementasi tipe $Call Flow.

edit: maaf @goodmind , saya tidak menyadari bahwa Anda sudah menautkannya dengan tepat.

Saya memperbarui posting saya sebelumnya tentang kasus penggunaan proposal ini (atau interpretasi panggilan jenisnya) berdasarkan penambahan TS baru-baru ini.
Kasus penggunaan pencocokan pola sekarang dicakup oleh #21496, meninggalkan... kasus di mana kita ingin menghitung jenis berdasarkan lambda yang disediakan oleh pengguna, misalnya curry , komposisi fungsi, map , reduce , pengeditan lensa berdasarkan lambda... hal-hal yang menyenangkan. :)

PS @thorn0 : Saya pikir kasus penggunaan Angular Anda dapat diisi dengan ReturnType (#21496) sekarang!

Saya pikir ini harus ditutupi dengan membiarkan ekspresi ini.
prop2: ketik this.prop1.big.complex;

@mhegazy Apakah ada masalah terpisah untuk melacak ini?
Sangat menjengkelkan bahwa typeof berfungsi untuk penduduk setempat tetapi tidak untuk properti, saat bekerja untuk properti statis.

class A {
    x: number;
    static y: number;
    f() {
        const a: number = 1;
        const b: typeof a = 2; // OK
        const c: this.x = 3; // No :(
        const d: this['x'] = 3; // OK
        const e: typeof A.y = 4 // OK
    }
}

@NN--- Anda selalu dapat menggunakan tipe yang diindeks untuk ini:

this['x']

@cameron-martin Tidak berfungsi. Tempat bermain

@tycho01 inferensi dan kondisional tipe baru luar biasa, tetapi juga sangat menjengkelkan karena tidak berfungsi untuk fungsi yang berlebihan. Alasan yang diberikan adalah perlu sesuatu seperti ini typeof untuk menyelesaikan jenis fungsi dari yang mungkin.

@NN cukup gunakan const d: this['x'] = 3;

Oh bagus :)

@NN--- atau gunakan

class A {
    x: number;
    static y: number;
    f() {
        const self = this;
        const a: number = 1;
        const b: typeof a = 2; // OK
        const c: typeof self.x = 3; // OK
        const d: typeof self['x'] = 3; // OK
        const e: typeof A.y = 4 // OK
    }
}

@tsofist Saya sadar bahwa karya lokal, tetapi saya menganggap ini jelek.
Ini sama dengan menyimpan 'ini' secara manual untuk panggilan balik 'function(){}' alih-alih menggunakan lambda dengan tangkapan implisit.

@NN

jelek ini.

ya, ini hanya pilihan :)

Jenis bersyarat sekarang membuat ini sebagian besar tidak relevan karena Anda dapat menulis ReturnTypeOf<T> bersama dengan alias tertentu lainnya jika Anda ingin memvalidasi serangkaian argumen tertentu. Mereka tidak dapat melakukan resolusi yang berlebihan tetapi menurut kami fitur ini tidak sebanding dengan kerumitannya hanya untuk kasus penggunaan itu.

@RyanCavanaugh saya yakin maksud Anda ReturnType<T> ?

@RyanCavanaugh sangat disayangkan - resolusi yang berlebihan adalah yang benar-benar saya butuhkan. Apakah ada masalah lain untuk melacak penambahan resolusi kelebihan beban ke tipe kondisional/inferensi?

Anda harus bisa menulisnya:

type Return1<A1, T extends (a: A1) => any> = T extends (a: A1) => infer R ? R : any;
type Return2<A1, A2, T extends (a: A1, a: A2) => any> = T extends (a: A1, a: A2) => infer R ? R : any;

declare function something(a: number): number;
declare function something(a: string): string;
declare function something(a: number, b: string): boolean;

type A = Return1<number, something>; // number
type B = Return1<string, something>; // string
type C = Return2<number, string, something>; // boolean

Saya belum mengujinya, dan Anda akan membutuhkan pembantu terpisah untuk setiap jumlah argumen.

@ForbesLindesay : something saat ini merupakan variabel tingkat ekspresi -- merujuknya dengan misalnya typeof di sini (atau mendeklarasikannya sebagai antarmuka) memperbaikinya. Saya sebenarnya tidak berhasil membuatnya menghasilkan tipe pengembalian yang sesuai (pada 2.8.0-dev.20180318 ).

@ForbesLindesay sayangnya saya tidak percaya itu berhasil; mekanisme infer akan memilih metode _last_ overload:

type Funcs = ((p1: string, p2: string) => void) & ((p1: number) => void);

type FuncPromise1<T> = T extends (p1: infer P1) => void ? (p1: P1) => Promise<[P1]> : never;
type FuncPromise2<T> = T extends (p1: infer P1, p2: infer P2) => void ? (p1: P1, p2: P2) => Promise<[P1, P2]> : never;

let foo: FuncPromise1<Funcs> & FuncPromise2<Funcs>;

image

Namun , mekanisme infer _is_ mampu menangani serikat tuple:

type Tuples = [string, string] | [number];

type TuplePromise1<T> = T extends [infer P1] ?  (p1: P1) => Promise<[P1]> : never;
type TuplePromise2<T> = T extends [infer P1, infer P2] ? (p1: P1, p2: P2) => Promise<[P1, P2]> : never;

let foo: TuplePromise1<Tuples> & TuplePromise2<Tuples>;

image

mungkin kita perlu sesuatu untuk mengizinkan kelebihan -> objek, dan fungsi -> objek, membuka bungkusnya. Lakukan pemetaan tipe dan inferensi di sana, lalu bungkus kembali ke suatu fungsi dan kelebihan beban.

@MeirionHughes :

mungkin kita perlu sesuatu untuk mengizinkan kelebihan -> objek, dan fungsi -> objek, membuka bungkusnya. Lakukan pemetaan tipe dan inferensi di sana, lalu bungkus kembali ke suatu fungsi dan kelebihan beban.

Suka (a: number, b?: string) => boolean -> { a: number, b?: string } ? Kami belum bisa mendapatkan nama param seperti itu, tetapi secara konseptual ini menjadi lebih sulit untuk params sisanya ( (a: number, ...b: string[]) => boolean ), juga karena kami tidak dapat menggunakan tipe objek untuk melakukan pemesanan.

Urutan mungkin lebih penting daripada nama, dan kita dapat mengonversi antara params dan tupel. Spread/opsional mungkin masih sedikit memperumitnya.

Ini mengurangi masalah untuk mengekstraksi kelebihan beban. Kelebihan beban seharusnya merupakan persimpangan dari tipe fungsi seperti ((a: number) => 123) & ((s: string) => 'hi') , jadi masalahnya adalah bagaimana 'membuka' tipe persimpangan (menjadi misalnya tipe Tuple) -- sampai sekarang, kami tidak memilikinya.

Saya menemukan jalur ini tidak memuaskan karena akan mengatasi kasus penggunaan yang berlebihan namun tidak mengatakan obat generik, tapi ya. Pembukaan persimpangan masih merupakan celah.

Karena masalah ini sekarang sudah ditutup, apakah ada proposal baru untuk bagian-bagian yang masih hilang? Suka cara untuk menangani tipe pengembalian tergantung pada argumen?

Karena masalah ini sekarang sudah ditutup, apakah ada proposal baru untuk bagian-bagian yang masih hilang?

tidak ada yang saya sadari.

Suka cara untuk menangani tipe pengembalian tergantung pada argumen?

tidak berpikir kami memiliki masalah pelacakan jenis panggilan.

Apakah ada dukungan awal untuk gagasan hanya menambahkan aplikasi fungsi level tipe? Saya bisa menulis proposal untuk itu. Secara sintaksis saya pikir itu jalan termudah.

type MyType<A> = {
    foo: A
}

type Wrap = {
    <T>(maybe: MyType<T>): MyType<T>;
    (maybe: any): MyType<any>;
}

type Naive = ReturnType<Wrap>; // Naive = { foo: any }
type Proposed1 = Wrap(maybe: number); // Proposed1 = { foo: number }
type Proposed2 = Wrap(maybe: MyType<number>); // Proposed2 = { foo: number }
type Proposed3 = (<T>(maybe: T) => MyType<T>)(maybe: number) // Proposed3 = { foo: number }

Kasus tepi:

const foo = <T>(a: T) => T:

type Edge1 = (typeof foo)(a: number) // Probably trivial?

type Foo = {
    <T>(a: string): T
}

type Edge2 = Foo<number>(a: string) // Should this be allowed? Probably not, because:

type Bar<A> = {
    (a: string): A
}

type Edge3 = Bar<number>(a: string) // Things are getting strange

interface Baz<A> {
    <T>(a: T): T | A
}

type Edge4 = Baz<number>(a: string) // What is this?

Apakah ada dukungan awal untuk gagasan hanya menambahkan aplikasi fungsi level tipe? Saya bisa menulis proposal untuk itu. Secara sintaksis saya pikir itu jalan termudah.

Tidak saat ini. Kami benar-benar tidak ingin menempatkan resolusi kelebihan beban di ruang tipe orde tinggi; prosesnya agak terlibat, dan mencakup beberapa lintasan untuk menyimpulkan parameter tipe, dan argumen tipe kontekstual, dll. melakukan ini dalam urutan yang lebih tinggi pertama-tama membutuhkan banyak pekerjaan, dan kedua akan menimbulkan tantangan kinerja yang tidak siap kami tangani di saat ini.

@mhegazy memiliki sikap tim tentang hal ini berubah sama sekali, mengingat pekerjaan baru-baru ini di #24897 ??

Tampaknya ada beberapa masalah seputar solusi yang dapat direduksi menjadi tipe $Call , dan tipe $Call akan membuka pintu ke cara yang relatif mudah untuk meniru tipe yang lebih tinggi; lihat https://Gist.github.com/hallettj/0fde5bd4c3ce5a5f6d50db6236aaa39e misalnya (ganti penggunaan $PropertyType dan $ObjMap dengan $Call ). EDIT: Contoh tambahan: https://github.com/facebook/flow/issues/30#issuecomment -346674472

Fitur seperti itu bisa dibilang sejalan dengan rekam jejak TypeScript dalam menemukan solusi umum yang masuk akal untuk banyak masalah, bukan?

Jenis bersyarat sekarang membuat ini sebagian besar tidak relevan karena Anda dapat menulis ReturnTypeOf<T> bersama dengan alias tertentu lainnya jika Anda ingin memvalidasi serangkaian argumen tertentu. Mereka tidak dapat melakukan resolusi yang berlebihan tetapi menurut kami fitur ini tidak sebanding dengan kerumitannya hanya untuk kasus penggunaan itu.

@RyanCavanaugh @mhegazy

Saya setuju bahwa adalah mungkin untuk melakukan sesuatu menggunakan tipe kondisional. Saya pikir itu tidak akan membawa banyak kerumitan tambahan dalam kompiler jika kita menulis ulang User.avatar menjadi User extends { avatar: infer T } ? T : never ? Jadi misalnya kita bisa menulis

export type Avatar = User extends { avatar: infer T } ? T : never;

sebagai

export type Avatar = User.avatar;

untuk meningkatkan keterbacaan.

Contoh lengkap

Misalkan kita memuat dan mengubah beberapa data, dan berakhir dengan fungsi findUser seperti ini

export function findUser() {
  return {
    username: 'johndoe',
    avatar: {
      lg: '1.jpg',
      s: '2.jpg'
    },
    repos: [
      {
        name: 'ts-demo',
        stats: {
          stars: 42,
          forks: 4
        },
        pull_requests: [
          { date: '2019-08-19', tags: ['bug', 'agreed-to-cla'] },
          { date: '2019-08-10', tags: ['bug', 'includes-tests'] },
          { date: '2019-08-07', tags: ['feature'] }
        ]
      }
    ]
  };
}

Berkat inferensi dari tipe yang dipetakan, kita dapat mengekstrak tipe dari fungsi seperti:

export type User = ReturnType<typeof findUser>;
export type Avatar = User extends { avatar: infer T } ? T : never;

Saran: ini harus mengevaluasi hal yang sama

export type Avatar = User.avatar;

Selain itu, kami bahkan dapat menegaskan bahwa User.avatar tidak boleh bertipe never .

Contoh lainnya

export type Repositories = User extends { repos: infer T } ? T : never;
export type Repository = User extends { repos: (infer T)[] } ? T : never;
export type RepositoryStats = Repository extends { stats: infer T } ? T : never;
export type PullRequests = Repository extends { pull_requests: (infer T)[] } ? T : never;
export type PullRequest = Repository extends { pull_requests: (infer T)[] } ? T : never;
export type Tags = PullRequest extends { tags: infer T } ? T : never;
export type Tag = PullRequest extends { tags: (infer T)[] } ? T : never;
export type Repositories = User.repos;
export type Repository = User.repos[];
export type RepositoryStats = User.repos[].stats;
export type PullRequests = User.repos[].pull_requests;
export type PullRequest = User.repos[].pull_requests[];
export type Tags = User.repos[].pull_requests[].tags;
export type Tag = User.repos[].pull_requests[].tags[];

Saat memetakan properti bersarang sekaligus, tidak terlalu jelas apa yang terjadi

export type Tag2 = User extends { repos: { pull_requests: { tags: (infer T)[] }[] }[] } ? T : never;

Ini akan sangat memperjelasnya

export type Tag = User.repos[].pull_requests[].tags[];

Kasus sudut

export class Hello {
  static world = 'world';
  world = 42;
}
export type ThisWillBeANumber = Hello extends { world: infer T } ? T : never;
export type ThisWillBeANumber = Hello.world;
export type ThisWillBeAString = (typeof Hello) extends { world: infer T } ? T : never;
export type ThisWillBeAString = (typeof Hello).world;

@lukaselmer Sepertinya Anda hanya ingin

export type Avatar = User["avatar"];

yang bekerja hari ini

@lukaselmer Sepertinya Anda hanya ingin

export type Avatar = User["avatar"];

yang bekerja hari ini

Itulah yang saya cari. Saya mencarinya di dokumentasi , tetapi tidak menemukannya. Terima kasih!

Apakah ini bagian dari buku pegangan, atau adakah dokumentasi resmi tentang cara kerjanya? Saya cukup akrab dengan cara menggunakannya, tetapi ketika saya mencoba mengarahkan orang ke dokumentasi, yang dapat saya temukan hanyalah jenis pelindung, yang benar-benar berbeda.

Jadi, saya perhatikan bahwa proposal ini muncul sejak tahun 2015 dan salah satu tujuan awalnya adalah untuk mendapatkan jenis properti tunggal dari sebuah antarmuka.

interface a {
 foo: bar;
 /* more types */
}

const example = (fooNeeded: [magic] a.foo ) => {};

apakah saya benar berasumsi bahwa ini masih tidak mungkin 5 tahun kemudian?

@MFry Saya pikir Anda sedang mencari sintaks ini: a['foo']

Apakah kita tahu apakah ada solusi untuk ini?

Saya mencoba mendapatkan sesuatu seperti ini:

declare function something<A, B>(): void;

type Payload = string;

const hello = something<{}, Payload>();

declare function doThing<T extends ReturnType<typeof something>>(arg: T): { payload: unknown };

doThing(hello).payload === 123; // this should validate to a string aka type Payload

https://www.typescriptlang.org/play/index.html?ts=4.0.0-dev.20200512#code/CYUwxgNghgTiAEAzArgOzAFwJYHtXwGccBbEDACy1QHMAeAQQBp4AhAPgAoBKALngDccWYAG4AUGIwBPAA4IAClCkQcUYPAC8hDDCrVxYsHgIZ45EBBWbCJMpRq0A3gF9mi5auCcuB0JFgIKOjYePDAOAAq9nQR8CAAHhggqMAE8ABKZMgwqBGyILTScjiINqQUemycsNR8EbzwjvAySipqfGgA1qg4AO74zr6R0RzmljhcAHQtHmqaGloAjABMAMwi8AD0m -AVaQTkOMgQ6vxQEMJQSbs48FDaujR3nfdFCq2eYkA

Hai @maraisr Saya tidak 100% yakin dengan apa yang ingin Anda capai. Dalam contoh Anda something mengambil dua jenis tetapi tidak menggunakannya, dan hello adalah nilai balik dari sesuatu yang akan selalu void ; Jadi doThing tidak pernah melihat tipe string pada titik mana pun.

Mungkin sesuatu seperti di bawah ini yang Anda inginkan?

declare function something<ReturnType>(): ReturnType;

type Payload = string;

const hello = () => something<Payload>();

declare function doThing<F extends () => any>(f: F): { payload: ReturnType<F> };

doThing(hello).payload === 'a string';

Ah ya - sangat menyesal tentang itu. Terima kasih atas respon yang cepat!! :100: @acutmore

void hanya untuk menunjukkan bahwa tipe pengembalian fungsi itu tidak relevan. Kedua tipe tersebut diteruskan ke tipe generik lainnya, yang pada akhirnya digunakan dalam argumen.

sesuatu seperti:

declare function something<A, B>(a: MyComplexGeneric<A>, b: B[]): { somethingA: number, somethingB: number };

// Those 2 generics influence the return object so they do get used as such. And the 2 arguments are roughly that. Its an object and an array, more-or-less. 

Fungsi doThing saya tidak terlalu peduli apa yang pertama ( A ) generik, tetapi itu peduli apa yang kedua ( B ) aktif.

Lihat bahwa something dalam usecase saya sendiri melakukan efek samping yang dibaca oleh doThing .

Jadi saya tidak bisa begitu saja mendapatkan ReturnType dari suatu fungsi - entah bagaimana saya perlu menyedot generik dari fungsi yang dibuat.


Jika Anda merasa kueri ini berada di luar cakupan masalah ini, lanjutkan perjalanan saya di StackOverflow!

@maraisr terima kasih atas info tambahannya.

Jika Anda ingin doThing untuk bisa mendapatkan tipe B asli dari something maka harus diteruskan ke hello bagaimanapun caranya. TypeScript hanya melihat hello dan tanpa bantuan itu tidak akan tahu bahwa itu adalah tipe pengembalian something .

Ini adalah salah satu cara yang bisa dilakukan:

/** Create a type that can store some extra type information **/
interface SomethingResult<T> {
    __$$__: T;
    somethingA: number;
    somethingB: number;
}

declare function something<A, B>(): SomethingResult<B>;

type Payload = string;

const hello = something<{}, Payload>();

declare function doThing<Result extends SomethingResult<any>>(arg: Result): { payload: Result['__$$__'] };

doThing(hello).payload === 1123; // error because `payload` is of type string
interface User {
  avatar: string;
}

interface UserData {
  someAvatar: User['avatar'];
}

@RyanCavanaugh Mengapa ini ditutup? Tipe Bersyarat tidak menyelesaikan ini dan banyak kasus penggunaan lainnya, dan jika ini digabungkan, itu akan membuat banyak hal menjadi mungkin.

Saya sedang mengerjakan fungsi yang dapat mengubah panggilan metode apa pun menjadi versi "bebas poin" (contoh: [].map(() => n > 5) berubah menjadi map(() => n > 5)([]) dan satu-satunya hal yang hilang adalah Tipe Bersyarat dan infer tidak dapat mendeteksi obat generik, jadi dalam fungsi generik beberapa jenis akan muncul sebagai unknown .

Jika saya bisa "memanggil" fungsi untuk mendapatkan tipe ( typeof myFunc(() => Either<string,number>) ) adalah mungkin untuk memiliki fungsi ini (yang saat ini tidak mungkin), dan membuat banyak hal lain lebih mudah dilakukan (HKT, dll. .)

Apakah kompleksitasnya sangat tinggi untuk dapat $Call suatu fungsi (seperti dalam aliran)? Saya merasa TypeScript sudah melakukannya secara otomatis.

@nythrox kami tidak merasa bahwa kebingungan sintaksis yang dapat ditimbulkannya sebanding dengan kasus-kasus di mana Anda membutuhkannya untuk mendapatkan beberapa jenis. Kasus khusus untuk menyelesaikan ekspresi panggilan dilacak di tempat lain; proposal dalam OP "izinkan ekspresi apa pun" bukanlah sesuatu yang menurut kami cocok untuk bahasa tersebut.

@RyanCavanaugh oh oke, saya mengerti. Terima kasih atas tanggapannya, apakah Anda tahu masalah apa yang melacak penyelesaian panggilan fungsi?

Saya telah mencari-cari sedikit dan belum menemukan masalah untuk tipe utilitas panggilan fungsi; satu-satunya referensi yang saya temukan adalah di #20352, yang baru saja ditautkan kembali ke masalah ini.

Kasus spesifik untuk menyelesaikan ekspresi panggilan dilacak di tempat lain

@RyanCavanaugh Pikiran menautkan ke tempat lain? 🙂.

@tjjfvi #37181 lebih spesifik tentang menyelesaikan fungsi berdasarkan inputnya. Mungkin apa yang Anda cari.

@acutmore Itu agak sejalan dengan apa yang saya cari, meskipun saya secara khusus berbicara tentang utilitas $Call flow-esque, atau sintaks lain untuk dapat mengimplementasikannya. Pendekatan yang disarankan di sana aneh, tetapi terima kasih untuk tautannya.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat