ข้ามไปยังเนื้อหา

Why TypeScript?

นี่คือ JavaScript ธรรมดาที่ดูโอเคแต่พังตอน runtime:

function greet(user) {
return "Hello, " + user.name.toUpperCase();
}
greet({ nam: "Ada" }); // typo: `nam` not `name`
// 💥 TypeError: Cannot read properties of undefined (reading 'toUpperCase')

typo นี้จะไม่ถูกเจอจนกว่าบรรทัดนั้นจะรัน — อาจจะบน production, อาจจะเจอกับ user โชคร้ายคนหนึ่ง TypeScript ย้ายการค้นพบนั้นมาที่ compile time:

function greet(user: { name: string }) {
return "Hello, " + user.name.toUpperCase();
}
greet({ nam: "Ada" });
// ❌ Error: Object literal may only specify known properties,
// and 'nam' does not exist in type '{ name: string }'.

คุณรู้ทันทีที่เขียน ใน editor ก่อนที่อะไรจะรัน การเปลี่ยนแปลงนั้น — จาก “พังทีหลัง ที่ไหนสักแห่ง” ไปเป็น “พังตอนนี้ ตรงนี้” — คือคุณค่าทั้งหมดของตัวเอง

TypeScript มีสอง phase ที่แยกกันสิ้นเชิง และการสับสนระหว่างสอง phase นี้คือต้นตอของความผิดพลาดของมือใหม่ส่วนใหญ่

flowchart LR
  ts[".ts source
(type + logic)"] -->|tsc: type-check| check["Type checking
(error รายงานตรงนี้)"]
  ts -->|tsc: emit| js[".js output
(type ถูกลบ)"]
  js --> run["Runtime
(ไม่มี type อยู่แล้ว)"]
type ถูกเช็ค แล้วถูกลบทิ้ง
  • Compile time — type checker อ่าน annotation และ type ที่ infer ได้ของคุณ แล้วรายงาน error ความปลอดภัยทั้งหมดเกิดขึ้นตรงนี้
  • Runtime — JavaScript ที่ emit ออกมารัน type หายไปหมดแล้ว เพราะถูกลบทิ้งตอน compile

นี่คือเหตุผลที่คุณทำแบบนี้ไม่ได้:

function isString(x: unknown) {
return x is string; // ❌ not a thing — types don't exist at runtime
}

ตอน runtime ไม่มี type string ให้เช็ค — มีแต่ value ของ JavaScript การจะ narrow type ตอน runtime คุณต้องใช้ operation จริงของ JavaScript (typeof x === "string") ที่ compiler เข้าใจ เดี๋ยวเจาะลึกในบท narrowing

compile อันนี้:

interface User { name: string; age: number; }
const u: User = { name: "Ada", age: 36 };
function older(u: User): number { return u.age + 1; }

JavaScript ที่ emit ออกมาคือ:

const u = { name: "Ada", age: 36 };
function older(u) { return u.age + 1; }

interface หายไปทั้งอัน annotation ถูกถอดออก type เพิ่ม runtime cost เท่ากับ ศูนย์ และไม่มีพฤติกรรมตอน runtime เลย — type คือบทสนทนาระหว่างคุณกับ compiler ที่จบก่อนโปรแกรมจะรัน

TypeScript ไม่ได้ฟรี และการแกล้งทำเป็นว่าฟรีก็ไม่ยุติธรรมกับคุณ:

  • build step คุณต้องมี tsc หรือ bundler ที่ strip type ออก .ts ธรรมดารันตรง ๆ ใน Node หรือ browser ไม่ได้ (แม้ว่า runtime หลายตัวจะเริ่มเพิ่มการ strip type เข้ามา)
  • learning curve type system เป็นภาษาในตัวเอง — คอร์สนี้ทั้งคอร์สมีอยู่ก็เพราะเหตุนี้
  • ช่องว่างระหว่าง type กับ runtime compiler อาจ ผิด เรื่องความจริงตอน runtime ได้ — any, cast ที่ผิด หรือ external data ที่ไม่ผ่านการ validate โกหก compiler ได้ type คือ model ไม่ใช่การการันตีเรื่องโลกภายนอก (โมดูล Practical Mastery พูดเรื่องนี้เป็นหลัก)

การแลกเปลี่ยนนี้เกือบจะคุ้มเสมอสำหรับอะไรก็ตามที่มากกว่า script ใช้แล้วทิ้ง: คุณจ่ายภาษีก้อนคงที่ แล้วได้ผลตอบแทนที่ทบต้นก้อนใหญ่กลับมาในรูปของ bug ที่ถูกจับ, การ refactor ที่ปลอดภัย และความฉลาดของ editor

เกิดอะไรขึ้นกับ type ของ TypeScript เมื่อ code ถูก compile เป็น JavaScript?
คุณค่าหลักของ TypeScript คืออะไร?
ทำไมคุณถึงเช็ค type ของ TypeScript ตรง ๆ ตอน runtime ไม่ได้?
ข้อใดเป็นต้นทุนจริงของการใช้ TypeScript?