Iziworkda bizning orqada qolgan vazifamiz bor edi: Flow kod bazasini TypeScript-ga ko'chiring. Biz bir necha oylik tayyorgarlikdan so'ng, kod bazasini muzlatib qo'ymasdan, 300k LoC yadroimizni TypeScript-ga muvaffaqiyatli o'tkazganimizdan mamnunmiz!

Ushbu blog postida biz ushbu migratsiyani amalga oshirish uchun nima qilganimizni ko'rib chiqamiz. Bu 2 qismli seriyaning birinchi qismi.

Chiqish oqimi

Iziwork boshlanganda, biz tipni tekshirish vositalaridan foyda olishni xohladik. Biz Flowni tanladik. Tez oldinga, 3 yil o'tgach, menimcha, Flow o'z tezligini yo'qotdi deb aytish mumkin. Hatto Flow ortida turgan Facebook ham TypeScript-ga o'tdi. TypeScript hozirda sanoatda keng qo'llaniladi, turlar ekotizimlari ancha boy va TypeScript atrofidagi asboblar rivojlandi.

Bundan tashqari, iziwork-dagi loyihalarimizning aksariyati allaqachon TypeScript-dan foydalangan. Bizning yadromiz TypeScript-ga o'tish vaqti keldi.

Maqsadlar

Migratsiya - bu og'ir vazifa va biz aniq maqsadlarni belgilab oldik:

  • Sifatni yo'qotmasdan iloji boricha tezroq ko'chiring
  • Tur darajasida imkon qadar ko'proq ishlagan holda kodda potentsial buzilish o'zgarishlari bo'lmasligiga ishonch hosil qiling. Shuning uchun, haqiqiy, tuzilgan, ish vaqti kodi bir xil bo'lib qoladi
  • iziwork tez sur'atda, biz kodlar bazasini muzlatishni xohlamadik. Migratsiya parallel ravishda amalga oshirilishi kerak
  • Avtomatlashtiriladigan narsalarni avtomatlashtiring, iloji boricha qo'lda ishlashdan qoching (masalan, jscodeshift)

Bu bizning o'sha paytdagi sharoitimizga mos keladigan maqsadlar edi. Ushbu blog postining qolgan qismida ushbu maqsadlarga muvofiq qolish uchun qilgan ishlarimiz tushuntiriladi.

Incremental migratsiya va katta portlash migratsiyasi

Ikkita migratsiya yo'li mavjud:

1. Kodda ikki turdagi fayllarga ega bo'lish orqali Flow'dan TypeScriptga bosqichma-bosqich o'tkazing. Bu Babel 7 bekor qilish kuchidan foydalanish orqali ishlashi mumkin

  • Ko'rib chiqish oson
  • Migratsiya jarayoni osongina parallel ravishda amalga oshirilishi mumkin va kodni muzlatib qo'ymasdan haftalar/oylar davom etadi
  • Turlarni qayta ishlatish qiyin. Biz TS da Flow turini import qila olmaymiz va aksincha
  • IDE ishlab chiqarish tajribasi dahshatli. Kodni kiritish-tekshirish uchun ishlaydigan ikkita vosita, kontekst menyularida juda ko'p ma'lumot va h.k.

2. Barcha kodlarni bir marta o'tkazing

  • Ko'rib chiqish juda murakkab bo'lishi mumkin
  • Kod bazasini muzlatib qo'ymasdan qilish qiyin (spoiler ogohlantirishi - bu mumkin)
  • Ishlab chiqarish tajribasi juda yaxshi, IDE faqat TypeScript-ni boshqarishi kerak
  • Biz Flow asboblaridan butunlay xalos bo'lishimiz mumkin

Internetda topishimiz mumkin bo'lgan fikr-mulohazalarga asoslanib, hamma ikkinchi yo'l afzalroq ekanligiga rozi bo'ladi. Ko'rib chiqishning murakkabligini aniq jarayonga ega bo'lish va jarayonning ko'p qismini avtomatlashtirish orqali yumshatish mumkin. Shuning uchun biz ikkinchi yo'lni tanladik: kodlar bazasining 100% ni bir marta o'tkazish.

Barqaror kod uslubini ta'minlash

To'g'ri ko'chirishni ta'minlash uchun biz kodni ko'chirishda toza farqlarga ega bo'lishini xohlaymiz. Bu turli xil majburiyatlarni ko'rib chiqishni osonlashtiradi.

Bundan tashqari, biz avtomatik migratsiya vositalarini, skriptlarni ishga tushiramiz va biz juda ko'p ommaviy topish va almashtirishni amalga oshiramiz. Ko'pincha, bu bizning kod uslubimizni buzadi va oxir-oqibat Git tariximizni ifloslantiradi.

Shu sababli, kodingizni deterministik tarzda formatlaydigan vositadan foydalanish muhimdir. Biz Prettier-ni tanladik, bu JS ekotizimidagi deyarli yetakchi kod formati vositasi.

Siz xohlagan vositani tanlashingiz mumkin, lekin migratsiyani boshlashdan oldin Flow kodingiz “uslubga mos” ekanligiga ishonch hosil qiling.

Xavfsizlik tarmog'ini joriy qilish

Migratsiya orqali biz ko'plab kod o'zgarishlarini boshdan kechiramiz.

Shuning uchun bizga xavfsizlik tarmog'i kerak. Buni tekshirish usuli, agar biz juda ko'p kodni o'zgartirsak ham, biz hech narsani buzmaymiz. Mukammal dunyoda bu test to'plami tomonidan ta'minlanadi. Bizda test to'plami bor, lekin sinovdan o'tmagan kodning aniq yo'llari bor.

Maqsadlarimizda aytilganidek, biz imkon qadar ko'proq turdagi darajada ishlaymiz. Shuning uchun, ideal holda, barcha TypeScript izohlaridan olib tashlangan kompilyatsiya qilingan JS kodi Flow bilan bir xil bo'lishi kerak. Shuni yodda tutgan holda, biz Flow kod bazamizning tuzilgan JS kodini suratga olish g'oyasiga ega edik. Shunday qilib, biz kodni TypeScript-ga o'tkazar ekanmiz, biz kompilyatsiya qilingan kodni suratga olishimiz, uni mos yozuvlar nuqtamiz (Oqim surati) bilan solishtirishimiz va hech narsani buzmasligimizga ishonch hosil qilishimiz mumkin.

Buni amalga oshirish uchun biz bir xil qurish vositasidan foydalanishimiz kerak.

Aks holda, chiqarilgan kod biroz boshqacha bo'ladi va buni minglab fayllarni ko'rib chiqish qiyin bo'ladi. Ehtimol, siz @babel/plugin-transform-flow-strip-types plagini bilan Babeldan foydalanyapsiz. Bizning baxtimizga, Babel ham Flow, ham TypeScript-ni qo'llab-quvvatlaydi. Bu shuni anglatadiki, biz Babel-dan TypeScript kompilyatori emas, balki TypeScript yaratish vositasi sifatida foydalanishimiz mumkin. Biz tsc dan --noEmit opsiyasi bilan faqat turni tekshirish uchun foydalanamiz.

⚠️ Agar siz Oqim kodingizni koʻchirish uchun Babeldan foydalanmasangiz (masalan, flow-remove-types dan foydalansangiz), Babelga osongina koʻchirishingiz kerak va mumkin.

Jest biz xohlagan narsani qilish imkonini beruvchi ajoyib xususiyatga ega: kodni suratga olish va uni mos yozuvlar surati bilan solishtirish. Mana biz yaratgan Jest testi:

Yuqoridagi testlar ikkita narsani tekshiradi:

  1. glob funksiyasi natijasini saqlash orqali kompilyatsiya qilingan kodning fayl tuzilishi bir xil bo'lishi kerak
  2. Har bir faylning mazmuni bir xil bo'lishi kerak

Ushbu test bilan qurollangan holda, endi siz fayl tuzilishi va har bir faylning mazmunini suratga olish uchun kodingizni yaratib, testni o'tkazishni xohlaysiz. Bu katta .snap faylni yaratadi, bu Jest tomonidan ishlatiladigan mos yozuvlar surati bo'ladi.

💡 Maslahat: Kodni suratga olishdan oldin .babelrc raqamiga comments: false belgilang.

Flow Babel plagini kodingizdagi sharhlarni TypeScript plaginidan farqli ravishda formatlashi mumkin. Sharhlarni o'chirib qo'yish, ortiqcha turli xil sharh formatlari o'rniga haqiqiy kod o'zgarishlarini ko'rib chiqishga yordam beradi.

⚠️ Bizning suratimiz tayyor bo‘lgach, migratsiya filialingizni develop filialingizga o‘zgartirmang. Bu oniy tasvirni buzadi. Oʻzgarishlarni develop dan keyinroq migratsiya boʻlimiga butun kodni koʻchirganingizdan soʻng olishingiz mumkin boʻladi.

Endi har safar kodni migratsiyaning bir qismi sifatida o‘zgartirsangiz, ushbu jarayondan o‘tasiz:

  1. Babel yordamida kodni yarating
  2. Jest xavfsizlik tarmog'i testini o'tkazing

Agar u oʻtib ketsa, ish vaqti kodi oʻzgartirilmagan.
Agar u oʻtmasa, farqni koʻrib chiqishingiz mumkin boʻladi. Agar siz o'zgarishlarni yaxshi bilsangiz, suratlarni yangilash uchun jest --updateSnapshot ni ishga tushirishingiz mumkin

Yuqorida aytib o'tilganidek, ko'pincha siz tur darajasida ishlaysiz. Ammo ish vaqtini o'zgartirmoqchi bo'lgan yoki o'zgartirmoqchi bo'lgan holatlar mavjud. Misol uchun, TypeScript-ning qat'iyligi tufayli siz Flow tushunmagan bir nechta xatolarni ko'rishingiz mumkin.

Avtomatik Flow to TS konvertatsiyasini ishga tushirish

Kodni TypeScript-ga o'zgartirish va bir nechta o'rnatilgan Flow turlarini o'rnatilgan TypeScript turlariga (masalan, $Keys<T> dan keyof T gacha) solishtirish bo'yicha dastlabki ish avtomatik ravishda bajarilishi mumkin. Buni amalga oshirishga qodir bo'lgan bir nechta vositalar mavjud, xususan:

  • "Xon/oqim-ts"
  • "zxbodya/flowts"

Birinchisi ko'proq "ma'lum", ammo bizning kodimizda ishlaganda u hech qanday jurnallarsiz qulab tushdi.

Ikkinchisi kamroq ma'lum, ammo ko'proq xavfsizlikni taklif qiladi:

  • Unda ko'proq avtomatik turdagi xaritalar mavjud
  • Olingan TypeScript kodida u avtomatik ravishda Prettier dasturini ishga tushiradi
  • Olingan JS AST ning oldin va keyin aynan bir xilligini tekshiradi, agar bunday bo'lmasa, aniq, batafsil ogohlantirishlarni ko'rsatadi.

Shuning uchun biz flowts bilan borishga qaror qildik. Siz ikkala vositani sinab ko'rishingiz mumkin, sizning kilometringiz farq qilishi mumkin.

Endi siz npx flowts . ni ishga tushirishingiz mumkin. Sizning kodingiz endi TypeScript-ga aylantiriladi. Siz bir nechta ogohlantirishlarni va baʼzi xatoliklarni koʻrishingiz mumkin: tekshirish amalga oshmadi, turdagi izohlar olib tashlanganidan keyin farq. Faqat farqni tekshiring va agar mavjud bo'lsa, muammolarni tuzating. 300 ming kodli bazamizda bizda bitta muammo bor edi: ko'p qatorli shablonli satrda 2 ta ortiqcha bo'sh joy bor edi. Tuzatish arzimas edi!

Bizning kodimiz endi TypeScript-da. Asbob, AST ni tekshirish orqali, chiqish kodi bir xil bo'lishini kafolatlaydi va olib tashlash turidagi izohlardan keyingi farq bir xil bo'ladi, ya'ni kompilyatsiya qilingan .js aynan bir xil bo'ladi. Boshqacha qilib aytganda, bizning kodimiz avvalgidek ishlaydi.

TypeScript-ni qo'llab-quvvatlash uchun asboblarni sozlash

Bizning kodimiz endi TypeScript-da yozilgan! Uni qo'llab-quvvatlash uchun asboblarimizni sozlash vaqti keldi.

TypeScript-ni qo'llab-quvvatlash uchun .babelrc ni sozlash juda oddiy va faqat @babel/preset-typescript oldindan o'rnatishni qo'shish masalasidir. Shuningdek, biz .ts kengaytmasini qo'llab-quvvatlash uchun Babel CLI qo'ng'iroqlarini yangilashimiz kerak.

💡 Maslahat: onlyRemoveTypeImports: true ni oldindan oʻrnatilgan @babel/preset-typescript parametrlarida oʻrnating.

Flow Babel plagini chiqarilgan koddan faqat import type importni olib tashlaydi. TypeScript plagini biroz aqlliroq: u import type sintaksisi bilan aniq import qilinmasa ham, faqat tur sifatida ishlatiladigan barcha importlarni olib tashlaydi. Suratlaringizni ko‘rib chiqishni osonlashtirish uchun yuqoridagi parametr yordamida ushbu xatti-harakatni o‘chirib qo‘ysangiz, avvalgidek importlarni saqlab qolishingiz mumkin. Siz, albatta, (va kerak) migratsiyadan keyin standart xatti-harakatga qaytishingiz mumkin.

Shuningdek, TypeScript-ni qo'llab-quvvatlash uchun linter va test kutubxonangizni sozlashingiz kerak. Agar siz ESLint dan foydalanayotgan bo'lsangiz, typescript-eslint README ko'rsatmalariga amal qiling: https://github.com/typescript-eslint/typescript-eslint

Konfiguratsiyaning eng muhim bitlaridan biri tsconfig.json faylidir. Biz Babelni qurish vositasi sifatida ishlatganimiz sababli, siz TypeScript-ni faqat matn turini tekshirish uchun sozlashni xohlaysiz. Mana, masalan, biz iziwork-da backend uchun foydalanadigan konfiguratsiya:

E'tibor bering, biz hech qanday strict bayroqni yoqmaganmiz. Qattiq bayroqlar tsc o'tish uchun zarur bo'lgan migratsiya vaqtini osongina ikki baravar oshirishi mumkin. Migratsiyadan keyin qat'iy bayroqlarni bosqichma-bosqich yoqishni taklif qilamiz.

Barcha asboblar sozlanganda, flow check oʻrniga tsc ishga tushirish uchun skriptlaringizni (package.json, CI quvurlari) yangilashni unutmang. Shuningdek, TypeScript uchun IDE-ni sozlashni unutmang.

Shuningdek, siz kodingizni Babel bilan yaratishingiz va Jest xavfsizlik tarmog'i testini o'tkazishingiz mumkin. flowts tomonidan kafolatlanganidek, TypeScript versiyamizning transpilyatsiya qilingan kodi dastlabki Oqim kodining transpilyatsiya qilingan kodi bilan aynan bir xil ekanligini ko'rasiz.

Hozirga kelib, tsc tekshiruvidan tashqari, birlik testlari va CI o'tishi kerak. Muvaffaqiyat! Kod bazasi va asboblaringiz endi TypeScript-ni yoqdi! 🎉

Hali ko'p yo'l bor, lekin...

tsc qochmasdan yugurish

tsc ni ishga tushirganingizda, sizda juda ko'p xatolar bo'lishi mumkin. Biz buni qanday his qilishini bilamiz: bizda 20 000 xatolik bor edi.

TypeScript ko'p jihatlari bo'yicha Flowga qaraganda aqlli va qat'iyroq. Ammo tushkunlikka tushmang, bu xatolarning ko'p qismini tuzatish juda oson:

  • Sizda juda ko'p etishmayotgan @types/ paket bor
  • Ehtimol, sizning kodingizda tsconfig.json da mavjud bo'lmagan ba'zi taxalluslar mavjud.
  • Flow-da ba'zi sintaksislarga ruxsat berilgan, ammo TypeScript-da emas. Masalan, TypeScript-da TS1095: A 'set' accessor cannot have a return type annotation xatosi bor, shu bilan birga Flow-da bunga ruxsat berilgan (qaytish turi void bilan). Ushbu turdagi xatolarni tuzatish ahamiyatsiz va kod modlari yordamida tuzatilishi mumkin, bu haqda keyinroq

Garchi bu xatolarning ko'pchiligini tuzatish ahamiyatsiz bo'lsa-da, sirli tsc chiqishiga qaraganingizda aynan qaysilarini ko'rish qiyin. Keling, buni tuzatamiz!

tsc dan kengroq xato hisobotlarini olish

TypeScript tomonidan bildirilgan xatolarni yaxshiroq ko'rib chiqish uchun hisobotni sintez qila olish muhimdir. TypeScript bunday imkoniyatni ta'minlamaydi, lekin buni hal qilish uchun osongina skript yozishingiz mumkin. Bizning shaxsiy hisobotimiz quyidagicha ko'rinadi (migratsiya yakunida olingan skrinshot - boshida bizda ko'proq xatolar bor edi):

Ushbu hisobot ikkita muhim ma'lumotni ko'rsatadi:

  1. Birlashtirilgan TS xato turlari. Bu sizga vaziyatni ko'rib chiqishga imkon beradi va siz TS hujjatlarini ko'rib chiqishingiz va ba'zi xatolarni tuzatish qiyinligini aniqlashingiz mumkin.
  2. Qaysi fayllar eng muammoli ekanligini ko'rish imkonini beruvchi har bir fayldagi xatolar soni. Bu sizga migratsiya ishiga ustuvorlik berishga va vazifalarni bir nechta ishlab chiquvchilar o'rtasida taqsimlashga yordam beradi

Yuqoridagi hisobotni yaratish uchun biz yozgan tez va iflos skript "bu erda Gist sifatida mavjud".

Buning yordamida siz barcha xatolaringizni tahlil qilishni boshlashingiz mumkin. Ehtimol, avtomatik ravishda tuzatilishi mumkin bo'lgan xatolarni ko'rasiz ...

Kod modlarini qo'llash

O'z xatolarimizni tahlil qilganimizda, biz ko'p narsalarni topdik:

const foo = {}
foo.bar = true // TS2339: Property 'bar' does not exist on type '{}'

Yuqoridagi kod Flow-da to'liq amal qiladi, lekin ortiqcha xususiyat tekshiruvi tufayli u TypeScript-da emas. Endi, agar bizda bu xatolarning bir nechtasi bo'lsa, ularni qo'lda tuzatishimiz mumkin edi, lekin bizda bir necha minglab xatolar bor edi. Oqim harakati bilan moslashish uchun biz foo ni any deb e'lon qilishimiz kerak. Buni avtomatlashtirishning bir usuli bo'lishi kerak, to'g'rimi?

Bunga erishish uchun haqiqatan ham ko'plab vositalar mavjud. Biz tanlaganimiz:

  • "facebook/jscodeshift"

jscodeshift - bu bizga kod modlarini yaratishga imkon beruvchi asboblar to'plami. Codemod - bu siz tahrirlashingiz mumkin bo'lgan kirish sifatida ASTni oladigan skript. Keyin jscodeshift natijada olingan kodni dastlabki faylga yozadi. Bu regexni topish/almashtirishga qaraganda ancha kuchli.

Mana, yuqoridagi muammoni hal qiladigan kod rejimi:

Siz uni npx jscodeshift --parser ts --extensions ts -t ./codemod.js src bilan ishga tushirishingiz mumkin.

Buni qisqacha tushuntirish uchun:

  • U ASTdagi barcha o'zgaruvchan deklaratorlarni topadi
  • Ularning har biri uchun, agar u allaqachon tip annotatsiyasiga ega bo'lmasa, qiymat ob'ekt ifodasi bo'lsa va bu ob'ekt hech qanday xususiyatga ega bo'lmasa, biz tur izohini any deb belgilaymiz.

Biz "https://astexplorer.net" dan foydalanishni taklif qilamiz, bu kod modlarini yozishda juda yordam beradi.

Agar siz tizimli ravishda bir xil tarzda tuzatiladigan va qo'lda tuzatish juda uzoq bo'lgan xatolarni sezsangiz, kod modlarini yozing. Muammoni qo'lda tuzatishdan ko'ra, yozish uchun ko'proq vaqt kerak bo'lsa, codemod yozish mantiqiy emas.

Umid qilamizki, sizda bir necha yuz yoki mingta xatolik qoladi. Kod modlari bilan tugatdim deb o'ylasangiz, qolgan xatolarni qo'lda tuzatish vaqti keldi!

Qolgan xatolarni qo'lda tuzatish

Tan olaylik, bu migratsiyaning eng zerikarli bosqichi, lekin eng foydalisi, chunki oxirida siz tsc o'tishni ko'rasiz.

Hozircha siz qolgan xatolar haqida aniq tasavvurga ega bo'lishingiz kerak. Shaxsiy tsc hisobotingizga qarab, avval hal qilinishi kerak bo'lgan xatolar ro'yxatini tuzishingiz kerak. 2000 ta xato qolgani 2000 ta joydagi xatolarni tuzatishingiz kerak degani emas; biror joyda xatolikni tuzatib, boshqa joyda 100 ta xatoni tuzatishingiz mumkin. Shuning uchun aniq harakatlar rejasiga ega bo'lish muhimdir.

Bundan buyon migratsiya bilan shug'ullanadigan yagona odam bo'lmaslik mantiqan to'g'ri keladi, chunki siz bir vaqtning o'zida narsalarni ko'chirishni boshlashingiz mumkin.

Bir vaqtning o'zida siz qolgan barcha xatolar bir-biriga bog'liq emasligini va ularni endi birinchi o'ringa qo'yib bo'lmasligini bilib olasiz. Agar siz migratsiya ustida ishlayotgan 2 ta ishlab chiquvchi bo'lsangiz, bitta ishlab chiqaruvchi fayllar ro'yxatining yuqorisidan boshlab xatolarni tuzatadi, ikkinchisi esa pastdan xatolarni tuzatadi. Shunday qilib, siz hamkasblaringiz bilan mos kelmasligingizni ta'minlab, samarali ishlaysiz.

Ma’lumot uchun, iziwork’da bu qadam 2 ta ishlab chiquvchi uchun 2 kun davom etdi, 2000 ga yaqin xatoliklar qo‘lda tuzatildi.

Hammasi yaxshi, endi sizda ishlaydigan TypeScript filiali bor.

Hali tugatmadingiz! Keyingi qismda biz TypeScript filialingizni rivojlanish bilan qanday yangilash mumkinligini ko'rib chiqamiz, bu ehtimol migratsiya boshidan buyon rivojlangan bo'lishi mumkin va bundan oldin hamkasblaringiz ustida ishlagan barcha parallel filiallarni qanday ko'chirish mumkin. nihoyat barcha TypeScript kodlarini birlashtirdi.

Yangiliklarni kuzatib boring, xabardor bo'lib boring; Biz bilan qoling!