TypeScript Sihir Ekspresi

TypeScript adalah bahasa yang sangat indah. Gudang senjata memiliki segala yang diperlukan untuk pengembangan berkualitas tinggi. Dan jika tiba-tiba, seseorang terbiasa dengan sketsa seks dramatis dengan JavaScript, maka saya akan mengerti. TypeScript memiliki sejumlah asumsi, sintaks yang tidak terduga, dan konstruksi menyenangkan yang menekankan keindahan, bentuk, dan isi dengan makna baru. Hari ini kita berbicara tentang mereka, tentang asumsi-asumsi ini, tentang keajaiban ekspresi. Siapa peduli, selamat datang.

Sedikit kebenaran


Benar N1.

Sebagian besar desain dan penemuan tak terduga yang diuraikan di bawah ini, pertama kali menarik perhatian saya pada halaman Stack Overflow, github atau diciptakan sendiri. Dan baru kemudian ia sadar - semua ini ada di sini atau di sini . Oleh karena itu, saya meminta Anda untuk memperlakukan dengan pengertian terlebih dahulu jika temuan yang dinyatakan tampaknya basi untuk Anda.

Benar N2.

Nilai praktis dari beberapa desain adalah 0.

Benar N3.

Contoh diuji di bawah tsc versi 3.4.5 dan target es5. Untuk jaga-jaga, di bawah konfigurasi spoiler

tsconfig.json
{
"CompilerOptions": {
"OutFile": "./target/result.js",
"Modul": "amd",
"Target": "es5",
"Deklarasi": true,
"NoImplicitAny": true,
"NoImplicitReturns": true,
"StrictNullChecks": true,
"StrictPropertyInitialization": true,
"ExperimentalDecorators": true,
"EmitDecoratorMetadata": true,
"PreserveConstEnums": true,
"NoResolve": true,
"SourceMap": true,
"InlineSources": true
},
"Termasuk": [
"./src"
]
}


Implementasi dan Warisan


Temukan : di bagian implement, Anda dapat menentukan antarmuka, tipe, dan kelas . Kami tertarik pada yang terakhir. Detail di sini

abstract class ClassA { abstract getA(): string; } abstract class ClassB { abstract getB(): string; } // , tsc     abstract class ClassC implements ClassA, ClassB { // ^  ,   implements  . abstract getA(): string; abstract getB(): string; } 

Saya pikir para pengembang TypeScript menangani 'kontrak ketat' yang dieksekusi melalui kata kunci kelas. Selain itu, kelas tidak harus abstrak.

Temukan : ekspresi diperbolehkan di bagian extends. Detail Jika mengajukan pertanyaan - apakah mungkin mewarisi dari 2 kelas, maka jawaban formalnya adalah tidak. Tetapi jika Anda maksud mengekspor fungsionalitas - ya.

 class One { one = "__one__"; getOne(): string { return "one"; } } class Two { two = "__two__"; getTwo(): string { return "two"; } } //  ,    :   IDE (  )    . class BothTogether extends mix(One, Two) { // ^   ,    extends   info(): string { return "BothTogether: " + this.getOne() + " and " + this.getTwo() + ", one: " + this.one + ", two: " + this.two; // ^   IDE   ^  } } type FaceType<T> = { [K in keyof T]: T[K]; }; type Constructor<T> = { // prototype: T & {[key: string]: any}; new(): T; }; // TODO:    ,   .       function mix<O, T, Mix = O & T>(o: Constructor<O>, t: Constructor<T>): FaceType<Mix> & Constructor<Mix> { function MixinClass(...args: any) { o.apply(this, args); t.apply(this, args); } const ignoreNamesFilter = (name: string) => ["constructor"].indexOf(name) === -1; [o, t].forEach(baseCtor => { Object.getOwnPropertyNames(baseCtor.prototype).filter(ignoreNamesFilter).forEach(name => { MixinClass.prototype[name] = baseCtor.prototype[name]; }); }); return MixinClass as any; } const bt = new BothTogether(); window.console.log(bt.info()); // >> BothTogether: one and two, one: __one__, two: __two__ 

Temukan : anonim yang dalam dan pada saat yang sama tidak berarti.

 const lass = class extends class extends class extends class extends class {} {} {} {} {}; 

Dan siapa yang akan menulis kelas kata dengan 4 meluas pada contoh di atas?

Jika demikian
 // tslint:disable const Class = class Class extends class Class extends class Class extends class Class extends class Class {} {} {} {} {}; 

Dan lagi?

Seperti ini
 // tslint:disable const lass = class Class<Class> extends class Class extends class Class extends class Class extends class Class {} {} {} {} {}; 

Yah, Anda mengerti - itu hanya kelas!

Tanda Seru - Operator dan Pengubah Tidak Terbatas



Jika Anda tidak menggunakan strictNullChecks dan pengaturan kompilasi strictPropertyInitialization,
maka kemungkinan besar pengetahuan tentang tanda seru berlalu di dekat Anda ... Selain tujuan utama, 2 peran lebih ditugaskan untuk itu.

Temukan : Tanda seru sebagai operator penegasan yang tidak nol

Operator ini memungkinkan Anda untuk mengakses bidang struktur, yang bisa nol tanpa memeriksa nol. Contoh dengan penjelasan:

  //     --strictNullChecks type OptType = { maybe?: { data: string; }; }; // ... function process(optType: OptType) { completeOptFields(optType); //   ,   completeOptFields    . window.console.log(optType.maybe!.data); // ^ -    ,    null //   !,    tsc: Object is possibly 'undefined' } function completeOptFields(optType: OptType) { if (!optType.maybe) { optType.maybe = { data: "some default info" }; } } 

Secara total, operator ini memungkinkan Anda untuk menghapus cek yang tidak perlu untuk null dalam kode, jika kami yakin ...

Temukan : Tanda seru sebagai pengubah pernyataan penetapan tugas yang pasti

Pengubah ini akan memungkinkan kita untuk menginisialisasi properti kelas nanti, di suatu tempat dalam kode, dengan opsi kompilasi ketatPropertyInisialisasi dihidupkan. Contoh dengan penjelasan:

 //     --strictPropertyInitialization class Field { foo!: number; // ^ // Notice this '!' modifier. // This is the "definite assignment assertion" constructor() { this.initialize(); } initialize() { this.foo = 0; // ^   } } 

Tetapi semua kalkulasi mini tentang tanda seru ini tidak akan masuk akal tanpa momen humor.

Pertanyaan: Apakah Anda pikir ungkapan berikut ini akan dikompilasi?

 //     --strictNullChecks type OptType = { maybe?: { data: string; }; }; function process(optType: OptType) { if (!!!optType.maybe!!!) { window.console.log("Just for fun"); } window.console.log(optType.maybe!!!!.data); } 

Jawabannya
Ya

Jenis



Setiap orang yang menulis tipe kompleks menemukan banyak hal menarik. Jadi saya beruntung.

Temukan : subtipe dapat dirujuk dengan nama bidang tipe utama.

 type Person = { id: string; name: string; address: { city: string; street: string; house: string; } }; type Address = Person["address"]; 

Saat Anda menulis sendiri, pendekatan deklarasi ini hampir tidak masuk akal. Tetapi kebetulan suatu tipe berasal dari perpustakaan eksternal, tetapi subtipe tidak.

Trik subtipe juga dapat digunakan untuk meningkatkan keterbacaan kode. Bayangkan Anda memiliki kelas dasar dengan tipe generik yang berasal dari kelas-kelas itu. Contoh di bawah ini menggambarkan apa yang telah dikatakan.

 class BaseDialog<In, Out> { show(params: In): Out {/**  .   return ... */ } } //  - class PersonDialogOld extends BaseDialog<Person[], string> {/**   */} //   class PersonDialog extends BaseDialog<Person[], Person["id"]> {/**   */} 

Cari : menggunakan sistem tipe TypeScript, dimungkinkan untuk mencapai serangkaian kombinatorial dari tipe yang dihasilkan dengan cakupan fungsionalitas yang diinginkan. Sulit dikatakan, saya tahu. Saya memikirkan formulasi ini untuk waktu yang lama. Saya akan menunjukkan kepada Anda contoh template Builder, sebagai salah satu yang paling terkenal. Bayangkan Anda perlu membangun objek tertentu menggunakan pola desain ini.

 class SimpleBuilder { private constructor() {} static create(): SimpleBuilder { return new SimpleBuilder(); } firstName(firstName: string): this { return this; } lastName(lastName: string): this { return this; } middleName(midleName: string): this { return this; } build(): string { return "what you needs"; } } const builder = SimpleBuilder.create(); //     . const result = builder.firstName("F").lastName("L").middleName("M").build(); 

Jangan melihat metode membuat redundan, konstruktor pribadi, dan umumnya penggunaan template ini di ts. Fokus pada rantai panggilan. Idenya adalah metode yang disebut harus digunakan secara ketat 1 kali. Dan IDE Anda juga harus mengetahui hal ini. Dengan kata lain, setelah memanggil metode apa pun pada instance builder, metode ini harus dikecualikan dari daftar yang tersedia. Untuk mencapai fungsi seperti itu, tipe NarrowCallside akan membantu kami.

 type ExcludeMethod<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>; type NarrowCallside<T> = { [P in keyof T]: T[P] extends (...args: any) => T ? ReturnType<T[P]> extends T ? (...args: Parameters<T[P]>) => NarrowCallside<ExcludeMethod<T, P>> : T[P] : T[P]; }; class SimpleBuilder { private constructor() {} static create(): NarrowCallside<SimpleBuilder> { return new SimpleBuilder(); } firstName(firstName: string): this { return this; } lastName(lastName: string): this { return this; } middleName(midleName: string): this { return this; } build(): string { return "what you needs"; } } const builder = SimpleBuilder.create(); const result = builder.firstName("F") // ^ -    .lastName("L") // ^ -   lastName, middleName  build .middleName("M") // ^ -   middleName  build .build(); // ^ -    build 

Temukan : menggunakan sistem tipe TypeScript, Anda dapat mengontrol urutan panggilan dengan menentukan urutan yang ketat. Dalam contoh di bawah ini, menggunakan tipe DirectCallside, kami menunjukkan ini.

 type FilterKeys<T> = ({[P in keyof T]: T[P] extends (...args: any) => any ? ReturnType<T[P]> extends never ? never : P : never })[keyof T]; type FilterMethods<T> = Pick<T, FilterKeys<T>>; type BaseDirectCallside<T, Direct extends any[]> = FilterMethods<{ [Key in keyof T]: T[Key] extends ((...args: any) => T) ? ((..._: Direct) => any) extends ((_: infer First, ..._1: infer Next) => any) ? First extends Key ? (...args: Parameters<T[Key]>) => BaseDirectCallside<T, Next> : never : never : T[Key] }>; type DirectCallside<T, P extends Array<keyof T>> = BaseDirectCallside<T, P>; class StrongBuilder { private constructor() {} static create(): DirectCallside<StrongBuilder, ["firstName", "lastName", "middleName"]> { return new StrongBuilder() as any; } firstName(firstName: string): this { return this; } lastName(lastName: string): this { return this; } middleName(midleName: string): this { return this; } build(): string { return "what you needs"; } } const sBuilder = StrongBuilder.create(); const sResult = sBuilder.firstName("F") // ^ -   firstName  build .lastName("L") // ^ -   lastName  build .middleName("M") // ^ -   middleName  build .build(); // ^ -   build 

Total



Ini semua temuan menarik saya di TypeScript hari ini. Terima kasih atas perhatian Anda dan sampai jumpa lagi.

Source: https://habr.com/ru/post/id465267/


All Articles