Template literal type tạo ra string literal type bằng đúng cú pháp backtick của template string trong JavaScript. `on${Capitalize<"click" | "focus">}` là kiểu "onClick" | "onFocus", do trình biên dịch tính ra:
Template literal type chỉ tồn tại lúc biên dịch. Chúng kiểm tra chuỗi literal và các giá trị có kiểu trong lúc bạn viết code; chúng không thêm gì vào đầu ra JavaScript. Bảng handler ở trên dùng Record để bắt buộc mỗi tên phải có một hàm.
Cú pháp
Bên trong dấu backtick, bạn viết văn bản literal và các placeholder ${...}. Một placeholder chứa một kiểu, không phải một giá trị: một literal type kiểu string, number, bigint hoặc boolean, một union của chúng, hoặc một trong các kiểu rộng string, number, bigint, boolean, null và undefined.
Một placeholder với kiểu rộng như string hay number tạo ra một mẫu: kiểu vẫn giữ nguyên là `hello ${string}` và mọi chuỗi khớp mẫu đều được chấp nhận. Một placeholder với union hữu hạn, như boolean, được mở rộng thành từng thành viên của nó.
Union nhân lên
Với nhiều union, kết quả là mọi tổ hợp:
Ba kích thước nhân hai tông màu cho ra sáu thành viên. Số lượng tăng rất nhanh: năm placeholder, mỗi cái chứa một union mười chữ cái, sẽ là 100.000 thành viên, và TypeScript từ chối với error TS2590: Expression produces a union type that is too complex to represent. Hãy dùng một placeholder rộng như ${string} khi bạn không cần từng giá trị chính xác.
Uppercase, Lowercase, Capitalize, Uncapitalize
Bốn kiểu có sẵn đổi chữ hoa chữ thường của string literal type. Chúng là kiểu nội tại (intrinsic): được cài đặt bên trong trình biên dịch, không viết bằng TypeScript.
| Kiểu | Đầu vào | Kết quả |
|---|---|---|
Uppercase<S> | "hello world" | "HELLO WORLD" |
Lowercase<S> | "Content-Type" | "content-type" |
Capitalize<S> | "hello world" | "Hello world" |
Uncapitalize<S> | "UserName" | "userName" |
Chúng chỉ thay đổi kiểu. Để tạo chuỗi tương ứng lúc chạy, bạn vẫn gọi toUpperCase() hoặc tự cắt chuỗi rồi viết hoa, và báo cho TypeScript biết kết quả có kiểu chính xác:
Cần dùng as vì toUpperCase() được khai báo là trả về string thông thường. Chữ ký của hàm là thứ nơi gọi nhìn thấy, nên capitalize("report") có literal type "Report".
Mẫu chuỗi: ${number}px và các dạng tương tự
Một kiểu mẫu chấp nhận mọi chuỗi có hình dạng cho trước. Nó tiện cho giá trị CSS, id và khóa có tiền tố biết trước:
${number} chấp nhận mọi chuỗi mà JavaScript đọc được thành số, lỏng hơn bạn tưởng: "-3px", "1e3px" và "0x10px" đều qua được kiểm tra kiểu. Hãy coi các mẫu này là cách chặn lỗi gõ sai trong literal, không phải phép kiểm tra hợp lệ đầy đủ.
Template literal với mapped type
Template literal type hữu ích nhất khi làm mệnh đề as của một mapped type, nơi chúng sinh ra tên thuộc tính từ những tên thuộc tính khác:
string & K chỉ giữ lại các khóa kiểu chuỗi, vì Capitalize không nhận số hay symbol. Mỗi callback nhận kiểu tham số từ chính thuộc tính mà nó theo dõi.
Phân tích chuỗi bằng infer
Trong một conditional type, template literal có thể khớp với một chuỗi và bắt lấy từng phần của nó bằng infer. Ví dụ này lấy ra tên các tham số từ một mẫu route:
Bỏ postId trong lời gọi hàm, trình biên dịch sẽ báo là thiếu.
Biểu thức template bị mở rộng thành string
Một biểu thức template string trong code thông thường có kiểu string, kể cả khi mọi phần của nó đều là literal type. Thêm as const để giữ template literal type:
Không có as const, gán loose cho `log:${Level}` sẽ thất bại, vì string có thể là bất cứ chuỗi nào.
Câu hỏi thường gặp
Template literal type trong TypeScript là gì?
Là string literal type được viết bằng dấu backtick và placeholder ${...}, giống template string của JavaScript nhưng ở cấp kiểu. type Greeting = `hello ${string}` chấp nhận mọi chuỗi bắt đầu bằng hello , và `on${Capitalize<"click">}` là literal type "onClick".
Chuyện gì xảy ra khi đặt một union vào template literal type?
Template được mở rộng cho từng thành viên, và với nhiều union bạn sẽ có mọi tổ hợp. `${"sm" | "lg"}-${"red" | "blue"}` là "sm-red" | "sm-blue" | "lg-red" | "lg-blue". Các tổ hợp quá lớn sẽ thất bại với lỗi TS2590.
Uppercase, Lowercase, Capitalize và Uncapitalize làm gì?
Đây là các kiểu có sẵn dùng để biến đổi string literal type: Uppercase<"id"> là "ID", Lowercase<"ID"> là "id", Capitalize<"name"> là "Name" và Uncapitalize<"Name"> là "name". Chúng chỉ thay đổi kiểu; để đổi một chuỗi lúc chạy, bạn vẫn gọi toUpperCase() và các phương thức tương tự.
Template literal type có kiểm tra chuỗi lúc chạy không?
Không. Như mọi kiểu trong TypeScript, chúng bị xóa đi, nên chỉ kiểm tra chuỗi literal và các giá trị có kiểu lúc biên dịch. Một chuỗi đến lúc chạy, từ JSON hay từ đầu vào của người dùng, chỉ là string cho đến khi bạn tự viết code kiểm tra nó.
Vì sao template string của tôi có kiểu string thay vì literal type?
Một biểu thức template như `on${event}` bị mở rộng thành string khi gán cho một biến. Thêm as const (`on${event}` as const) hoặc chú thích kiểu đích, TypeScript sẽ giữ template literal type, ví dụ "onclick" | "onfocus".