TypeScript กลายเป็นมาตรฐานของ Frontend และ Backend development ไปแล้ว ในปี 2026 ถ้าคุณยังเขียน TypeScript เหมือน JavaScript ที่แค่ใส่ Type ไป คุณยังไม่ได้ใช้ TypeScript เต็มศักยภาพ บทความนี้รวม 20 เทคนิคจากมืออาชีพที่ใช้จริงใน Production
1. เปิด Strict Mode เสมอ
// tsconfig.json
{
"compilerOptions": {
"strict": true,
// strict = เปิดทั้งหมดเหล่านี้:
// "noImplicitAny": true,
// "strictNullChecks": true,
// "strictFunctionTypes": true,
// "strictBindCallApply": true,
// "strictPropertyInitialization": true,
// "noImplicitThis": true,
// "alwaysStrict": true,
// "useUnknownInCatchVariables": true
}
}
2. อย่าใช้ any — ใช้ unknown แทน
// BAD — any ปิด Type checking ทั้งหมด
function parse(data: any) {
return data.name.toUpperCase(); // ไม่ Error แต่ Runtime crash ได้
}
// GOOD — unknown บังคับให้ตรวจสอบก่อนใช้
function parse(data: unknown) {
if (typeof data === 'object' && data !== null && 'name' in data) {
return (data as { name: string }).name.toUpperCase();
}
throw new Error('Invalid data');
}
3. ใช้ Discriminated Unions
// Discriminated Union — ทำให้ Type narrowing อัตโนมัติ
type Result<T> =
| { status: 'success'; data: T }
| { status: 'error'; error: string }
| { status: 'loading' };
function handleResult(result: Result<User>) {
switch (result.status) {
case 'success':
console.log(result.data.name); // TypeScript รู้ว่ามี data
break;
case 'error':
console.log(result.error); // TypeScript รู้ว่ามี error
break;
case 'loading':
console.log('Loading...');
break;
}
}
4. const assertion
// without as const — Type กว้างเกินไป
const colors = ['red', 'green', 'blue']; // string[]
const config = { api: '/api', timeout: 5000 }; // { api: string; timeout: number }
// with as const — Type แคบลง เป็น Literal types
const colors = ['red', 'green', 'blue'] as const; // readonly ["red", "green", "blue"]
const config = { api: '/api', timeout: 5000 } as const;
// { readonly api: "/api"; readonly timeout: 5000 }
5. satisfies Operator (TypeScript 4.9+)
type Colors = Record<string, [number, number, number]>;
// ปัญหา: ถ้าใช้ type annotation จะ lose literal types
const colors: Colors = {
red: [255, 0, 0],
green: [0, 255, 0],
};
colors.red; // [number, number, number] — ไม่รู้ว่ามี key "red"
// satisfies: ตรวจสอบ Type แต่ไม่ widen
const colors = {
red: [255, 0, 0],
green: [0, 255, 0],
} satisfies Colors;
colors.red; // [number, number, number] — รู้ว่ามี key "red" ✓
colors.blue; // Error! ✓
6. Template Literal Types
type EventName = 'click' | 'focus' | 'blur';
type Handler = `on${Capitalize<EventName>}`;
// "onClick" | "onFocus" | "onBlur"
type Route = `/${string}`;
function navigate(path: Route) { /* ... */ }
navigate('/users'); // OK ✓
navigate('users'); // Error! ✓ ต้องขึ้นต้นด้วย /
7. Branded Types
// ปัญหา: UserId กับ OrderId เป็น string เหมือนกัน สลับกันได้
type UserId = string & { readonly __brand: 'UserId' };
type OrderId = string & { readonly __brand: 'OrderId' };
function createUserId(id: string): UserId { return id as UserId; }
function createOrderId(id: string): OrderId { return id as OrderId; }
function getUser(id: UserId) { /* ... */ }
function getOrder(id: OrderId) { /* ... */ }
const userId = createUserId('u-123');
const orderId = createOrderId('o-456');
getUser(userId); // OK ✓
getUser(orderId); // Error! ✓ ป้องกันสลับ ID
8. Exhaustive Checking
type Shape = 'circle' | 'square' | 'triangle';
function area(shape: Shape): number {
switch (shape) {
case 'circle': return Math.PI * 10 * 10;
case 'square': return 10 * 10;
case 'triangle': return 0.5 * 10 * 10;
default:
// ถ้าเพิ่ม Shape ใหม่แล้วลืม handle → Compile error!
const _exhaustive: never = shape;
throw new Error(`Unhandled shape: ${_exhaustive}`);
}
}
9. Zod Validation
import { z } from 'zod';
// Define schema
const UserSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
age: z.number().int().min(0).max(150),
role: z.enum(['admin', 'user', 'moderator']),
});
// Infer TypeScript type จาก Zod schema
type User = z.infer<typeof UserSchema>;
// { name: string; email: string; age: number; role: "admin" | "user" | "moderator" }
// Validate at runtime
const result = UserSchema.safeParse(unknownData);
if (result.success) {
const user: User = result.data; // Type-safe ✓
} else {
console.error(result.error.issues);
}
10. Utility Types Mastery
interface User {
id: number;
name: string;
email: string;
password: string;
createdAt: Date;
}
// Partial — ทุก Field เป็น Optional
type UpdateUser = Partial<User>;
// Pick — เลือกเฉพาะ Field ที่ต้องการ
type UserProfile = Pick<User, 'id' | 'name' | 'email'>;
// Omit — ตัด Field ที่ไม่ต้องการ
type PublicUser = Omit<User, 'password'>;
// Required — ทุก Field เป็น Required
type CompleteUser = Required<Partial<User>>;
// Record — สร้าง Object type
type UserRoles = Record<string, 'admin' | 'user'>;
// Readonly — ทุก Field เป็น Readonly
type FrozenUser = Readonly<User>;
// ReturnType — ดึง Return type ของ Function
type Result = ReturnType<typeof fetchUser>;
// Parameters — ดึง Parameter types ของ Function
type Params = Parameters<typeof fetchUser>;
11. Generic Constraints
// BAD — ไม่มี Constraint
function getProperty<T>(obj: T, key: string) {
return obj[key]; // Error! T ไม่รู้ว่ามี key
}
// GOOD — ใช้ Constraint
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key]; // OK ✓ Type-safe
}
const user = { name: 'John', age: 30 };
getProperty(user, 'name'); // string ✓
getProperty(user, 'foo'); // Error! ✓
12. Module Augmentation
// เพิ่ม Type ให้ Library ที่มีอยู่
declare module 'express' {
interface Request {
user?: {
id: string;
role: 'admin' | 'user';
};
}
}
// ตอนนี้ req.user มี Type แล้ว
app.get('/profile', (req, res) => {
if (req.user?.role === 'admin') {
// TypeScript รู้ว่า user มี id และ role
}
});
13. Declaration Merging
// Interface สามารถ Merge ได้ (Type ไม่ได้)
interface Config {
database: string;
}
interface Config {
port: number;
}
// Result: Config = { database: string; port: number }
// ใช้สำหรับ extend Library types
14. Path Aliases
// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"],
"@utils/*": ["src/utils/*"],
"@types/*": ["src/types/*"]
}
}
}
// ใช้:
import { Button } from '@components/Button'; // แทน '../../../components/Button'
import { formatDate } from '@utils/date'; // สะอาดกว่ามาก
15. Barrel Exports
// src/components/index.ts (Barrel file)
export { Button } from './Button';
export { Input } from './Input';
export { Modal } from './Modal';
export { Card } from './Card';
// ใช้:
import { Button, Input, Modal, Card } from '@components';
// แทนที่จะ import ทีละไฟล์
// ข้อควรระวัง: Barrel exports อาจทำให้ Bundle size ใหญ่ขึ้น
// ถ้าใช้ Tree-shaking ไม่ได้ → import ตรงจากไฟล์แทน
16. Error Handling Patterns
// Pattern 1: Result Type (ไม่ใช้ throw)
type Result<T, E = Error> =
| { ok: true; value: T }
| { ok: false; error: E };
async function fetchUser(id: string): Promise<Result<User>> {
try {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) return { ok: false, error: new Error(`HTTP ${res.status}`) };
const data = await res.json();
return { ok: true, value: data };
} catch (e) {
return { ok: false, error: e instanceof Error ? e : new Error(String(e)) };
}
}
// ใช้:
const result = await fetchUser('123');
if (result.ok) {
console.log(result.value.name); // Type-safe ✓
} else {
console.error(result.error.message);
}
17. Testing Types
// ใช้ Type assertions ทดสอบว่า Type ถูกต้อง
type Assert<T, Expected> = T extends Expected ? true : false;
type AssertEqual<T, Expected> = [T] extends [Expected]
? [Expected] extends [T] ? true : false
: false;
// ทดสอบ:
type Test1 = AssertEqual<Pick<User, 'name'>, { name: string }>; // true ✓
type Test2 = AssertEqual<ReturnType<typeof add>, number>; // true ✓
18. Performance Tips
// 1. ใช้ interface แทน type สำหรับ Object shapes (Compile เร็วกว่า)
interface User { name: string; } // ดีกว่า type User = { name: string; }
// 2. หลีกเลี่ยง Deep type inference
// BAD: TypeScript ต้อง infer หลายชั้น
const result = arr.map(x => transform(x)).filter(x => validate(x)).reduce(...);
// GOOD: ระบุ Type ช่วย
const result: FinalType = arr.map(...).filter(...).reduce(...);
// 3. ใช้ Project References สำหรับ Monorepo
// tsconfig.json
{
"references": [
{ "path": "./packages/core" },
{ "path": "./packages/ui" }
]
}
19. Migration Strategies (JS → TS)
- เริ่ม allowJs: true: ให้ JS และ TS อยู่ด้วยกันได้
- เปลี่ยนทีละไฟล์: .js → .ts เริ่มจากไฟล์เล็กๆ
- ใช้ @ts-check: เพิ่ม
// @ts-checkที่บรรทัดแรกของ JS file เพื่อให้ TypeScript ตรวจสอบ - เปิด strict ทีละตัว: เริ่มจาก noImplicitAny → strictNullChecks → เปิดทั้งหมด
- อย่า migrate ทุกไฟล์พร้อมกัน: ทำ Sprint ละ 5-10 ไฟล์
20. TypeScript 5+ Features
// TypeScript 5.0: Decorators (Stage 3)
function log(target: any, context: ClassMethodDecoratorContext) {
return function (...args: any[]) {
console.log(`Calling ${String(context.name)}`);
return target.apply(this, args);
};
}
class UserService {
@log
getUser(id: string) { /* ... */ }
}
// TypeScript 5.2: using declarations (Explicit Resource Management)
async function processFile() {
using file = await openFile('data.txt');
// file จะถูก dispose อัตโนมัติเมื่อออกจาก scope
// ไม่ต้อง try-finally-close
}
// TypeScript 5.5: Inferred type predicates
function isString(x: unknown) {
return typeof x === 'string';
// TypeScript 5.5 infer ว่า return type คือ "x is string" อัตโนมัติ
}
สรุป
TypeScript ในปี 2026 มีความสามารถมากกว่าแค่ "JavaScript + Types" มาก เทคนิคทั้ง 20 ข้อนี้จะช่วยให้ Code ของคุณ Type-safe มากขึ้น ตรวจจับ Bug ได้เร็วขึ้น และ Maintain ง่ายขึ้นในระยะยาว เริ่มจาก Strict mode แล้วค่อยๆ เพิ่มเทคนิคอื่นทีละตัว คุณจะรู้สึกว่า TypeScript เป็นเพื่อนที่ช่วยเขียน Code ไม่ใช่อุปสรรค
