โหมดมืด
บทที่ 01 — ติดตั้งและตั้งค่า TypeScript
⏱ ใช้เวลา 1–2 ชั่วโมง — บทนี้ต้องลงมือทำตามไปด้วย เปิด terminal ไว้เลย
📌 เวอร์ชันอ้างอิงของบทนี้: TypeScript 5.7 (ออกปลายปี 2024 — เป็น baseline ของหนังสือเล่มนี้) ฟีเจอร์บางตัวที่จะพูดถึงในบทหลัง ๆ ต้องการเวอร์ชันที่สูงกว่านี้ — จะระบุ "ต้องการ TS เวอร์ชัน X" กำกับเมื่อใช้
บทนี้จะพาคุณทำอะไร
บทที่แล้วเราเข้าใจ "ภาพใหญ่" ไปแล้ว บทนี้เราจะลงมือจริง:
- ติดตั้ง TypeScript ลงเครื่อง
- เขียนไฟล์
.tsไฟล์แรก แล้วทำให้มันรันได้ - ทำความรู้จัก
tsconfig.json— ไฟล์ตั้งค่าที่สำคัญที่สุด (จะอธิบายทีละบรรทัด ไม่ปล่อยให้งง) - รู้จักวิธีรันโค้ด TypeScript แบบต่าง ๆ และเลือกใช้ให้ถูก
- ตั้งค่า editor ให้พร้อมทำงาน
⚠️ สิ่งที่ต้องมีก่อน: เครื่องคุณต้องติดตั้ง Node.js ไว้แล้ว
แนะนำให้ใช้ เวอร์ชันที่เป็น LTS (Long-Term Support = รุ่นที่ได้รับการดูแลระยะยาว มั่นคง เหมาะกับมือใหม่) เช่น Node 22 LTS ขึ้นไป ในบท Part 2.4 เราจะใช้ฟีเจอร์ "รันไฟล์
.tsตรง ๆ" ซึ่งพฤติกรรมต่างกันตามเวอร์ชัน — Node 22 ใช้ได้แต่ต้องเติม flag--experimental-strip-typesเอง ส่วน Node เวอร์ชันที่ใหม่กว่านั้นบางรุ่นเปิดฟีเจอร์นี้ให้เป็นค่าเริ่มต้น (ไม่ต้องเติม flag) — ฟีเจอร์นี้ยังค่อนข้างใหม่/อยู่ในช่วงทดลอง ควรตรวจสอบสถานะล่าสุดจากเอกสาร Node.js อีกทีถ้ามี Node เวอร์ชันเก่ากว่า 22 อยู่ ก็ยังเรียนตามบทนี้ได้ทั้งหมด เพียงแต่ส่วน "รัน
.tsตรง ๆ" ใน Part 2.4 จะข้ามไป (ใช้วิธี tsx หรือ compile ก่อนแทนได้)ตัว Node.js จะติดตั้งคำสั่ง
nodeและnpm(Node Package Manager — เครื่องมือจัดการ library) มาให้พร้อมกัน ถ้ายังไม่มี Node เลย ให้ไปดาวน์โหลดจาก nodejs.org ก่อน (เลือกปุ่มที่เขียนว่า LTS) เสร็จแล้วเปิด terminal พิมพ์node --versionเพื่อตรวจสอบว่าติดตั้งสำเร็จ
💡 วิธีเปิด terminal
- Windows: กด
Win + R→ พิมพ์cmd→ Enter (หรือค้นหา "PowerShell" ใน Start menu)- macOS: กด
Cmd + Space→ พิมพ์Terminal→ Enter- Linux: ขึ้นกับ distro — โดยทั่วไปหาใน Application menu หรือกด
Ctrl + Alt + T
💡 npm คืออะไร? npm (Node Package Manager) คือเครื่องมือสำหรับ "ดาวน์โหลดและจัดการ library" ที่มาพร้อม Node.js — เราจะใช้มันลง TypeScript และเครื่องมืออื่น ๆ
Part 1: ติดตั้ง TypeScript
TypeScript ติดตั้งได้ 2 แบบ ต้องเข้าใจความต่างก่อนเลือก
1.1 ติดตั้งแบบ "ผูกกับโปรเจกต์" (แนะนำ)
วิธีนี้คือติดตั้ง TypeScript ไว้ "ในโฟลเดอร์โปรเจกต์" โดยเฉพาะ ก่อนอื่นสร้างโฟลเดอร์โปรเจกต์ใหม่:
bash
mkdir my-ts-project # สร้างโฟลเดอร์
cd my-ts-project # เข้าไปในโฟลเดอร์
npm init -y # สร้างไฟล์ package.json (แฟ้มข้อมูลโปรเจกต์)
npm install -D typescript # ติดตั้ง TypeScriptอธิบายคำสั่งสุดท้าย:
npm install— สั่งดาวน์โหลด library-D— ย่อมาจาก--save-devหมายถึง "library นี้ใช้แค่ตอนพัฒนา ไม่ต้องเอาไปรันจริง" (เพราะ TypeScript compile เสร็จแล้วเหลือแต่ JavaScript)typescript— ชื่อ library
หลังติดตั้ง TypeScript จะอยู่ในโฟลเดอร์ node_modules ของโปรเจกต์นี้
ทำไมแนะนำวิธีนี้? เพราะแต่ละโปรเจกต์อาจต้องการ TypeScript คนละเวอร์ชัน การผูกเวอร์ชันไว้กับโปรเจกต์ทำให้ทุกคนในทีม (และเซิร์ฟเวอร์ build) ใช้เวอร์ชันเดียวกันเป๊ะ ไม่มีปัญหา "เครื่องฉันรันได้ เครื่องเธอรันไม่ได้"
1.2 ติดตั้งแบบ "ทั้งเครื่อง" (global)
bash
npm install -g typescript # -g = globalวิธีนี้ติดตั้ง TypeScript ไว้ใช้ได้ทุกที่ในเครื่อง สะดวกสำหรับทดลองเล่น แต่ ไม่แนะนำสำหรับโปรเจกต์จริง เพราะปัญหาเรื่องเวอร์ชันที่กล่าวไปข้างบน
1.3 ตรวจสอบว่าติดตั้งสำเร็จ
bash
npx tsc --version
# ควรขึ้นประมาณ: Version 5.x.x💡
npxคืออะไร?npxคือคำสั่งที่ช่วย "รันเครื่องมือที่ติดตั้งไว้ในโปรเจกต์" ในที่นี้npx tscหมายถึง "รัน tsc ตัวที่อยู่ใน node_modules ของโปรเจกต์นี้" — ถ้าเราพิมพ์tscเฉย ๆ เครื่องอาจหาไม่เจอ (เพราะมันไม่ได้ติดตั้งแบบ global)
tsc ย่อมาจาก TypeScript Compiler — คือตัวแปลงและตรวจสอบโค้ดที่เราพูดถึงในบทที่แล้ว
Part 2: เขียนและรันไฟล์ TypeScript ไฟล์แรก
2.1 สร้างไฟล์
สร้างไฟล์ชื่อ hello.ts (สังเกตนามสกุล .ts — นี่คือไฟล์ TypeScript):
typescript
const greet = (name: string): string => `Hello, ${name}!`;
console.log(greet("Alice"));อธิบายโค้ด:
name: string— พารามิเตอร์nameต้องเป็นข้อความ: stringหลังวงเล็บ — ฟังก์ชันนี้คืนค่าเป็นข้อความ- ส่วนที่เหลือคือ JavaScript ปกติ
2.2 วิธีที่ 1 — compile แล้วรัน (เข้าใจกลไกพื้นฐาน)
bash
npx tsc hello.ts # แปลง hello.ts → ได้ไฟล์ hello.js ออกมา
node hello.js # รันไฟล์ JavaScript ที่ได้ลองเปิดดูไฟล์ hello.js ที่เกิดขึ้น คุณจะเห็นว่า : string หายไปหมด — นี่คือ type erasure ที่เราเรียนในบทที่แล้วของจริง
ขั้นตอนนี้แสดงให้เห็นกลไกพื้นฐานชัดเจน แต่ในการทำงานจริงเราไม่อยากพิมพ์ 2 คำสั่งทุกครั้ง มีวิธีที่สะดวกกว่า
2.3 วิธีที่ 2 — รันไฟล์ .ts ตรง ๆ ด้วย tsx (แนะนำตอน develop)
tsx เป็นเครื่องมือที่ให้เรา "รันไฟล์ .ts ได้เลย" โดยมันแอบ compile ให้เบื้องหลังแบบเร็วมาก
bash
npm install -D tsx # ติดตั้ง
npx tsx hello.ts # รันได้เลย ไม่ต้องสร้าง .js ก่อนสะดวกกว่ามาก เหมาะกับตอนพัฒนา/ทดลอง
2.4 วิธีที่ 3 — runtime สมัยใหม่ที่รัน TypeScript ได้เอง
bash
# Node.js 22 (LTS) — ต้องเติม flag --experimental-strip-types เอง
node --experimental-strip-types hello.ts
# Node.js เวอร์ชันใหม่กว่า (ตั้งแต่ 23.6 เป็นต้นมา) — บางรุ่นเปิดให้รัน .ts ได้เลย ไม่ต้องเติม flag
node hello.ts
# Bun — runtime ทางเลือกแทน Node.js รองรับ TypeScript เต็มตัวมาแต่เกิด
bun run hello.ts
# Deno — runtime ทางเลือกอีกตัว รองรับ TypeScript เต็มตัวเช่นกัน
deno run hello.ts📌 หมายเหตุเรื่องเวอร์ชัน Node: ฟีเจอร์ "strip types" (ลบ type ทิ้งแล้วรัน) เพิ่มเข้ามาครั้งแรกใน Node 22 แบบ "ต้องเปิดด้วย flag
--experimental-strip-types" จากนั้นตั้งแต่ Node 23.6 เป็นต้นมาบางรุ่นก็เปิดให้เป็นค่าเริ่มต้น (ไม่ต้องเติม flag) แต่ให้ระวังว่า Node เลขคี่ (เช่น 23) เป็นรุ่น Current ไม่ใช่ LTS — หมดอายุการดูแลเร็ว จึงแนะนำให้ยึด รุ่น LTS (เช่น Node 22 LTS หรือรุ่น LTS ที่ใหม่กว่า) เป็นหลัก และเนื่องจากฟีเจอร์นี้ยังใหม่/อยู่ในช่วงทดลอง ควรตรวจสอบสถานะล่าสุดจากเอกสาร Node.js ว่าเวอร์ชันไหนเปิด default ให้แล้วบ้าง ส่วน Node เวอร์ชันต่ำกว่า 22 จะรัน.tsตรง ๆ ไม่ได้ ต้องใช้ tsx หรือ compile ก่อน
📌 สรุปควรใช้อันไหน? ตอนเรียนและพัฒนา ใช้ tsx (หรือ Bun/Node ถ้ามี) เพราะเร็วและสะดวก ส่วน
tscเราจะใช้เป็นหลักเพื่อ "ตรวจ type" มากกว่าเพื่อ compile — เดี๋ยวจะอธิบายใน Part 7
Part 3: tsconfig.json — ไฟล์ตั้งค่าหัวใจของโปรเจกต์
นี่คือส่วนที่สำคัญที่สุดของบท tsconfig.json คือไฟล์ที่บอก TypeScript ว่า "จะให้เข้มงวดแค่ไหน, จะ compile ออกมาเป็นแบบไหน, จะตรวจไฟล์ไหนบ้าง" ถ้าตั้งค่าผิด ประสบการณ์การใช้ TypeScript จะแย่ไปเลย
3.1 สร้างไฟล์
bash
npx tsc --initคำสั่งนี้สร้างไฟล์ tsconfig.json พร้อมตัวเลือกมากมาย (และ comment อธิบาย) แต่ค่าตั้งต้นนั้นไม่เหมาะกับงานปี 2026 เราจะเขียนใหม่ให้ดีกว่า
3.2 tsconfig.json ที่แนะนำ (อธิบายทีละบรรทัด)
เพื่อไม่ให้ท่วมตัวเลือกในรอบเดียว เราจะแบ่งเป็น 2 ระดับ:
- ระดับ "พื้นฐาน" — เปิดไว้ในทุกโปรเจกต์ใหม่ ตัวเลือกที่ตรงตัวเข้าใจง่าย
- ระดับ "แนะนำเพิ่ม" — ตัวเลือกขั้นสูงที่ดีต่อ "งานจริง" แต่อาจดูแปลกสำหรับมือใหม่ — จะเพิ่มทีหลังเมื่อเริ่มชินแล้ว
ระดับพื้นฐาน — เริ่มจากชุดนี้ก่อน
ลบเนื้อหาในไฟล์ แล้วใส่แบบนี้:
json
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"lib": ["ES2022", "DOM"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}ทีนี้มาทำความเข้าใจทุกตัว — อย่าข้าม เพราะตัวเลือกพวกนี้คุณจะเจอตลอดชีวิตการเขียน TypeScript
target — "จะ compile ออกมาเป็น JavaScript เวอร์ชันไหน" JavaScript มีหลายเวอร์ชันตามปี (ES2015, ES2020, ES2022, ...) เวอร์ชันใหม่มีไวยากรณ์สะดวกกว่า แต่เครื่องเก่าอาจรันไม่ได้ ตั้ง ES2022 กำลังดีสำหรับปี 2026 — เบราว์เซอร์และ Node.js สมัยใหม่รองรับหมด
target กับ ESNext — คำว่า ESNext ย่อมาจาก "ECMAScript รุ่น Next (ถัดไป)" หมายถึงรุ่นใหม่ล่าสุดที่ TypeScript เวอร์ชันนั้นรู้จัก ในปี 2026 คือประมาณ ES2025 ขึ้นอยู่กับเวอร์ชัน TypeScript ที่ใช้ — สำหรับมือใหม่ใช้ ES2022 ที่ระบุชัดเจนจะปลอดภัยกว่า เพราะพฤติกรรมไม่เปลี่ยนตาม TypeScript version
module — "จะใช้ระบบ import/export แบบไหนในไฟล์ผลลัพธ์" JavaScript มี 2 ระบบหลักในการแยกไฟล์: แบบเก่า (CommonJS — ใช้ require) และแบบใหม่ (ES Modules ย่อ ESM — มาตรฐานใหม่ที่ใช้ import/export) ตั้ง ESNext หมายถึงใช้ระบบใหม่ล่าสุด
moduleResolution — "จะค้นหาไฟล์ที่ import ยังไง" ตอนเราเขียน import x from "./foo" TypeScript ต้องรู้ว่าจะไปหาไฟล์ยังไง ตั้ง Bundler เหมาะกับโปรเจกต์ที่ใช้ bundler สมัยใหม่ (Vite, esbuild) — ข้อดีคือไม่ต้องเติม .js ต่อท้ายตอน import
⚠️ ข้อควรระวัง: ค่า
Bundlerเหมาะเมื่อโปรเจกต์ มี bundler (Vite/esbuild/webpack) อยู่ในสายงานจริง ถ้าคุณตั้งใจจะรันด้วย Node ล้วน ๆ หรือ compile ด้วยtscอย่างเดียว (แบบตัวอย่างnpx tsc hello.tsก่อนหน้านี้) ควรใช้"moduleResolution": "NodeNext"คู่กับ"module": "NodeNext"แทน มิฉะนั้นเส้นทาง import ที่tscยอมให้ผ่านอาจกลับพังตอนรันจริงบน Node — สำหรับบทเรียนนี้ที่เราใช้ tsx เป็นหลักBundlerยังใช้ได้สบาย
lib — "มี API อะไรให้ใช้ได้บ้าง" บอก TypeScript ว่าโค้ดเราจะรันในสภาพแวดล้อมที่มีอะไร ["ES2022", "DOM"] หมายถึง "มีฟีเจอร์ JavaScript ES2022 และมี API ของเบราว์เซอร์ (DOM — เช่น document, window)" ถ้าเขียนโปรแกรมฝั่ง Node.js ล้วน ๆ อาจตัด "DOM" ออก
outDir — "compile แล้วเอาไฟล์ .js ไปไว้โฟลเดอร์ไหน" — ที่นี่คือ ./dist
rootDir — "ไฟล์โค้ดต้นฉบับอยู่โฟลเดอร์ไหน" — ที่นี่คือ ./src
strict — ตัวสำคัญที่สุด — เปิดโหมดเข้มงวดทั้งหมด (อธิบายละเอียดใน 3.3)
esModuleInterop — ช่วยให้ import library แบบเก่า (CommonJS) กับแบบใหม่ (ESM) เข้ากันได้ราบรื่น — เปิดไว้เถอะ
skipLibCheck — "ไม่ต้องตรวจ type ในไฟล์ของ library คนอื่น" — เปิดไว้เพื่อให้ compile เร็วขึ้น (เราเชื่อว่า library เขาตรวจมาแล้ว)
forceConsistentCasingInFileNames — บังคับให้การใช้ตัวพิมพ์เล็ก/ใหญ่ในชื่อไฟล์ตรงกันเป๊ะ ป้องกันบั๊กที่เกิดเฉพาะบนบางระบบปฏิบัติการ (Windows ไม่สนตัวพิมพ์ แต่ Linux สน)
resolveJsonModule — อนุญาตให้ import ไฟล์ .json ได้
ส่วน 2 บรรทัดล่าง:
include— "ตรวจไฟล์ในโฟลเดอร์ไหน" — ที่นี่คือทุกไฟล์ในsrcexclude— "ไม่ต้องตรวจโฟลเดอร์ไหน"
ระดับแนะนำเพิ่ม — เปิดเมื่อเริ่มชินแล้ว
ตัวเลือกกลุ่มนี้เป็นของ "งานจริงในปี 2026 มักเปิด" แต่ผลของมันเข้าใจยากกว่าตัวข้างบน ถ้าเพิ่งเริ่ม ข้ามกล่องนี้ไปก่อนได้เลย แล้วค่อยกลับมาเปิดอ่านตอนที่เริ่มชินกับ tsconfig แล้ว
กดเพื่อดู "ตัวเลือกแนะนำเพิ่ม" (ขั้นสูง — ข้ามได้ถ้าเพิ่งเริ่ม)
json
{
"compilerOptions": {
"isolatedModules": true,
"verbatimModuleSyntax": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
"isolatedDeclarations": true
}
}💡 สองตัวเลือกแรก (
isolatedModulesและverbatimModuleSyntax) ควบคุมวิธีที่ TypeScript จัดการimportที่เป็น type เพื่อให้ build tool ทำงานได้ถูกต้อง — อ่านคำอธิบายแล้วค่อยเปิดใช้ทีหลังได้
isolatedModules — บังคับให้แต่ละไฟล์ compile แยกกันได้แบบไม่ต้องรู้ไฟล์อื่น
ทำไมต้องบังคับแบบนี้? เพราะ bundler สมัยใหม่ (เช่น Vite, esbuild, swc — เครื่องมือที่รวมและแปลงไฟล์ให้เร็ว) จะทำงานแบบ "ทีละไฟล์" เพื่อความเร็ว มันไม่ได้มองทั้งโปรเจกต์พร้อมกัน แต่ฟีเจอร์บางตัวของ TypeScript ต้องดูทั้งโปรเจกต์ถึงจะ compile ถูก ตัวเลือกนี้จึงคอย "ห้าม" ไม่ให้เราใช้ฟีเจอร์เหล่านั้น เพื่อให้ bundler ทำงานได้ไม่มีปัญหา
ตัวอย่างฟีเจอร์ที่โดนห้าม: const enum (จะเรียนในบทที่ 02) จะใช้ไม่ได้เมื่อเปิด option นี้
verbatimModuleSyntax — ("verbatim" แปลว่า "ตามตัวอักษร") บังคับให้เขียน import type กำกับเมื่อ import เฉพาะ type — ทำให้ TypeScript ลบ import ที่เป็น type ทิ้งตอน compile ได้ถูกต้องเสมอ ตัวอย่าง:
typescript
// ❌ ไม่ผ่าน (เมื่อ verbatimModuleSyntax: true)
// User เป็น type อย่างเดียว แต่เขียน import แบบ value
import { User } from "./types"; // ไฟล์ ./types.ts คือตัวอย่างสมมติ — ยังไม่ต้องสร้างตอนนี้
function greet(user: User) {
return `Hello ${user.name}`;
}
// ✅ ผ่าน — เติม "type" กำกับให้ชัด
import type { User } from "./types"; // ไฟล์ ./types.ts คือตัวอย่างสมมติ — ยังไม่ต้องสร้างตอนนี้
function greet(user: User) {
return `Hello ${user.name}`;
}
// ✅ ผ่าน — กรณีไฟล์เดียวกัน export ทั้ง value และ type
import { calculate, type CalcOptions } from "./calc";
// ^^^^^^^^^ value ^^^^^^^^^^^^^^^^^ type (ต้องเติม type ข้างหน้า)ทำไมถึงสำคัญ — เพราะถ้าไม่เติม type เครื่องมือ build บางตัวจะคิดว่า User เป็น value แล้วพยายาม require ไฟล์ที่อาจ export type เปล่า ๆ (ไม่มี value จริง) — เกิด error ตอน build
noUncheckedIndexedAccess — ตัวนี้สำคัญและคนมักไม่เปิด (อธิบายใน 3.4)
noImplicitOverride — เมื่อ override method ของ class แม่ ต้องเขียนคำว่า override กำกับให้ชัด (เรื่อง class จะลงลึกในบทที่ 08 — ตอนนี้แค่รู้ว่ามีตัวเลือกนี้)
isolatedDeclarations — (TS 5.5+) บังคับให้ทุก exported declaration มี type annotation ชัดเจนพอที่จะสร้างไฟล์ .d.ts ได้โดยไม่ต้องรัน type inference — ทำให้ build .d.ts แบบ parallel ได้ เหมาะกับ monorepo ขนาดใหญ่
⚠️ ตัวนี้เข้มมาก อย่าเพิ่งเปิด — มันบังคับให้ทุกอย่างที่
export(ทั้งฟังก์ชันและตัวแปร) ต้องเขียน type ของค่าที่คืน/ชนิดกำกับเองให้ครบ ห้ามพึ่ง "การเดา type อัตโนมัติ" ของ TypeScript เลย โค้ดมือใหม่ทั่วไปที่ปล่อยให้ TS เดา type ให้ พอเปิดตัวนี้จะขึ้น error เต็มไปหมดจนงง แนะนำให้ ปิดไว้ก่อน แล้วค่อยเปิดเฉพาะตอนที่คุณต้องการสร้างไฟล์.d.tsแบบขนานในโปรเจกต์ใหญ่จริง ๆ
3.3 strict: true — เปิดไว้เสมอ และนี่คือเหตุผล
"strict": true เป็นการเปิดสวิตช์ตรวจสอบที่เข้มงวดหลายตัวพร้อมกัน ตัวที่สำคัญที่สุด 2 ตัวคือ:
1. noImplicitAny — ห้าม TypeScript "เดา type เป็น any เงียบ ๆ"
typescript
// ถ้าไม่เปิด strict — TypeScript ปล่อยผ่าน (param เป็น any โดยปริยาย)
function double(x) { return x * 2; }
// ถ้าเปิด strict — ฟ้องทันที!
// ❌ Error: Parameter 'x' implicitly has an 'any' type.
// (implicitly = โดยปริยาย/ไม่ได้บอกชัด — TypeScript เดาให้เป็น any แบบเงียบ ๆ)
// → บังคับให้เราใส่ type: function double(x: number)จำได้ไหมว่า any คือ "การปิดการตรวจสอบ" — ถ้าปล่อยให้ TypeScript เดาเป็น any เงียบ ๆ คุณจะเสียประโยชน์ของ TypeScript ไปฟรี ๆ
2. strictNullChecks — บังคับให้จัดการกับค่า null / undefined อย่างชัดเจน
typescript
// ถ้าไม่เปิด — โค้ดนี้ผ่าน แต่จะพังตอน runtime ถ้า find ไม่เจอ
const user = users.find(u => u.id === 1);
console.log(user.name); // 💥 อาจ "Cannot read property 'name' of undefined"
// ถ้าเปิด — TypeScript ฟ้องว่า user อาจเป็น undefined
// ❌ Error: 'user' is possibly 'undefined'.
// → บังคับให้เราเช็กก่อน: if (user) { console.log(user.name); }นี่คือฟีเจอร์ที่ป้องกันบั๊กที่พบบ่อยที่สุดในโลก JavaScript — "อ่าน property ของ null/undefined"
🎯 กฎเหล็ก: โปรเจกต์ใหม่ทุกโปรเจกต์ เปิด
strict: trueเสมอ การปิดมันเพื่อ "ความง่าย" คือการโกหกตัวเอง — บั๊กไม่ได้หายไป มันแค่ถูกเลื่อนไปเจอตอน runtime
3.4 noUncheckedIndexedAccess — ตัวที่ควรเปิดเพิ่ม
ตัวนี้ไม่ได้รวมอยู่ใน strict แต่ควรเปิด มันแก้ปัญหานี้:
typescript
const fruits = ["apple", "banana"];
const third = fruits[2]; // ตำแหน่ง 2 ไม่มีของจริง!โดยปกติ TypeScript จะบอกว่า third เป็น string — แต่ความจริงตอน runtime มันคือ undefined (เพราะ array มีแค่ 2 ตัว)
เมื่อเปิด noUncheckedIndexedAccess TypeScript จะซื่อสัตย์ขึ้น — มันจะบอกว่า third เป็น string | undefined บังคับให้เราเช็กก่อนใช้ ป้องกันบั๊กได้อีกชั้น
3.5 ตัวเลือกเข้มงวดเพิ่มเติม (สำหรับโปรเจกต์ที่อยากแน่นจริง)
เมื่อคุณเก่งขึ้น พิจารณาเปิดพวกนี้เพิ่ม:
json
{
"noFallthroughCasesInSwitch": true, // กัน switch-case ที่ลืมใส่ break
"noPropertyAccessFromIndexSignature": true, // บังคับใช้ obj["key"] กับ index signature
"exactOptionalPropertyTypes": true // แยกแยะ "ไม่มี field" กับ "field เป็น undefined"
}ตอนนี้ยังไม่ต้องเปิดก็ได้ ค่อยกลับมาเมื่อเข้าใจมากขึ้น
Part 4: วิธีรันโค้ด TypeScript — เลือกใช้ให้ถูกงาน
เราเห็นวิธีรันไปแล้วใน Part 2 มาสรุปให้ชัดว่าแต่ละแบบเหมาะกับอะไร
4.1 ตอนพัฒนา (development) — ใช้ tsx watch (โหมดเฝ้าดูไฟล์)
bash
npx tsx watch src/main.tswatch (เฝ้าดู) หมายถึง "เฝ้าดูไฟล์" — ทุกครั้งที่คุณแก้โค้ดแล้วเซฟ มันจะรันใหม่ให้อัตโนมัติ ทำให้พัฒนาเร็ว
4.2 ตรวจชนิด (type-check) อย่างเดียว — ใช้ tsc --noEmit
bash
npx tsc --noEmit--noEmit แปลว่า "ตรวจ type อย่างเดียว ไม่ต้องสร้างไฟล์ .js" คำสั่งนี้สำคัญมาก เพราะ tsx/Bun ตอนรันโค้ด จะข้ามการตรวจ type เพื่อความเร็ว — มันแค่ลบ type ทิ้งแล้วรัน ดังนั้นเราต้องมีคำสั่งแยกไว้ "ตรวจ type จริงจัง" และนั่นคือ tsc --noEmit
4.3 โหมด watch สำหรับตรวจ type
bash
npx tsc --noEmit --watchเปิดทิ้งไว้ใน terminal อีกหน้าต่าง มันจะตรวจ type ให้ตลอดเวลาที่คุณเขียนโค้ด
Part 5: ตั้งค่า script ใน package.json
แทนที่จะจำคำสั่งยาว ๆ เราตั้ง "ทางลัด" ไว้ในไฟล์ package.json ได้ เปิดไฟล์นั้น แล้วเพิ่มส่วน scripts:
json
{
"scripts": {
"dev": "tsx watch src/main.ts",
"typecheck": "tsc --noEmit",
"build": "tsc"
}
}จากนั้นเรียกใช้ด้วย npm run:
bash
npm run dev # รันโหมดพัฒนา
npm run typecheck # ตรวจ type
npm run build # compile เป็น .jsข้อดีคือทุกคนในทีมใช้คำสั่งชุดเดียวกัน ไม่ต้องจำรายละเอียด
Part 6: ตั้งค่า Editor (VS Code)
VS Code คือ editor ที่เข้ากับ TypeScript ดีที่สุด — มันมี "TypeScript Language Server" ติดตั้งมาในตัว ทำให้เห็น error และ autocomplete แบบเรียลไทม์
6.1 คีย์ลัดที่ต้องรู้
| คีย์ลัด | ทำอะไร |
|---|---|
| เอาเมาส์ชี้ (hover) | แสดง type ของสิ่งนั้น |
F12 | กระโดดไปยังจุดที่นิยาม (definition) ของสิ่งนั้น |
Shift+F12 | หาทุกที่ที่ใช้สิ่งนี้ (find references) |
F2 | เปลี่ยนชื่อ (rename) ทุกที่ในโปรเจกต์พร้อมกัน |
Ctrl+. | แสดง "quick fix" — TypeScript เสนอวิธีแก้ error ให้ |
ลองใช้ F2 rename ดูสักครั้ง คุณจะเห็นพลังของ TypeScript ทันที
6.2 ตั้งค่า VS Code ที่แนะนำ
สร้างไฟล์ .vscode/settings.json ในโปรเจกต์:
json
{
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.updateImportsOnFileMove.enabled": "always",
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit"
}
}typescript.tsdk— บอก VS Code ให้ใช้ TypeScript เวอร์ชันของโปรเจกต์ (ไม่ใช่เวอร์ชันที่ติดมากับ VS Code) — สำคัญเพื่อให้ตรงกับตอน buildupdateImportsOnFileMove— เวลาย้ายไฟล์ ให้แก้ path ใน import ให้อัตโนมัติorganizeImports— เวลาเซฟ ให้จัดเรียง import ให้เป็นระเบียบ
Part 7: Path Alias — เลิกเขียน import แบบ ../../..
เมื่อโปรเจกต์ใหญ่ขึ้น คุณจะเจอ import น่าเกลียดแบบนี้:
typescript
import { Button } from "../../../components/Button";แก้ได้ด้วย "path alias" — ตั้งชื่อย่อแทน path เพิ่มใน tsconfig.json:
json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
}
}แล้วเขียน import ได้สวยขึ้น:
typescript
import { Button } from "@/components/Button";⚠️ ข้อควรระวัง: path alias ต้องตั้งให้ "เครื่องมือ build รู้จักด้วย" ไม่ใช่แค่ TypeScript ถ้าใช้ Vite ต้องไปตั้งในไฟล์
vite.config.tsด้วย:typescriptresolve: { alias: { "@": "/src" } }ไม่งั้น TypeScript จะไม่ฟ้อง error แต่โปรแกรมจะพังตอน build เพราะ bundler หาไฟล์ไม่เจอ
ถ้ายังไม่ได้ใช้ Vite (หรือ bundler ใด ๆ) — ข้ามส่วนนี้ไปก่อนได้ ตอนนี้แค่รู้จักหน้าตา path alias พอ เดี๋ยวเจอตอนใช้ Vite จริงค่อยกลับมาตั้ง
Part 8: โครงสร้างโปรเจกต์ที่แนะนำ
text
my-ts-project/
├── src/ ← โค้ดต้นฉบับทั้งหมดอยู่ที่นี่
│ ├── main.ts ← ไฟล์เริ่มต้น
│ ├── lib/ ← ฟังก์ชันช่วยเหลือต่าง ๆ
│ └── types/ ← ไฟล์ type ที่เราเขียนเอง (.d.ts)
├── dist/ ← ผลลัพธ์หลัง build (ควรใส่ใน .gitignore)
├── tsconfig.json ← ไฟล์ตั้งค่า TypeScript
├── package.json ← ไฟล์ข้อมูลโปรเจกต์
└── node_modules/ ← library ที่ติดตั้ง (ควรใส่ใน .gitignore)หลักการง่าย ๆ: โค้ดที่เราเขียนอยู่ใน src/ ผลลัพธ์ที่ build ออกมาอยู่ใน dist/
Part 9: แก้ Error ที่เจอบ่อยตอนเริ่มต้น
มือใหม่มักเจอ error ไม่กี่แบบซ้ำ ๆ มารู้จักไว้ล่วงหน้า
9.1 "Cannot find name 'X'" / "Cannot find module"
แปลว่า TypeScript ไม่รู้จักสิ่งนั้น สาเหตุที่พบบ่อย:
- ลืม
import— เพิ่ม import ให้ถูก - library ไม่มี type — ติดตั้งไฟล์ type เพิ่ม:
npm install -D @types/ชื่อlibrary
9.2 "Type 'X' is not assignable to type 'Y'"
แปลว่า "คุณเอา type X ไปใส่ที่ที่ต้องการ type Y" — type ไม่ตรงกัน ให้ดูว่าค่าที่ส่งผิดประเภทตรงไหน นี่คือ error ที่ดี — TypeScript กำลังจับบั๊กให้คุณ
9.3 "Object is possibly 'null'" หรือ "possibly 'undefined'"
แปลว่าค่านั้นอาจเป็น null/undefined (มาจาก strictNullChecks) วิธีแก้คือเช็กก่อนใช้:
typescript
// วิธีที่ 1 — เช็กด้วย if
if (user) {
console.log(user.name);
}
// วิธีที่ 2 — optional chaining (ถ้า user เป็น null/undefined ทั้งนิพจน์จะได้ undefined)
console.log(user?.name);9.4 "Parameter 'x' implicitly has an 'any' type"
แปลว่าคุณลืมใส่ type ให้พารามิเตอร์ (มาจาก noImplicitAny) — แค่ใส่ type ให้มัน
Part 10: Lab — สร้างโปรเจกต์จริง
ทำตามขั้นตอนนี้ให้ครบ จะได้โปรเจกต์ TypeScript ที่พร้อมใช้งาน
ขั้นที่ 1 — สร้างโปรเจกต์
bash
mkdir ts-playground && cd ts-playground
npm init -y
npm install -D typescript tsxขั้นที่ 2 — สร้าง tsconfig.json ตามแบบใน Part 3.2 (ก๊อปได้เลย)
ขั้นที่ 3 — สร้างไฟล์ src/main.ts
typescript
interface Task {
id: number;
title: string;
done: boolean;
}
function summarize(tasks: Task[]): string {
const doneCount = tasks.filter(t => t.done).length;
return `เสร็จแล้ว ${doneCount} จาก ${tasks.length} งาน`;
}
const myTasks: Task[] = [
{ id: 1, title: "เรียน TypeScript", done: true },
{ id: 2, title: "ทำ Lab", done: false },
];
console.log(summarize(myTasks));ขั้นที่ 4 — เพิ่ม script ใน package.json (ตาม Part 5)
ขั้นที่ 5 — รัน
bash
npm run dev # ควรเห็น: เสร็จแล้ว 1 จาก 2 งาน
npm run typecheck # ควรไม่มี errorขั้นที่ 6 (ทดลองให้เกิด error) — ลองแก้ done: false เป็น done: "no" แล้วรัน npm run typecheck อีกครั้ง คุณควรเห็น TypeScript ฟ้อง error — นี่คือ TypeScript กำลังทำงานปกป้องคุณ จากนั้นแก้กลับ
Part 11: Checkpoint
1. ทำไมแนะนำติดตั้ง TypeScript แบบผูกกับโปรเจกต์ มากกว่าแบบ global?
เพื่อให้ทุกคนในทีมและเซิร์ฟเวอร์ build ใช้ TypeScript เวอร์ชันเดียวกันเป๊ะ ป้องกันปัญหา "เครื่องฉันรันได้ เครื่องเธอรันไม่ได้"
2. tsc กับ tsx ต่างกันยังไง?
tsc = TypeScript Compiler — ตรวจ type และ/หรือ compile เป็นไฟล์ .js tsx = เครื่องมือที่ให้ "รันไฟล์ .ts ได้เลย" ตอนพัฒนา (เร็ว แต่ข้ามการตรวจ type)
3. strict: true เปิดอะไรที่สำคัญบ้าง?
ตัวสำคัญคือ noImplicitAny (ห้ามเดา type เป็น any เงียบ ๆ) และ strictNullChecks (บังคับจัดการ null/undefined ให้ชัดเจน)
4. noUncheckedIndexedAccess ทำอะไร?
ทำให้การเข้าถึง array/object ด้วย index ให้ type เป็น T | undefined แทน T — สะท้อนความจริงว่าตำแหน่งนั้นอาจไม่มีของ บังคับให้เราเช็กก่อนใช้
5. target กับ module ใน tsconfig ต่างกันยังไง?
target = "compile ออกมาเป็น JavaScript เวอร์ชันไหน" (เกี่ยวกับไวยากรณ์) module = "ใช้ระบบ import/export แบบไหนในไฟล์ผลลัพธ์" (CommonJS หรือ ES Modules)
6. ทำไมต้องมีคำสั่ง tsc --noEmit แยกต่างหาก ทั้งที่ใช้ tsx รันได้?
เพราะ tsx/Bun ตอนรัน จะข้ามการตรวจ type เพื่อความเร็ว — มันแค่ลบ type ทิ้งแล้วรัน เราจึงต้องมี tsc --noEmit ไว้ "ตรวจ type จริงจัง" แยกต่างหาก
7. ตั้ง path alias แล้วต้องทำอะไรเพิ่ม?
ต้องตั้งให้ "เครื่องมือ build รู้จักด้วย" เช่นใน vite.config.ts ไม่ใช่แค่ใน tsconfig.json ไม่งั้นจะพังตอน build
Part 12: สรุปบทนี้
- ติดตั้ง TypeScript แบบ ผูกกับโปรเจกต์ ด้วย
npm install -D typescript - รันโค้ดตอนพัฒนาด้วย tsx, ตรวจ type ด้วย
tsc --noEmit— เป็นคนละงานกัน tsconfig.jsonคือหัวใจ — เปิดstrict: trueเสมอ และเพิ่มnoUncheckedIndexedAccess- ตั้ง script ใน
package.jsonให้ทีมใช้คำสั่งชุดเดียวกัน - ตั้งค่า VS Code ให้ใช้ TypeScript เวอร์ชันของโปรเจกต์
- โครงสร้าง: โค้ดอยู่
src/, ผลลัพธ์อยู่dist/
บทถัดไป — เริ่มเรียน Basic Types ของจริง