“มาตามหาคำตอบกันว่าเพราะอะไร ? ทำไมเราควรเปลี่ยนจาก React JS มาเป็น React TS ? วันนี้เราจะมาลองให้ดูกันแบบเต็ม ๆ ตา”
**บทความนี้ใช้ตัวอย่างโค้ดที่เขียนด้วย React แบบ Functional และใช้ VS code ในการ Compile และเขียนโค้ด
เขียนโดย
Developer @ borntoDev
สำหรับใครที่กำลังเขียนหรือกำลังพัฒนาเว็บด้วย React อยู่น่าจะคุ้นเคยกับการใช้ JavaScript (JS) เป็นอย่างดี ซึ่งหลายคนอาจจะเคยเห็น TypeScript (TS) ผ่านตามาบ้างและกำลังลังเลว่าจะลองขยับมาใช้ TS ดีมั้ย? ในเมื่อมันทำงานอย่างเดียวกันได้จะเปลี่ยนไปใช้ให้ยุ่งยากทำไม?
บทความนี้จะมาแนะนำสิ่งที่เปลี่ยนไปเมื่อเปลี่ยนจากการเขียน React JS ไปเป็น React TS ว่ามันมีหน้าตาของโค้ดเปลี่ยนไปยังไงบ้าง แล้วการเปลี่ยนไปเป็นแบบนี้มีข้อดีอย่างไร ถ้าพร้อมแล้วไปดูกันเลย
ขั้นตอนการติดตั้งแบบง่าย ๆ
ให้เราสร้าง React App ขึ้นมาด้วยคำสั่งด้านล่าง
(ถ้า Folder Code มีไฟล์ .tsx ขึ้นมาแสดงว่าเรามาถูกทางแล้ว)
npx create-react-app name-of-app --template typescript
ตัวอย่างการสร้าง Component สำหรับแสดงผลการคำนวณ VAT
-
การประกาศ Component JS
export default function VatResult({price, rate}) {
return (
<div>
{price+(price*rate/100)}
</div>
)
}
โค้ดตัวอย่างการเขียน Component ในไฟล์ .jsx ที่เราคุ้นเคยกันดี โดยมันจะรับ Prop 2 ตัวคือ price (ราคาสินค้า) และ rate (อัตรา % ของ VAT) แล้วนำมาแสดงผลการคำนวณ
-
การประกาศ Component TS
ถ้าเราเอาโค้ดแบบเดียวกันมาประกาศในไฟล์ .tsx เราก็จะโดนเตือน Error เรื่องที่เรายังไม่ได้กำหนด Type ทันที
ซึ่งวิธีประกาศ Type ให้เราสร้างตัวแปรประเภท type ขึ้นมาแล้วกำหนด Type ให้ Prop ตามตัวอย่างด้านล่าง
(สำหรับใครที่สงสัยเรื่อง Type ของ TypeScript อ่านเพิ่มเติมได้ที่นี่เลย TypeScript: Documentation – Everyday Types (typescriptlang.org))
type Prop = { price: number rate: number } export default function VatResult({price, rate}: Prop) { return ( <div> {price*rate} </div> ) }
พอเราสร้าง Component ทั้ง 2 แบบออกมาแล้วลองส่ง Prop price เป็นค่า String ดูจะเห็นว่า Component TS ที่กำหนด Type price เป็น number จะแสดง error ออกมา
สำหรับ JavaScript ถ้าเรายังไม่ได้รันโค้ดเพื่อดูผลลัพธ์เราก็จะยังไม่รู้ว่าการใส่ค่าเป็น String จะทำให้ผลลัพธ์การทำงานผิดไปจากที่เราต้องการ
<VatResultJS price={'9000'} rate={7}/> <VatResultTS price={9000} rate={7}/>
ผลลัพธ์ที่เกิดขึ้นจากการรันเทียบกันระหว่าง (1) การใส่ค่า ‘9000’ ที่เป็น String และ (2) การใส่ค่า 9000 ที่เป็น Number
9000630
9630
จะเห็นว่าถ้าเราเอา Component ของคนอื่นไปใช้แล้วมันแสดงผลไม่ถูกต้องจากเดิมเราอาจต้องตามไปนั่งอ่านโค้ด Component นั้น ๆ หรือถ้าอ่านไม่เข้าใจก็อาจต้องไปติดต่อกับคนที่เขียน Component ขึ้นมาเลย จะเห็นว่าการกำหนด Type ให้ Prop จะช่วยให้เราประหยัดเวลาในส่วนนี้ไปได้
ในกรณีที่เราอยากละ Prop บางตัวให้ใช้ค่า Default ใน TypeScript
เราสามารถทำได้ด้วยการใช้เครื่องหมาย ? ตอนที่ประกาศ Type สำหรับ Prop บางตัวเพื่อกำหนดให้ไม่จำเป็นต้องส่งมาตอนเรียกใช้ Component ได้ตามโค้ดด้านล่าง
type Prop = { price: number rate?: number }
และใน Component ก็สามารถกำหนดค่า Default ในกรณีที่ไม่ได้ส่ง Prop ตัวนั้นมาด้วย (กรณีไม่ใส่จะเป็น undefined)
export default function VatResultTS({price, rate = 7}: Prop) { return ( <div> {price+(price*rate/100)} </div> ) }
ตอนนี้เราก็สามารถเรียกใช้ Component VatResultTS โดยที่ส่งเพียงค่า price แล้วใช้ rate ที่ 7% ได้
<VatResultTS price={9000}/>
ผลลัพธ์ที่ออกมา
9630
อยากได้ Function คำนวณราคาสินค้าจากตะกร้าด้วย
ตัวอย่างที่มีการใช้ Type เป็น number หรือ string อาจจะยังไม่เห็นภาพประโยชน์ของ TypeScript เท่าที่ควร ลองมาดูตัวอย่างเคสที่เกิดขึ้นกับ Array ของ Object กันดูบ้าง
-
ตัวอย่างข้อมูลที่ไม่สอดคล้องกัน
const cart = [ { productName: 'a', price: 199 }, { productName: 'b', price: 248 }, { productName: 'c', price: '268' } ]
-
การประกาศ Function JS
function calculatePriceJS(cart) { return cart.reduce((sumPrice, product) => sumPrice + product.price, 0) }
Function JS ที่ใช้ reduce เพื่อหาผลรวมของ Property price ในแต่ละ Object ภายใน Array ที่ส่งเข้ามา
ความน่ากลัวของมันคือพอประกาศด้วย JavaScript Output ที่ Return ออกจาก Function จะเป็น Type any เสมอ ซึ่งต่อให้ Component TypeScript ที่มารับค่าไปใช้จะกำหนด Type ไว้แล้วมันก็ยังปล่อยให้ผ่านไปได้แม้อาจจะทำให้เกิดผลลัพธ์ที่ไม่ถูกต้องก็ตาม
44726831308.76
-
การประกาศ Function TS
ประกาศ Type ให้ข้อมูลก่อน Object แต่ละตัวก่อน
type cartType = { productName: string price: number }
นำ Type ที่ประกาศไว้แล้วมาต่อด้วยเครื่องหมาย [] เพื่อประกาศเป็นตัวแปรประเภท Array ของ Type นั้น ๆ
function calculatePriceTS(cart: cartType[]) { return cart.reduce((sumPrice, product) => sumPrice + product.price, 0) }
เมื่อนำมาลองใช้ด้วยข้อมูลเดิมก็จะโดน Error ว่าข้อมูลที่รับเข้ามามี 2 แบบ แต่ตอนที่ประกาศรับได้แบบเดียว แปลว่าข้อมูลไม่เหมาะสมที่จะนำมาใช้กับ Function นี้
เมื่อเรารู้ก็เปลี่ยนข้อมูลให้ถูกต้อง
const cartEdited = [ { productName: 'a', price: 199 }, { productName: 'b', price: 248 }, { productName: 'c', price: 268 } ]
เมื่อเปลี่ยนข้อมูลให้ Type ถูกต้องก็จะไม่ Error แล้ว
ผลลัพธ์การรันเปลี่ยนเทียบตอนที่ยังไม่แก้และหลังจากแก้ข้อมูลแล้ว
44726831308.76 765.05
สรุปข้อดีจากการเปลี่ยนมาใช้ TS
-
เหมือนมี Doc ในตัว ทำให้อ่านโค้ดได้ง่ายขึ้น Component นี้ Prop รับค่าอะไรได้บ้าง ต้องใส่เป็นอะไร
-
ช่วยให้เราเจอบัคในบางเคสก่อนโค้ดจะรัน
-
ปกติ Prop ใน JS เราไม่สนใจว่ามันมีอะไรอยู่บ้างแต่ ใน TS เราต้องประกาศไว้ก่อน
-