TypeScript Template Literal Types: Tricks ít người biết
Khám phá sức mạnh ẩn của template literal types trong TypeScript — từ basic string patterns đến advanced type-level programming.
Template literal types là một trong những features mạnh nhất nhưng ít được khai thác của TypeScript. Nó cho phép bạn tạo string types phức tạp, validate patterns tại compile-time, và viết type-safe APIs mà trước đây không thể.
Basics: String Pattern Types
Template literal types hoạt động giống template strings nhưng ở type level:
// Basic: Combine string literals
type Greeting = `Hello, ${string}`;
const a: Greeting = "Hello, World"; // ✅
const b: Greeting = "Hi, World"; // ❌ Error
// Union expansion
type Color = "red" | "blue" | "green";
type Size = "sm" | "md" | "lg";
type ClassName = `${Color}-${Size}`;
// = "red-sm" | "red-md" | "red-lg" | "blue-sm" | ... (9 combinations)
// CSS unit validation
type CSSLength = `${number}px` | `${number}rem` | `${number}%`;
const width: CSSLength = "16px"; // ✅
const bad: CSSLength = "16apples"; // ❌ Error
Trick 1: Type-safe Event Emitter
type EventMap = {
user: { id: string; name: string };
post: { id: string; title: string };
comment: { id: string; body: string };
};
type EventName = keyof EventMap;
type EventHandler<T extends EventName> = `on${Capitalize<T>}`;
// "onUser" | "onPost" | "onComment"
type EventCallbacks = {
[E in EventName as EventHandler<E>]: (data: EventMap[E]) => void;
};
// Result:
// {
// onUser: (data: { id: string; name: string }) => void;
// onPost: (data: { id: string; title: string }) => void;
// onComment: (data: { id: string; body: string }) => void;
// }
const callbacks: EventCallbacks = {
onUser: (data) => console.log(data.name), // ✅ data is typed!
onPost: (data) => console.log(data.title), // ✅
onComment: (data) => console.log(data.body), // ✅
};
Trick 2: Extract Parts từ String Pattern
// Parse route params
type ExtractParams<T extends string> =
T extends `${string}:${infer Param}/${infer Rest}`
? Param | ExtractParams<Rest>
: T extends `${string}:${infer Param}`
? Param
: never;
type Params = ExtractParams<"/users/:userId/posts/:postId">;
// = "userId" | "postId" ✅
// Type-safe route function
function navigate<T extends string>(
route: T,
params: Record<ExtractParams<T>, string>
) {
// ...
}
navigate("/users/:userId/posts/:postId", {
userId: "123",
postId: "456",
// ❌ Error nếu thiếu param hoặc thêm param thừa
});
Trick 3: Dot Notation Path Types
// Deep object path type
type DotPath<T, Prefix extends string = ""> = T extends object
? {
[K in keyof T & string]: T[K] extends object
? `${Prefix}${K}` | DotPath<T[K], `${Prefix}${K}.`>
: `${Prefix}${K}`;
}[keyof T & string]
: never;
interface Config {
db: {
host: string;
port: number;
credentials: {
user: string;
password: string;
};
};
cache: {
ttl: number;
};
}
type ConfigPath = DotPath<Config>;
// = "db" | "db.host" | "db.port" | "db.credentials"
// | "db.credentials.user" | "db.credentials.password"
// | "cache" | "cache.ttl"
function getConfig(path: ConfigPath): unknown {
// Autocomplete cho tất cả valid paths!
}
Trick 4: Intrinsic String Manipulation
TypeScript built-in 4 utility types cho string manipulation:
type A = Uppercase<"hello">; // "HELLO"
type B = Lowercase<"HELLO">; // "hello"
type C = Capitalize<"hello">; // "Hello"
type D = Uncapitalize<"Hello">; // "hello"
// Real use case: Convert camelCase to kebab-case
type CamelToKebab<S extends string> =
S extends `${infer Head}${infer Tail}`
? Tail extends Uncapitalize<Tail>
? `${Lowercase<Head>}${CamelToKebab<Tail>}`
: `${Lowercase<Head>}-${CamelToKebab<Tail>}`
: S;
type Result = CamelToKebab<"backgroundColor">;
// = "background-color" ✅
Lưu ý về performance
Template literal types mạnh nhưng có thể khiến TypeScript compiler chậm nếu union quá lớn. TypeScript giới hạn template literal union ở 100,000 combinations. Nếu bạn thấy TS bị chậm, kiểm tra xem có tạo ra combinatorial explosion không.
Template literal types biến TypeScript từ "type checker" thành gần như một "type-level programming language". Đừng lạm dụng cho mọi thứ, nhưng khi cần validate string patterns, nó là công cụ không thể thay thế.
Admin
Bình luận (0)
Đăng nhập để bình luận
Chưa có bình luận nào. Hãy là người đầu tiên!