any, unknown và never khác nhau thế nào?
any tắt kiểm tra kiểu; unknown là 'any an toàn' (phải thu hẹp kiểu trước khi dùng); never là kiểu không có giá trị (hàm throw, nhánh không thể xảy ra).
Các utility types thường dùng: Partial, Pick, Omit, Record, ReturnType
Utility types là generic dựng sẵn để biến đổi type: `Partial<T>` làm mọi field optional, `Pick`/`Omit` chọn/loại field, `Record<K, V>` tạo object map, `ReturnType<F>` lấy kiểu trả về của hàm, `Readonly`, `Required`, `NonNullable`, `Awaited`... Chúng chỉ tồn tại ở compile time.
enum và const object (as const) khác nhau thế nào? Nên dùng cái nào?
`enum` tạo ra code runtime (numeric enum còn có reverse mapping), còn string-literal union hoặc `const` object + `as const` chỉ là type, không thêm gì vào bundle. Hiện nay thường ưu tiên const object/union vì tương thích với tooling chỉ xóa type và dễ debug.
interface và type khác nhau thế nào?
Cả hai mô tả hình dạng object. interface có thể mở rộng/khai báo gộp (declaration merging); type linh hoạt hơn: hỗ trợ union, tuple, mapped/conditional type.
readonly, Readonly và `as const` khác nhau thế nào?
`readonly` cấm gán lại property/phần tử ở compile time (nông, không bảo vệ runtime). `as const` suy luận kiểu hẹp nhất: literal type, tuple và mọi thứ thành readonly đệ quy. Muốn bất biến thật ở runtime cần `Object.freeze`.
Type narrowing và type guard là gì? Discriminated union hoạt động ra sao?
Narrowing là việc TypeScript thu hẹp union type trong một nhánh code nhờ `typeof`, `instanceof`, `in`, so sánh bằng, truthiness hoặc type guard tự định nghĩa (`x is T`). Discriminated union dùng một field literal chung (`kind`) để narrow chính xác, kèm kiểm tra `never` để bắt case thiếu.
`strict` trong tsconfig bật những flag nào? Nên bật thêm flag nào?
`strict: true` bật nhóm kiểm tra chặt: `strictNullChecks`, `noImplicitAny`, `strictFunctionTypes`, `strictBindCallApply`, `strictPropertyInitialization`, `noImplicitThis`, `useUnknownInCatchVariables`, `alwaysStrict`. Nên bật thêm `noUncheckedIndexedAccess`, `exactOptionalPropertyTypes`, `noImplicitOverride` vì không nằm trong `strict`.
Function overload trong TypeScript hoạt động thế nào? Khi nào nên dùng?
Overload khai báo nhiều signature cho một hàm; implementation signature đi sau cùng, phải tương thích với mọi overload và không hiển thị với bên ngoài. TS chọn overload đầu tiên khớp (từ trên xuống). Dùng khi kiểu trả về phụ thuộc kiểu đối số mà union/generic không biểu diễn được.
Generics là gì? Khi nào dùng?
Generics là tham số hóa kiểu: viết một hàm/kiểu dùng được cho nhiều kiểu mà vẫn giữ type-safety, thay vì dùng any.
Mapped types là gì? Modifier `+/-` và key remapping `as`
Mapped type tạo type mới bằng cách lặp qua các key: `{ [K in keyof T]: ... }`. Có thể thêm/bỏ modifier bằng `+`/`-` với `readonly` và `?`, và lọc hoặc đổi tên key với mệnh đề `as`. Hầu hết utility types (Partial, Readonly, Pick, Record) là mapped type.
Structural typing là gì? Làm sao mô phỏng nominal typing (branded types)?
TypeScript dùng structural typing: hai type tương thích nếu cấu trúc khớp, bất kể tên. Vì vậy `UserId` và `OrderId` cùng là string vẫn đổi chỗ được. Excess property check chỉ áp dụng cho object literal trực tiếp. Muốn phân biệt thì dùng branded type (intersection với tag giả).
Toán tử satisfies khác type annotation và `as` thế nào?
`satisfies` (TS 4.9) kiểm tra giá trị khớp một type nhưng giữ nguyên kiểu suy luận cụ thể của nó. Annotation `: T` làm rộng kiểu về T, còn `as T` ép kiểu bỏ qua kiểm tra. Dùng `satisfies` để vừa bắt lỗi cấu hình sai vừa giữ literal/key chính xác.
Conditional types và infer hoạt động thế nào? Distributive conditional type là gì?
`T extends U ? X : Y` chọn type theo điều kiện; `infer R` trong nhánh `extends` trích một type con (như phần tử mảng, kiểu trả về). Khi T là type tham số trần và nhận union thì điều kiện phân phối (distributive) lên từng thành viên; bọc `[T]` để tắt hành vi đó.
Declaration merging và module augmentation là gì?
Declaration merging: nhiều khai báo cùng tên (interface, namespace, enum, class + interface...) được TS gộp thành một định nghĩa. `type` alias không merge được. Module augmentation dùng `declare module` hoặc `declare global` để mở rộng type của thư viện/global (Express Request, Window) mà không sửa mã nguồn.
Template literal types là gì? Ứng dụng thực tế
Template literal type dựng string type từ các literal khác bằng cú pháp template, ví dụ `on${Capitalize<E>}`. Khi gặp union, nó sinh tích Descartes của mọi tổ hợp. Kết hợp `infer` để parse chuỗi (route params, event name) và với key remapping của mapped type để sinh API.
Type TypeScript và runtime validation: vì sao cần zod?
Type bị xóa khi compile nên không bảo vệ dữ liệu vào lúc chạy (API response, JSON.parse, form, env). Thư viện schema như zod định nghĩa schema một lần, vừa validate runtime vừa suy ra type bằng `z.infer`, giúp biên giới hệ thống (trust boundary) an toàn thật sự thay vì chỉ ép kiểu bằng `as`.
Variance: covariance, contravariance, invariance trong TypeScript
Covariance: `Box<Dog>` gán được cho `Box<Animal>` (vị trí đầu ra/chỉ đọc). Contravariance: tham số hàm đảo chiều, `(a: Animal) => void` gán được cho `(d: Dog) => void` (bật bằng `strictFunctionTypes`). Invariance: cần cả hai chiều. TS cố ý lỏng với mảng và method (bivariant) nên không hoàn toàn sound.