
JavaScript adalah salah satu bahasa dengan pengetikan dinamis. Bahasa seperti itu nyaman untuk pengembangan aplikasi yang cepat, tetapi ketika beberapa tim mengambil pengembangan satu proyek besar, lebih baik untuk memilih salah satu alat untuk pengecekan jenis dari awal.
Anda bisa mulai mengembangkan kode TypeScript atau memasukkannya dalam proyek Flow. TypeScript adalah versi kompilasi dari JavaScript yang dikembangkan oleh Microsoft. Flow, tidak seperti TypeScript, bukan bahasa, tetapi alat yang memungkinkan Anda untuk menganalisis kode dan memeriksa jenis. Anda dapat menemukan banyak artikel dan video di internet tentang pendekatan ini, serta panduan tentang cara mulai menggunakan pengetikan. Dalam artikel ini kami ingin memberi tahu Anda mengapa Flow tidak cocok untuk kami, dan bagaimana kami mulai beralih ke Scripteks.
Sedikit sejarah
Pada 2016, kami mulai mengembangkan klien web berbasis React / Redux untuk sistem ECM kami. Flow dipilih untuk menguji pengetikan karena alasan berikut:
- React and Flow adalah produk dari Facebook perusahaan yang sama.
- Flow berkembang lebih aktif.
- Flow mudah diintegrasikan ke dalam proyek.
Tetapi proyek bertambah, jumlah tim pengembangan meningkat, dan sejumlah masalah muncul ketika menggunakan Flow:
- Pengecekan tipe latar belakang Aliran menggunakan terlalu banyak sumber daya PC. Akibatnya, beberapa pengembang mematikannya dan mulai memeriksa sesuai kebutuhan.
- Ada beberapa situasi ketika dibutuhkan waktu untuk membawa kode sejalan dengan Flow ketika menulis kode itu sendiri.
- Kode mulai muncul dalam proyek, yang diperlukan hanya untuk lulus tes aliran. Misalnya, periksa ganda untuk null:
foo() { if (this.activeFormContainer == null) { return; }
- Sebagian besar pengembang menggunakan editor kode Visual Studio Code, di mana Flow tidak memiliki dukungan sebaik TypeScript. Penyelesaian otomatis (IntelliSense) tidak selalu berfungsi selama pengembangan, dan navigasi kode tidak stabil. Saya ingin memiliki kenyamanan pengembangan yang sama seperti ketika menulis dalam C # di Visual Studio.
Beberapa pengembang memiliki ide untuk mencoba beralih ke TypeScript. Untuk menguji ide transisi dan meyakinkan manajemen, kami memutuskan untuk mencoba prototipe.
Prototipe
Kami ingin menguji dua ide pada prototipe:
- Cobalah menerjemahkan keseluruhan proyek.
- Siapkan proyek sehingga Anda dapat menggunakan kedua Flow dan Script secara paralel.
Untuk ide pertama, diperlukan utilitas yang akan mengkonversi semua file proyek. Di jaringan ditemukan salah satunya. Dilihat dari deskripsi, dia akan dapat menerjemahkan sebagian besar, tetapi beberapa perubahan harus diedit oleh diri kita sendiri, atau utilitas itu sendiri harus ditambahkan. Kami dapat mengonversi proyek uji dengan sejumlah kecil file. Tetapi proyek yang sebenarnya tidak dapat dikompilasi, itu perlu untuk mengedit terlalu banyak file. Kami memutuskan untuk tidak melanjutkan ke arah ini, karena:
- Masih banyak yang harus dilakukan! Dan sementara kami akan menyelesaikan proyek, tim yang tersisa akan terus mengembangkan fungsionalitas baru, memperbaiki bug, menulis tes. Selain itu, akan membutuhkan banyak waktu untuk menggabungkan file.
- Bahkan jika kita menerjemahkan proyek dengan cara ini, maka seberapa banyak pekerjaan yang harus dilakukan oleh penguji kita!
Meskipun kami meninggalkan opsi ini, kami mendapatkan pengalaman yang bermanfaat di sana. Menjadi jelas perkiraan jumlah pekerjaan yang perlu dilakukan untuk menerjemahkan setiap file. Inilah terjemahan dari komponen React yang sederhana.

Seperti yang Anda lihat, tidak ada banyak perubahan. Pada dasarnya, mereka adalah sebagai berikut:
- hapus // @ flow;
- ganti tipe dengan antarmuka yang lebih akrab;
- tambahkan pengubah akses;
- ganti tipe dengan tipe dari pustaka ts (dari contoh pada gambar: event handler dan event itu sendiri).
Implementasi pada ide kedua akan memungkinkan pengembangan lebih lanjut, tetapi sudah pada TypeScript, dan di latar belakang untuk perlahan-lahan menerjemahkan basis kode yang ada. Ini memberikan beberapa keuntungan:
- Mudah diterjemahkan, tanpa takut kehilangan sesuatu.
- Mudah diuji.
- Mudah untuk menggabungkan perubahan.
Tetapi tidak sepenuhnya jelas apakah proyek dapat dikonfigurasi untuk bekerja dengan dua jenis pengetikan secara paralel. Pencarian di Internet tidak menghasilkan sesuatu yang konkret, jadi mereka mulai mengatasinya sendiri. Secara teori, penganalisa aliran hanya memeriksa file dengan ekstensi js / jsx dan berisi komentar:
Untuk kompiler TypeScript, file harus memiliki ekstensi ts / tsx. Dari sinilah kedua pendekatan untuk mengetik harus bekerja secara bersamaan dan tidak saling mengganggu. Berdasarkan ini, kami mengatur lingkungan proyek. Menggunakan pengalaman prototipe pertama, kami menerjemahkan beberapa file. Menyusun proyek, meluncurkan klien - semuanya bekerja seperti sebelumnya!
Lampu hijau
Dan satu hari yang indah - hari perencanaan sprint, tim kami memiliki Kisah Pengguna "Mulai Beralih ke TypeScript" di backlog, dengan daftar karya berikut:
- Siapkan webpack.
- Konfigurasikan tslint.
- Siapkan lingkungan pengujian.
- Terjemahkan file ke TypeScript.
Penyiapan paket web
Langkah pertama adalah mengajarkan webpack bagaimana menangani file dengan ekstensi ts / tsx. Untuk melakukan ini, kami menambahkan aturan ke bagian aturan dari file konfigurasi. Ts-loader yang awalnya digunakan:
Untuk mempercepat perakitan, ketik pengecekan dimatikan:
transpileOnly: true
, karena IDE sudah menunjukkan kesalahan saat menulis kode.
Tetapi ketika kami mulai menerjemahkan tindakan Redux kami, menjadi jelas bahwa mereka membutuhkan
plugin babel-plugin-transform-class-display-name untuk berfungsi. Plugin ini menambahkan properti displayName statis ke semua kelas. Setelah terjemahan, hanya tindakan ts-loader yang diproses, dan ini tidak memungkinkan plugin babel untuk diterapkan. Sebagai hasilnya, kami meninggalkan ts-loader dan memperluas aturan yang ada untuk js / jsx dengan menambahkan
babel / preset-typescript:
Agar kompiler TypeScript bekerja dengan benar, Anda perlu menambahkan file konfigurasi tsconfig.json, itu diambil dari dokumentasi.
Konfigurasikan Tslint
Kode yang ditulis menggunakan Flow juga diperiksa menggunakan eslint. TypeScript memiliki padanannya, tslint. Awalnya, saya ingin mentransfer semua aturan dari eslint ke tslint. Ada upaya untuk menyinkronkan aturan melalui plugin tslint-eslint-rules, tetapi sebagian besar aturan tidak didukung. Dimungkinkan juga untuk menggunakan eslint untuk memeriksa file ts menggunakan typescript-eslint-parser. Namun, sayangnya, hanya satu pengurai yang dapat dihubungkan ke eslint. Jika Anda hanya menggunakan ts-parser untuk semua jenis file, banyak kesalahan aneh muncul di file js dan ts. Akibatnya, kami menggunakan seperangkat aturan yang disarankan, diperluas ke persyaratan kami:
// tslint.json "extends": ["tslint:recommended", "tslint-react"]
Terjemahkan file ke dalam TypeScript
Sekarang semuanya sudah siap, dan Anda dapat mulai menerjemahkan file. Untuk mulai dengan, kami memutuskan untuk mentransfer komponen Bereaksi kecil, yang digunakan di seluruh proyek. Pilihannya jatuh pada komponen "Tombol".

Kami mengalami masalah selama proses terjemahan: tidak semua perpustakaan pihak ketiga memiliki pengetikan TypeScript, misalnya, bem-cn-lite. Pada sumber daya
TypeSearch dari Microsoft, tipe perpustakaan untuk itu tidak dapat ditemukan. untuk hampir semua pustaka yang diperlukan, kami menemukan dan menghubungkan pustaka jenis ts. Salah satu solusinya adalah terhubung melalui membutuhkan:
const b = require('bem-cn-lite');
Tetapi pada saat yang sama, masalah dengan kurangnya jenis tidak terpecahkan. Oleh karena itu, kami membuat "rintisan" untuk tipe itu sendiri, menggunakan utilitas
dts-gen :
dts-gen -m bem-cn-lite
Utilitas menghasilkan file dengan ekstensi * .d.ts. File ditempatkan di folder @types dan mengonfigurasi tsconfig.json:
// tsconfig.json "typeRoots": [ "./@types", "./node_modules/@types" ]
Selanjutnya, dengan analogi dengan prototipe, kami menerjemahkan komponen. Menyusun proyek, meluncurkan klien - semuanya berhasil! Tetapi tes itu gagal.
Pengaturan lingkungan pengujian
Untuk menguji aplikasi, kami menggunakan Storybook dan Mocha.
Storybook digunakan untuk pengujian regresi visual (
artikel ). Seperti halnya proyek itu sendiri, ia dibuat menggunakan webpack dan memiliki file konfigurasinya sendiri. Oleh karena itu, untuk bekerja dengan file ts / tsx, itu harus dikonfigurasikan secara analogi dengan konfigurasi proyek itu sendiri.
Sementara kami menggunakan ts-loader untuk membangun proyek, kami berhenti menjalankan tes Mocha. Untuk mengatasi masalah ini, tambahkan ts-node ke lingkungan pengujian:
// mocha.opts --require @babel/polyfill --require @babel/register --require test/index.js --require tsconfig-paths/register --require ts-node/register/transpile-only --recursive --reporter mochawesome --reporter-options reportDir=../../bin/TestResults,reportName=js-test-results,inlineAssets=true --exit
Tetapi setelah beralih ke Babel, Anda bisa menyingkirkannya.
Masalahnya
Dalam proses penerjemahan, kami menemukan sejumlah besar masalah dengan berbagai tingkat kompleksitas. Mereka terutama terkait dengan kurangnya pengalaman kami dengan TypeScript. Berikut ini beberapa di antaranya:
- Impor komponen / fungsi dari berbagai jenis file.
- Terjemahan komponen tingkat tinggi.
- Kehilangan riwayat perubahan.
Impor komponen / fungsi dari berbagai jenis file
Saat menggunakan komponen / fungsi dari berbagai jenis file, ekstensi file perlu ditentukan:
import { foo } from './utils.ts'
Ini memungkinkan Anda untuk menambahkan ekstensi yang valid ke file konfigurasi webpack dan eslint:
Terjemahan komponen tingkat tinggi
Dari semua jenis file, terjemahan Komponen Orde Tinggi (HOC) menyebabkan masalah paling besar. Ini adalah fungsi yang mengambil komponen pada input dan mengembalikan komponen baru. Ini terutama digunakan untuk menggunakan kembali logika, misalnya, itu bisa menjadi fungsi yang menambahkan kemampuan untuk memilih elemen:
const MyComponentWithSeletedItem = withSelectedItem(MyComponent);
Atau koneksi paling terkenal, dari perpustakaan Redux. Mengetik fungsi-fungsi seperti itu tidak sepele dan membutuhkan menghubungkan perpustakaan tambahan untuk bekerja dengan tipe. Saya tidak akan menjelaskan proses terjemahan secara terperinci, karena Anda dapat menemukan banyak manual tentang subjek di internet. Singkatnya, masalahnya adalah fungsi seperti itu abstrak: komponen apa pun dengan set properti apa pun dapat menerima input. Ini bisa berupa komponen Button dengan properti title dan onClick atau komponen Picture dengan properti alt dan imgUrl. Set properti ini tidak diketahui sebelumnya, hanya properti yang ditambahkan fungsi itu yang diketahui. Agar compiler TypeScript tidak bersumpah saat menggunakan komponen yang diperoleh dengan bantuan fungsi tersebut, perlu untuk "memotong" properti yang ditambahkan fungsi dari tipe kembali.
Untuk melakukan ini, Anda perlu:
- Tarik properti ini ke antarmuka:
interface IWithSelectItem { selectedItem: number; handleSelectedItemChange: (id: number) => void; }
- Hapus semua properti yang memasuki antarmuka IWithSelectItem dari antarmuka komponen. Untuk melakukan ini, Anda dapat menggunakan operasi Diff <T, U> dari pustaka jenis utilitas .
React.ComponentType<Diff<TPropsComponent, IWithSelectItem>>
Kehilangan riwayat perubahan
Untuk bekerja dengan sumber, misalnya, tinjauan kode, kami menggunakan Team Foundation Server. Saat menerjemahkan file, kami menemukan satu fitur yang tidak menyenangkan. Alih-alih satu file yang diubah, dua muncul di kumpulan permintaan:
- remote - versi lama file;
- dibuat - versi baru.

Perilaku ini diamati jika ada banyak perubahan dalam file (kesamaan <50%), misalnya, untuk file kecil. Untuk mengatasi masalah ini, kami mencoba menggunakan:
- perintah git mv
- jalankan dua commit: yang pertama mengubah ekstensi file, yang kedua adalah dengan koreksi langsung.
Namun, sayangnya, kedua pendekatan itu tidak membantu kami.
Ringkasan
Gunakan Flow atau TypeScript - semua orang memutuskan untuk dirinya sendiri, kedua pendekatan memiliki pro dan kontra mereka. Kami memilih TypeScript untuk diri kami sendiri. Dan Anda diyakinkan dari pengalaman Anda sendiri: jika Anda memilih salah satu pendekatan dan tiba-tiba menyadari, bahkan setelah tiga tahun itu tidak cocok untuk Anda, maka Anda selalu dapat mengubahnya. Dan untuk transisi yang lebih lancar, Anda dapat mengonfigurasi proyek, seperti kami, untuk bekerja secara paralel.
Pada saat penulisan, kami belum sepenuhnya beralih ke TypeScript, tetapi kami telah menulis ulang bagian utama - "inti" dari proyek. Dalam basis kode, Anda dapat menemukan contoh terjemahan semua jenis file, dari komponen reaksi sederhana hingga komponen tingkat tinggi. Juga, pelatihan dilakukan di antara semua tim pengembangan, dan sekarang setiap tim, sebagai bagian dari tugasnya, mentransfer bagian dari proyek ke tugas-tugas tersebut.
Kami berencana untuk menyelesaikan transisi sebelum akhir tahun, menerjemahkan tes dan buku cerita, dan bahkan mungkin menulis beberapa aturan tslint kami.
Menurut perasaan pribadi saya, saya dapat mengatakan bahwa pengembangan mulai memakan waktu lebih sedikit, pengecekan tipe dilakukan dengan cepat, sementara tidak memuat sistem, dan pesan kesalahan bagi saya secara pribadi menjadi lebih dimengerti.