TypeScript Tingkat Lanjut dengan Contoh Nyata (Bagian 1)
Diterbitkan: 2020-11-06Minggu lalu kami melihat sedikit pengenalan TypeScript, dan secara khusus, kami berbicara tentang bagaimana bahasa yang memperluas JavaScript ini dapat membantu kami membuat kode yang lebih kuat. Karena itu hanya pengantar, saya tidak berbicara tentang beberapa fitur TypeScript yang mungkin Anda ingin (dan mungkin perlu) gunakan dalam proyek Anda.
Hari ini saya akan mengajari Anda cara menerapkan TypeScript secara profesional dalam proyek nyata. Untuk melakukan ini, kita akan mulai dengan melihat sebagian dari kode sumber Nelio Content untuk memahami dari mana kita memulai dan batasan apa yang kita miliki saat ini. Selanjutnya, kami akan secara bertahap meningkatkan kode JavaScript asli dengan menambahkan sedikit peningkatan bertahap hingga kami memiliki kode yang diketik sepenuhnya.
Menggunakan kode sumber Nelio Content sebagai dasarnya
Seperti yang mungkin sudah Anda ketahui, Nelio Content adalah plugin yang memungkinkan Anda untuk membagikan konten situs web Anda di media sosial. Selain itu, ini juga mencakup beberapa fungsi yang bertujuan untuk membantu Anda terus-menerus menghasilkan konten yang lebih baik di blog Anda, seperti analisis kualitas posting Anda, kalender editorial untuk melacak konten mendatang yang perlu Anda tulis, dan sebagainya. .

Bulan lalu kami menerbitkan versi 2.0, desain ulang lengkap baik secara visual maupun internal dari plugin kami. Kami membuat versi ini menggunakan semua teknologi baru yang kami miliki di WordPress hari ini (sesuatu yang telah kami bicarakan baru-baru ini di blog kami), termasuk antarmuka React dan toko Redux.
Jadi, dalam contoh hari ini kita akan meningkatkan yang terakhir. Artinya, kita akan melihat bagaimana kita bisa mengetikkan toko Redux.
Pemilih Kalender Editorial Konten Nelio
Kalender editorial adalah antarmuka pengguna yang menunjukkan posting blog yang telah kami jadwalkan untuk setiap hari dalam seminggu. Ini berarti bahwa, minimal, toko Redux kami akan memerlukan dua operasi kueri: satu yang memberi tahu kami kiriman yang dijadwalkan pada hari tertentu, dan satu lagi yang, diberi ID kiriman, mengembalikan semua atributnya.
Dengan asumsi Anda telah membaca posting kami tentang masalah ini, Anda sudah tahu bahwa pemilih di Redux menerima sebagai parameter pertama status dengan semua informasi aplikasi kami diikuti dengan parameter tambahan apa pun yang mungkin diperlukan. Jadi dua contoh pemilih kami di JavaScript akan menjadi seperti ini:
function getPost( state, id ) { return state.posts[ id ]; } function getPostsInDay( state, day ) { return state.days[ day ] ?? []; } Jika Anda bertanya-tanya bagaimana saya tahu bahwa suatu negara memiliki atribut posts dan days , itu cukup sederhana: karena sayalah yang mendefinisikannya. Tapi inilah mengapa saya memutuskan untuk menerapkannya seperti ini.
Kami tahu bahwa kami ingin dapat mengakses informasi kami dari dua sudut pandang yang berbeda: kiriman dalam sehari atau kiriman menurut ID. Jadi sepertinya masuk akal untuk mengatur data kami dalam dua bagian:
- Di satu sisi, kami memiliki atribut
postsdi mana kami telah mendaftarkan semua posting yang kami peroleh dari server dan disimpan di toko Redux kami. Logikanya, kita bisa menyimpannya dalam array dan melakukan pencarian berurutan untuk menemukan postingan yang ID-nya cocok dengan yang diharapkan… tetapi sebuah objek berperilaku seperti kamus, menawarkan pencarian yang lebih cepat. - Di sisi lain, kita juga perlu mengakses postingan yang dijadwalkan pada hari tertentu. Sekali lagi, kami dapat menggunakan hanya satu larik untuk menyimpan semua kiriman dan memfilternya untuk menemukan kiriman milik hari tertentu, tetapi memiliki kamus lain menawarkan solusi pencarian yang lebih cepat.
Tindakan dan Peredam di Konten Nelio
Terakhir, jika kita menginginkan kalender yang dinamis, kita harus menerapkan fungsi yang memungkinkan kita untuk memperbarui informasi yang disimpan toko kita. Untuk kesederhanaan, kami akan mengusulkan dua metode sederhana: satu yang memungkinkan kita untuk menambahkan posting baru ke kalender dan lain yang memungkinkan kita untuk mengubah atribut dari yang sudah ada.
Pembaruan ke toko Redux memerlukan dua bagian. Di satu sisi, kami memiliki tindakan yang menandakan perubahan yang ingin kami buat dan, di sisi lain, ada peredam yang, mengingat status toko kami saat ini dan tindakan yang meminta pembaruan, menerapkan perubahan yang diperlukan ke status saat ini untuk menghasilkan keadaan baru.
Jadi, dengan mempertimbangkan hal ini, berikut adalah tindakan yang mungkin kami lakukan di toko kami:
function receiveNewPost( post ) { return { type: 'RECEIVE_NEW_POST', post, }; } function updatePost( postId, attributes ) { return { type: 'UPDATE_POST', postId, attributes, } }dan inilah peredamnya:
function reducer( state, action ) { state = state ?? { posts: {}, days: {} }; const postIds = Object.keys( state.posts ); switch ( action.type ) { case 'RECEIVE_NEW_POST'; if ( postIds.includes( action.postId ) ) { return state; } return { posts: { ...state.posts, [ action.post.id ]: action.post, }, days: { ...state.days, [ action.post.day ]: [ ...state.days[ action.post.day ], action.post.id, ], }, }; case 'UPDATE_POST'; if ( ! postIds.includes( action.postId ) ) { return state; } const post = { ...state.posts[ action.postId ], ...action.attributes, }; return { posts: { ...state.posts, [ post.id ]: post, }, days: { ...Object.keys( state.days ).reduce( ( acc, day ) => ( { ...acc, [ day ]: state.days[ day ].filter( ( postId ) => postId !== post.id ), } ), {} ), [ post.day ]: [ ...state.days[ post.day ], post.id, ], }, }; } return state; }Luangkan waktu Anda untuk memahami semuanya dan mari bergerak maju!
Dari JavaScript ke TypeScript
Hal pertama yang harus kita lakukan adalah menerjemahkan kode sebelumnya ke dalam TypeScript. Yah, karena TypeScript adalah superset dari JavaScript, itu sudah ... tetapi jika Anda menyalin dan menempelkan fungsi sebelumnya ke dalam TypeScript Playground, Anda akan melihat bahwa kompiler sedikit mengeluh karena ada terlalu banyak variabel yang tipe implisitnya adalah any . Jadi mari kita perbaiki dulu dengan menambahkan beberapa tipe dasar secara eksplisit.
Yang harus kita lakukan adalah secara eksplisit menambahkan tipe apa any ke apa pun yang "kompleks" (seperti status aplikasi kita) dan menggunakan number atau string atau apa pun yang kita inginkan ke variabel/argumen lain. Misalnya, pemilih JavaScript asli:
function getPost( state, id ) { return state.posts[ id ]; }dengan tipe TypeScript eksplisit akan terlihat seperti ini:
function getPost( state: any, id: number ): any | undefined { return state.posts[ id ]; } Seperti yang Anda lihat, tindakan sederhana mengetik kode kita (bahkan ketika kita menggunakan "tipe generik") menawarkan banyak informasi dengan pandangan sekilas; peningkatan yang jelas dibandingkan dengan JavaScript dasar! Dalam kasus ini, misalnya, kita melihat bahwa getPost mengharapkan sebuah number (ID postingan adalah bilangan bulat, ingat?) dan hasilnya akan menjadi sesuatu jika postingan ada ( any ) atau tidak sama sekali jika tidak ( undefined ).
Di sini Anda memiliki tautan dengan semua tipe kode menggunakan tipe sederhana sehingga kompiler tidak mengeluh.
Buat dan Gunakan Tipe Data Khusus di TypeScript
Sekarang setelah kompiler puas dengan kode sumber kita, saatnya untuk berpikir sedikit tentang bagaimana kita dapat memperbaikinya. Untuk ini, saya selalu mengusulkan untuk memulai dengan memodelkan konsep yang kami miliki di domain kami.

Membuat Jenis Kustom untuk Posting
Kami tahu toko kami akan berisi posting terutama, jadi saya berpendapat langkah pertama adalah memodelkan apa itu posting dan informasi apa yang kami miliki tentangnya. Kami sudah melihat cara membuat tipe kustom minggu lalu, jadi mari kita coba hari ini dengan konsep posting:
type Post = { id: number; title: string; author: string; day: string; status: string; isSticky: boolean; }; Tidak ada kejutan di sini, kan? Post adalah objek yang memiliki beberapa atribut, seperti id numerik, title teks, dan sebagainya.
Informasi penting lainnya yang dimiliki toko Redux adalah, Anda dapat menebaknya, statusnya. Pada bagian sebelumnya kita telah membahas atribut yang dimilikinya, jadi mari kita definisikan bentuk dasar dari tipe State kita:
type State = { posts: any; days: any; }; Meningkatkan Tipe State
Sekarang kita tahu State memiliki dua atribut ( posts dan days ), tetapi kita tidak tahu banyak tentang apa itu, karena bisa berupa any saja. Kami mengatakan kami ingin kedua atribut menjadi kamus. Artinya, diberikan kueri tertentu (entah ID posting untuk posts atau tanggal untuk days ), kami menginginkan data terkait (masing-masing posting atau daftar posting). Kami tahu kami dapat mengimplementasikan kamus menggunakan objek, tetapi bagaimana kami merepresentasikan kamus di TypeScript?
Jika kita melihat dokumentasi TypeScript, kita akan melihat bahwa itu mencakup beberapa jenis utilitas untuk menangani situasi yang cukup umum. Secara khusus, ada tipe yang disebut Record yang tampaknya menjadi yang kita inginkan: ini memungkinkan kita untuk mengetik variabel menggunakan pasangan kunci/nilai di mana kunci memiliki tipe Keys tertentu dan nilainya bertipe Type . Jika kita menerapkan tipe ini pada contoh kita, kita akan mendapatkan sesuatu seperti ini:
type State = { posts: Record<number, Post>; days: Record<string, number[]>; }; Dari sudut pandang kompiler, tipe Record bekerja sedemikian rupa sehingga, dengan nilai Keys (dalam contoh kita, number for posts dan string for days ), hasilnya akan selalu menjadi objek tipe Type (dalam kasus kita, a Post atau number[] , masing-masing). Masalahnya bukan itu yang kami inginkan agar kamus berperilaku: ketika kami mencari kiriman tertentu menggunakan ID-nya, kami ingin kompiler mengetahui bahwa kami mungkin atau mungkin tidak menemukan kiriman terkait, yang berarti hasilnya bisa berupa Post atau undefined .
Untungnya, kami dapat dengan mudah memperbaikinya dengan menggunakan tipe utilitas lain, tipe Partial :
type State = { posts: Partial< Record<number, Post> >; days: Partial< Record<string, number[]> >; };Meningkatkan Kode Kami dengan Alias Jenis
Lihatlah atribut posts di negara kita… Apa yang Anda lihat? Kamus yang mengindeks Post jenis Postingan dengan angka, bukan? Sekarang bayangkan diri Anda meninjau kode ini di tempat kerja. Jika Anda menemukan tipe seperti itu, Anda mungkin berasumsi bahwa number pengindeksan posting mungkin adalah ID dari posting yang diindeks… tapi itu hanya asumsi; Anda harus meninjau kode untuk memastikannya. Dan bagaimana dengan days ? "Daftar pengindeksan string acak dari angka." Itu tidak terlalu membantu, bukan?
Jenis TypeScript membantu kami menulis kode yang lebih kuat berkat pemeriksaan kompiler, tetapi mereka menawarkan lebih dari itu. Jika Anda menggunakan tipe yang bermakna, kode Anda akan didokumentasikan dengan lebih baik dan akan lebih mudah dipelihara. Jadi mari kita alias tipe yang ada untuk membuat tipe yang bermakna, ya?
Misalnya, mengetahui bahwa ID posting ( number ) dan tanggal ( string ) relevan dengan domain kita, kita dapat dengan mudah membuat alias tipe berikut:
type PostId = number; type Day = string;dan kemudian tulis ulang tipe asli kita menggunakan alias ini:
type Post = { id: PostId; title: string; author: string; day: Day; status: string; isSticky: boolean; }; type State = { posts: Partial< Record<PostId, Post> >; days: Partial< Record<Day, PostId[]> >; }; Tipe alias lain yang dapat kita gunakan untuk meningkatkan keterbacaan kode kita adalah tipe Dictionary , yang “menyembunyikan” kerumitan penggunaan Partial dan Record di belakang struktur yang nyaman:
type Dictionary<K extends string | number, T> = Partial< Record<K, T> >;membuat kode sumber kami lebih jelas:
type State = { posts: Dictionary<PostId, Post>; days: Dictionary<Day, PostId[]>; }; Dan itu saja! Di sana Anda memilikinya! Hanya dengan tiga jenis alias sederhana, kami dapat mendokumentasikan kode dengan cara yang jelas lebih baik daripada menggunakan komentar. Setiap pengembang yang datang setelah kami akan dapat mengetahui, sekilas, bahwa posts adalah kamus yang mengindeks objek bertipe Post menggunakan PostId mereka dan days itu adalah struktur data yang, diberikan Day , mengembalikan pengidentifikasi posting daftar. Itu cukup mengagumkan, jika Anda bertanya kepada saya.
Tetapi tidak hanya definisi tipe itu sendiri yang lebih baik… jika kita menggunakan tipe baru ini di seluruh kode kita:
function getPost( state: State, id: PostId ): Post | undefined { return state.posts[ id ]; }itu juga mendapat manfaat dari lapisan semantik baru ini! Anda dapat melihat versi baru dari kode yang diketik di sini.
Oh, omong-omong, perlu diingat bahwa alias tipe, dari sudut pandang kompiler, tidak dapat dibedakan dari tipe "asli". Ini berarti bahwa, misalnya, PostId dan number benar-benar dapat dipertukarkan. Jadi jangan berharap kompiler memicu kesalahan jika Anda menetapkan PostId ke number atau sebaliknya (seperti yang Anda lihat dalam contoh kecil ini); mereka hanya berfungsi untuk menambahkan semantik ke kode sumber kami.
Langkah selanjutnya
Seperti yang Anda lihat, Anda dapat mengetik kode JavaScript menggunakan tipe TypeScript secara bertahap dan, dengan melakukan itu, kualitas dan keterbacaannya meningkat. Dalam posting hari ini kita telah melihat secara rinci contoh implementasi nyata dari aplikasi React + Redux dan kita telah melihat bagaimana itu dapat ditingkatkan dengan sedikit usaha. Tapi jalan kita masih panjang.
Dalam posting berikutnya kita akan mengetik semua variabel/argumen yang tersisa yang saat ini menggunakan tipe any dan kita juga akan mempelajari beberapa prestasi TypeScript tingkat lanjut. Saya harap Anda menyukai bagian pertama ini dan, jika Anda menyukainya, silakan bagikan dengan teman dan kolega Anda.
Gambar unggulan oleh Danielle MacInnes di Unsplash.
