
TypeScript memiliki dukungan bawaan untuk sintaks JSX dan kompiler TypeScript menyediakan alat yang berguna untuk mengatur proses kompilasi JSX. Pada dasarnya, ini memungkinkan untuk menulis DSL yang diketik menggunakan JSX. Artikel ini akan membahas persis ini - cara menulis DSL dari r menggunakan JSX. Tertarik, saya minta kucing.
→ Repositori dengan contoh yang sudah jadi.
Dalam artikel ini, saya tidak akan menunjukkan kemungkinan dengan contoh yang terkait dengan web, Bereaksi, dan sejenisnya. Contoh tidak dari web akan menunjukkan bahwa kemampuan JSX tidak terbatas pada Bereaksi, komponennya dan generasi html dalam kasus umum. Pada artikel ini, saya akan menunjukkan bagaimana menerapkan DSL untuk menghasilkan objek pesan untuk Slack .
Berikut adalah kode yang kami ambil sebagai dasar. Ini adalah pabrik pesan kecil dengan jenis yang sama:
interface Story { title: string link: string publishedAt: Date author: { name: string, avatarURL: string } } const template = (username: string, stories: Story[]) => ({ text: `:wave: ${username}, .`, attachments: stories.map(s => ({ title, color: '#000000', title_link: s.link, author_name: s.author.name, author_icon: s.author.avatarURL, text: ` _${s.publishedAt}_.` }) })
Tampaknya terlihat bagus, tetapi ada satu hal yang dapat ditingkatkan secara signifikan - keterbacaan . Misalnya, perhatikan properti color
tidak dapat dipahami, dua bidang untuk judul ( title
dan title_link
), atau garis bawah dalam text
(teks di dalam _
akan dicetak miring ). Semua ini mencegah kita untuk memisahkan konten dari detail gaya, sehingga sulit untuk menemukan apa yang penting. Dan dengan masalah seperti itu, DSL seharusnya membantu.
Berikut adalah contoh yang sama yang baru saja ditulis dalam JSX:
const template = (username: string, stories: Story[]) => <message> :wave: ${username}, . {stories.map(s => <attachment color='#000000'> <author icon={s.author.avatarURL}>{s.author.name}</author> <title link={s.link}>{s.title}</title> <i>{s.publishedAt}</i>. </attachment> )} </message>
Jauh lebih baik! Segala sesuatu yang harus hidup bersama telah bersatu, detail gaya dan konten jelas dipisahkan - keindahan dalam satu kata.
Menulis DSL
Sesuaikan proyek
Pertama, Anda perlu mengaktifkan JSX dalam proyek dan memberi tahu kompiler bahwa kami tidak menggunakan Bereaksi, bahwa JSX kami perlu dikompilasi secara berbeda.
// tsconfig.json { "compilerOptions": { "jsx": "react", "jsxFactory": "Template.create" } }
"jsx": "react"
termasuk dukungan JSX dalam proyek dan kompiler mengkompilasi semua elemen JSX ke dalam panggilan React.createElement
. Dan opsi "jsxFactory"
mengkonfigurasi kompiler untuk menggunakan elemen JSX dari pabrik kami.
Setelah pengaturan sederhana ini, kode formulir:
import * as Template from './template' const JSX = <message>Text with <i>italic</i>.</message>
akan dikompilasi
const Template = require('./template'); const JSX = Template.create('message', null, 'Text with ', Template.create('i', null, 'italic'), '.');
Jelaskan Tag JSX
Sekarang setelah kompiler tahu apa yang harus dikompilasi dengan JSX, kita perlu mendeklarasikan tag itu sendiri. Untuk melakukan ini, kami menggunakan salah satu fitur keren TypeScript - yaitu, deklarasi namespace lokal. Untuk kasus dengan JSX, TypeScript mengharapkan bahwa proyek memiliki namespace JSX
(lokasi spesifik file tidak masalah) dengan antarmuka IntrinsicElements
di mana tag dijelaskan. Compiler menangkap mereka dan menggunakannya untuk memeriksa tipe dan untuk petunjuk.
Di sini kami mendeklarasikan semua tag JSX untuk DSL kami dan semua atributnya. Bahkan, nama kunci di antarmuka adalah nama dari tag itu sendiri, yang akan tersedia dalam kode. Nilai adalah deskripsi atribut yang tersedia. Beberapa tag (dalam kasus kami) mungkin tidak memiliki atribut, yang lain mungkin opsional atau bahkan perlu.
Pabrik itu sendiri - Template.create
Pabrik kami dari tsconfig.json
adalah topik pembicaraan. Ini akan digunakan dalam runtime untuk membuat objek.
Dalam kasus paling sederhana, mungkin terlihat seperti ini:
type Kinds = keyof JSX.IntrinsicElements
Tag yang hanya menambah gaya pada teks di dalamnya mudah untuk ditulis (dalam kasus kami): pabrik kami hanya membungkus isi tag dalam string dengan _
di kedua sisi. Masalah dimulai dengan tag kompleks. Sebagian besar waktu saya habiskan bersama mereka, mencari solusi yang lebih bersih. Apa masalah sebenarnya?
Dan kompiler mencetak tipe <message>Text</message>
ke any
. Yang tidak mendekati DSL yang diketik, yah, oke, bagian kedua dari masalahnya adalah bahwa jenis semua tag akan menjadi satu setelah melewati pabrik - ini adalah batasan JSX itu sendiri (dalam Bereaksi, semua tag dikonversi ke ReactElement).
Generik pergi untuk menyelamatkan!
Hanya Element
ditambahkan dan sekarang kompiler akan menampilkan semua tag JSX ke tipe Element
. Ini juga perilaku kompiler standar - gunakan JSX.Element
sebagai jenis untuk semua tag.
Element
kami hanya memiliki satu metode umum - casting ke tipe objek pesan. Sayangnya, ini tidak selalu berfungsi, hanya pada <message/>
tingkat atas dan ini akan habis.
Dan di bawah spoiler adalah versi lengkap dari pabrik kami.
Kode pabrik itu sendiri import { flatten } from 'lodash' type Kinds = keyof JSX.IntrinsicElements
→ Repositori dengan contoh yang sudah jadi.
Alih-alih sebuah kesimpulan
Ketika saya melakukan percobaan ini, tim TypeScript hanya memiliki pemahaman tentang kekuatan dan keterbatasan apa yang mereka lakukan dengan JSX. Sekarang kemampuannya bahkan lebih besar dan pabrik dapat ditulis bersih. Jika Anda ingin mencari-cari dan memperbaiki repositori dengan contoh - Selamat datang dengan permintaan tarik.