Câu hỏi phỏng vấn TypeScript
A. Cơ bản
1. TypeScript là gì? Tại sao dùng?
TypeScript = JavaScript + static type system. Compile sang JS. Lợi ích: bắt bug sớm, IDE autocomplete/refactor mạnh, self-documenting, scale tốt với codebase lớn. Type không tồn tại runtime.
2. TS khác JS ở đâu?
TS thêm: kiểu, generic, enum, interface, decorator. JS chạy thẳng trên trình duyệt/Node. TS phải compile (qua tsc, tsx, swc, esbuild, hoặc Node 23+ với --strip-types).
3. TS 7.0 có gì mới?
⭐ TS 7.0 là bản nâng cấp lớn nhất trong lịch sử TypeScript với compiler Go rewrite (tsgo).
- tsgo — compiler viết bằng Go, compile nhanh 10x, editor startup nhanh ~8x.
--checkersvà--buildersflag — parallelism cho type-check và emit.strict: truetrở thành default từ TS 6.0, được giữ nguyên trong 7.0.module: esnexttrở thành default (trước đây làcommonjs).types: []mặc định — không tự động scan@types/*, phải khai báo rõ.noUncheckedSideEffectImports: truemặc định — cấm import có side-effect không kiểm soát.stableTypeOrderingbắt buộc — deterministic type ordering cho parallelism.- Remove
target: es5— minimum target làes2020. - Remove
module: amd | umd | systemjs— chỉ giữesnext,commonjs,node16,nodenext. - Remove
moduleResolution: node | classic— chỉ cònbundler,node16,nodenext. - Remove
baseUrl, removepreserveConstEnums. - Unicode template literal fix — sửa bug surrogate pairs và combining characters.
- Reduced context sensitivity — giảm ambiguous parse edge case.
4. TS 7 (Corsa / tsgo) là gì?
Corsa là codename cho dự án viết lại TypeScript compiler bằng Go. Tên chính thức của compiler binary là tsgo.
- RC1 ra mắt tháng 6/2026, stable dự kiến Q3/2026.
- Compile nhanh 5-10x, editor startup nhanh ~8x so với tsc TypeScript cũ.
- Viết bằng Go để tận dụng multi-threading native, memory efficiency và compile-to-native speed.
- LSP server cũng được viết lại, phản hồi gần như tức thì kể cả với codebase >100k LOC.
- TS 6.0 là bản transition — giữ tsc cũ nhưng thêm deprecation warning và default mới để chuẩn bị cho TS 7.0.
5. strict mode bao gồm flag nào?
noImplicitAny, strictNullChecks, strictFunctionTypes, strictBindCallApply, strictPropertyInitialization, alwaysStrict, useUnknownInCatchVariables, noImplicitThis. Project mới luôn bật. Từ TS 6.0+, strict: true là default cho project mới.
6. tsc --noEmit để làm gì?
Chỉ type-check, không emit JS. Dùng ở CI / pre-commit để check type mà không build. Build thật do bundler (Vite, esbuild) làm. Với TS 7.0, dùng tsgo --noEmit — nhanh hơn 10x.
B. Type cơ bản
7. any vs unknown vs never?
any: tắt type check — escape hatch, dùng càng ít càng tốt.unknown: any an toàn — phải narrow trước khi dùng.never: không có giá trị — function throw / loop vô tận trảnever. Dùng cho exhaustive check.
8. null vs undefined?
undefined = chưa gán, null = gán có chủ ý = không gì. Với strictNullChecks, 2 type khác nhau, phải khai báo rõ.
9. Type assertion as có check gì không?
Không. as chỉ tell compiler tin bạn, không validate runtime. Sai → bug.
10. ! (non-null assertion) là gì?
Tell compiler "tôi chắc giá trị không null/undefined". el!.click(). Cẩn thận — sai sẽ crash runtime.
11. as const dùng làm gì?
Ép literal narrow, immutable. Object/array thành readonly + literal type:
const colors = ["red", "blue"] as const; // readonly ["red", "blue"]
12. satisfies (TS 4.9+) khác as ra sao?
as không check — bypass. satisfies check value khớp type nhưng giữ literal narrow:
const palette = { red: [255,0,0] } satisfies Record<string, [number,number,number]>;
palette.red[0] // number — không bị widen từ tuple thành number[]
C. Union, intersection, narrowing
13. Union | vs Intersection &?
A | B: value là A HOẶC B (OR).A & B: value có đủ field A VÀ B (AND).
14. Discriminated union là gì?
Mỗi union member có 1 field "discriminator" giá trị literal:
type Shape = { kind: "circle"; r: number } | { kind: "square"; s: number };
switch trên kind → TS auto-narrow type, autocomplete đúng.
15. Type narrowing có những cách nào?
typeof(primitive)instanceof(class)inoperator (key tồn tại)- Truthiness (
if (x)) - Equality (
===,!==) - Discriminated union (switch on tag)
- Custom type guard (
x is Type) - Assertion function (
asserts x is Type)
16. Type guard (x is Type) là gì?
Function trả param is Type → khi true, TS narrow param sang Type:
function isString(x: unknown): x is string {
return typeof x === "string";
}
17. Exhaustive check với never?
Pattern bắt compile-time khi quên handle case mới:
default:
const _: never = s;
throw new Error(`Unhandled: ${s}`);
D. type vs interface
18. type vs interface — khi nào dùng cái nào?
Cả 2 mô tả object shape. Interface: extends, declaration merging — chuyên cho object/class. Type: flexible — làm được union, intersection, tuple, mapped. Rule: object public API → interface, alias/union/utility → type.
19. Declaration merging?
Cùng tên interface 2 lần → merge thành 1. Dùng để augment lib ngoài (mở rộng Express.Request...).
20. extends vs &?
interface B extends A: B kế thừa A (1 quan hệ).type B = A & X: intersection — gộp.
Khác biệt nhỏ: extends nhanh hơn check, intersection tạo type mới.
E. Generic
21. Generic giải quyết gì?
Code reuse + type-safety. Function/class chạy với nhiều type mà vẫn giữ type info — không cần any rồi cast.
22. Constraint T extends U là gì?
Hạn chế T phải compatible với U:
function len<T extends { length: number }>(x: T) { return x.length; }
23. keyof T trả về gì?
Union các key của T dưới dạng string literal type:
keyof { id: number; name: string } // "id" | "name"
24. T[K] (lookup) là gì?
Lấy value type theo key:
type Name = User["name"]; // string
25. Generic default?
<T = string> — nếu không truyền T, dùng default.
26. Generic constraint với keyof — pattern get?
function get<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
Đảm bảo key là một trong các key của obj, return đúng type.
F. Conditional, mapped, infer
27. Conditional type T extends U ? X : Y?
Type chọn theo điều kiện. T extends U check assignability.
28. Distributive conditional?
Khi T là naked type parameter (T extends X) và T là union → conditional apply cho từng union member rồi gộp:
type ToArray<T> = T extends any ? T[] : never;
type R = ToArray<string | number>; // string[] | number[]
29. Cách chống distribute?
Wrap T trong tuple: [T] extends [U] ? .... Không còn naked → không distribute.
30. infer keyword?
Đặt biến type trong nhánh conditional → extract type:
type Return<F> = F extends (...args: any) => infer R ? R : never;
31. Mapped type là gì?
Tạo type mới bằng cách map qua keys của type khác:
type Readonly<T> = { readonly [K in keyof T]: T[K] };
32. Key remapping (TS 4.1+)?
[K in keyof T as NewKey] — đổi tên key:
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
33. Template literal type?
Type là string literal có placeholder: `Hello, ${string}!`. Combine union để sinh permutation, dùng với infer để parse string ở type level.
G. Utility types
34. Pick<T, K> định nghĩa thế nào?
type Pick<T, K extends keyof T> = { [P in K]: T[P] };
35. Omit<T, K> định nghĩa?
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
36. Partial<T> định nghĩa?
type Partial<T> = { [P in keyof T]?: T[P] };
37. Required<T>?
type Required<T> = { [P in keyof T]-?: T[P] };
(-? = remove optional modifier.)
38. Readonly<T>?
type Readonly<T> = { readonly [P in keyof T]: T[P] };
39. Record<K, V>?
type Record<K extends keyof any, V> = { [P in K]: V };
40. ReturnType<F>?
type ReturnType<F extends (...a: any) => any> = F extends (...a: any) => infer R ? R : never;
41. Awaited<T>?
Unbox Promise đệ quy: Awaited<Promise<Promise<string>>> = string.
42. Exclude<T, U> vs Extract<T, U>?
Exclude<T, U>: T loại U.Extract<T, U>: T giao U.
Exclude<"a"|"b"|"c", "a"> // "b"|"c"
Extract<"a"|"b"|"c", "a"|"d"> // "a"
H. Function & class
43. Method overload trong TS?
function len(x: string): number;
function len(x: any[]): number;
function len(x: any): number { return x.length; }
Khai báo nhiều signature, implementation 1 cái.
44. this parameter?
function greet(this: { name: string }) { return this.name; }
TS check call site phải có this đúng type. Không emit code.
45. Class field arrow vs method?
method()—thisdynamic, bị mất khi unbind.arrowField = () => {}—thislexical, luôn = instance. Tốt cho React handler.
46. private TS vs #private JS?
privateTS: compile-time only, runtime vẫn truy cập được.#privateJS native: runtime enforced, truly private.
Mới project → ưu tiên #.
47. abstract class trong TS?
Class không instantiate trực tiếp, ép subclass implement abstract method:
abstract class Animal {
abstract speak(): string;
}
I. Decorator & enum
48. Decorator có 2 dạng — khác nhau ra sao?
- Legacy (
experimentalDecorators+emitDecoratorMetadata): NestJS, Angular cũ, TypeORM. - TC39 stage 3 (stable TS 5.0+, metadata 5.9): chuẩn ECMAScript, signature khác.
Đang migrate dần — hiểu cả 2.
49. Vì sao Enum đang bị deprecate?
Enum emit code runtime → khó tree-shake → bundle phình. Khuyến nghị const + as const:
const Status = { Active: "active", Inactive: "inactive" } as const;
type Status = typeof Status[keyof typeof Status];
50. const enum khác enum?
const enum inline literal vào nơi dùng — không emit code runtime. Trade-off: không debug runtime được, không hoạt động với isolatedModules. ⚠️ TS 7.0 đã remove preserveConstEnums — const enum luôn inline, không emit object runtime.
J. Real-world
51. Type-safe HTTP API client?
Dùng generic + Zod/Valibot để validate runtime:
async function fetchJson<T>(url: string, schema: ZodType<T>): Promise<T> {
const data = await fetch(url).then(r => r.json());
return schema.parse(data);
}
52. Branded type cho ID?
type UserId = number & { __brand: "UserId" };
Chống nhầm UserId với OrderId dù cùng number.
53. Type-safe Object.keys?
function typedKeys<T extends object>(o: T): (keyof T)[] {
return Object.keys(o) as (keyof T)[];
}
Object.keys trả string[] — phải cast về (keyof T)[].
54. Module augmentation?
Mở rộng type của module ngoài:
declare module "express" {
interface Request { user?: User; }
}
55. .d.ts file là gì?
Declaration file — chỉ chứa type, không có implementation. Dùng để type lib không có type (@types/...) hoặc xuất type cho lib bạn publish.
K. Performance & tooling
56. tsc chậm — giải pháp?
⭐ TS 7.0 (tsgo) chính là giải pháp: compile nhanh 10x, editor startup ~8x nhanh hơn. Nếu chưa migrate được:
Trước TS 7.0 (vẫn dùng tsc cũ):
incremental: true+tsBuildInfoFile.- Project references chia monorepo.
skipLibCheck: true.- Dùng
swc/esbuildcho build (chỉ strip type, không check). - CI:
tsc --noEmitchạy parallel với build.
TS 7.0 (tsgo):
--checkers N— số worker song song cho type-checking, mặc định = số CPU core.--builders N— số worker song song cho emit JS/output.- Worker pool xử lý các file độc lập cùng lúc, thay vì tuần tự như tsc cũ.
- Ví dụ:
tsgo --checkers 8 --builders 4 --noEmit
57. ESM vs CommonJS — chọn gì?
⭐ ESM là default từ TS 7.0 — module: esnext mặc định cho project mới.
- CommonJS đã là legacy. Không hỗ trợ tree-shake, dynamic import khác cú pháp.
- ESM modern, tree-shake tốt, async import native.
- ⚠️ TS 7.0 đã remove
module: amd | umd | systemjs— chỉ cònesnext,commonjs,node16,nodenext. - Bẫy ESM: import phải kèm
.jsextension dù file là.ts:import { foo } from "./foo.js"; // đúng, dù file là foo.ts
58. isolatedModules: true để làm gì?
Ép mỗi file compile độc lập — bắt buộc với esbuild/swc. Cấm một số TS feature (const enum, namespace re-export).
59. Babel TypeScript vs tsc?
Babel chỉ strip type, không check. Nhanh hơn nhưng không bắt lỗi type. → Dev nhanh dùng Babel/swc/esbuild, CI dùng tsc --noEmit check. Với TS 7.0, tsgo --noEmit nhanh hơn 10x nên có thể dùng trực tiếp trong dev loop.
60. Bạn dùng TS bao lâu? Project lớn nhất?
Câu open-ended — kể project thực, các challenge gặp (generic deep, conditional type cho API client, migration JS→TS, performance build). Bonus: nếu đã trải qua migration TS 5.x → 6.0 → 7.0, kể về quá trình update tsconfig, fix deprecation, và hiệu năng sau khi chuyển sang tsgo.
L. TypeScript 7.0 — Migration & Breaking Changes
61. TS 7.0 có gì khác TS 6.0 về mặt kiến trúc?
⭐ TS 7.0 là cuộc đại tu kiến trúc lớn nhất trong lịch sử TypeScript.
| TS 6.x trở về trước | TS 7.0 (tsgo) | |
|---|---|---|
| Ngôn ngữ compiler | TypeScript / JavaScript | Go |
| Runtime | Node.js | Native binary |
| Parallelism | Single-thread | Multi-thread (worker pool) |
| Pipeline | Parse → Bind → Check → Emit tuần tự | Parse song song, check song song, emit song song |
| Memory | V8 GC overhead | Go memory model, thấp hơn đáng kể |
| LSP | TypeScript-based, khởi động chậm | Viết lại native, phản hồi gần như tức thì |
Về mặt type system, TS 7.0 tương thích ngược với TS 6.x — không có breaking change lớn về type-level. Thay đổi tập trung vào compiler infrastructure và performance.
62. --checkers và --builders flag hoạt động ra sao?
Hai flag kiểm soát parallelism trong tsgo — tính năng cốt lõi giúp đạt tốc độ 10x:
--checkers N— số worker chạy song song cho type-checking. Mỗi checker nhận một nhóm file và type-check độc lập. Mặc định =os.cpus().length.--builders N— số worker chạy song song cho emit JS / declaration. Mặc định = số CPU core.
# Type-check với 8 worker, không emit
tsgo --checkers 8 --noEmit
# Build với 4 checker + 4 builder
tsgo --checkers 4 --builders 4
# Tận dụng tối đa CPU (mặc định)
tsgo
Cơ chế: tsgo chia file thành các chunk, phân phối cho worker pool. stableTypeOrdering đảm bảo kết quả type-checking nhất quán bất kể worker nào hoàn thành trước. Khác hoàn toàn với tsc cũ — toàn bộ pipeline là single-thread, tuần tự.
63. Những default nào thay đổi trong TS 6.0 / 7.0?
Quá trình thay đổi default diễn ra qua 2 phiên bản để giảm shock cho hệ sinh thái:
| Option | TS 5.x | TS 6.0 | TS 7.0 |
|---|---|---|---|
strict | false (default) | true (cho project mới) | true |
module | commonjs | commonjs (cảnh báo) | esnext |
types | auto-scan @types/* | auto-scan (deprecation warning) | [] (không auto-scan) |
noUncheckedSideEffectImports | false | false (cảnh báo) | true |
⭐ Ý nghĩa thực tế:
- Project mới tạo bằng TS 7.0 sẽ ESM-first, strict by default, không auto-scan global types.
- Code cũ cần update tsconfig rõ ràng khi migrate.
64. Những option nào bị remove trong TS 7.0?
TS 7.0 xóa các option legacy để giảm maintenance burden và tập trung vào modern JavaScript:
| Option bị remove | Lý do | Thay thế |
|---|---|---|
target: es5 | ES5 quá cũ, không còn phù hợp | target: es2020 trở lên |
module: amd | AMD không còn được dùng | module: esnext |
module: umd | UMD legacy, bundler hiện đại không cần | module: esnext |
module: systemjs | SystemJS gần như tuyệt chủng | module: esnext |
moduleResolution: node | Resolution cũ, không chuẩn ESM | moduleResolution: bundler hoặc node16 |
moduleResolution: classic | Quá cũ, không ai dùng | moduleResolution: bundler |
baseUrl | Gây nhập nhằng với ESM path | paths với relative mapping |
preserveConstEnums | Const enum luôn inline trong TS 7.0 | Không cần — mặc định behavior mới |
// tsconfig.json cho TS 7.0
{
"compilerOptions": {
// Không còn target: "es5"
"target": "es2022",
// Không còn module: "amd" | "umd" | "systemjs"
"module": "esnext",
// Không còn moduleResolution: "node" | "classic"
"moduleResolution": "bundler",
// baseUrl removed — dùng paths thay thế
"paths": {
"@/*": ["./src/*"]
},
"strict": true
}
}
65. Migration path từ TS 5.x lên TS 7.0 như thế nào?
Lộ trình migrate khuyến nghị qua 3 bước:
Bước 1: TS 5.x → TS 6.0 (transition)
npm install typescript@6
- Fix tất cả deprecation warning (các option sắp bị remove trong 7.0).
- Bật
strict: truecho project nếu chưa. - Chạy
tsc --noEmitđảm bảo không lỗi. - Update các option:
module,moduleResolution,targetvề giá trị TS 7.0 chấp nhận.
Bước 2: TS 6.0 → TS 7.0 (tsgo)
npm install typescript@7
- Cập nhật tsconfig: xóa các option đã bị remove (
baseUrl,preserveConstEnums, etc.). - Thêm
typesarray rõ ràng nếu cần global types. - Switch từ
tscsangtsgo. - Verify tooling: IDE extension, CI pipeline, pre-commit hooks.
Bước 3: Verify & optimize
tsgo --noEmit # type-check với tsgo
tsgo --checkers 8 --builders 4 # build với parallelism
- Đo benchmark so với tsc cũ.
- Tinh chỉnh
--checkersdựa trên số CPU core của CI runner.
⭐ Có thể chạy song song TS 6 và TS 7 trong giai đoạn transition (xem Q67).
66. Tool nào compatible với tsgo?
| Tool | Status | Ghi chú |
|---|---|---|
| Vite 6+ | ✅ Compatible | Dùng tsgo làm built-in type checker |
| VS Code | ✅ Compatible | Cập nhật LSP extension cho tsgo |
| esbuild / swc | ✅ Compatible | Vẫn dùng riêng để strip type, không bị ảnh hưởng |
| tsx | ✅ Compatible | Dùng esbuild under the hood, không phụ thuộc compiler API |
| ts-node | ❌ Không hoạt động | Phụ thuộc programmatic API cũ của tsc |
| ts-jest | ❌ Không hoạt động | Phụ thuộc compiler API cũ |
| ts-morph | ❌ Không hoạt động | Dựa trên compiler API cũ để thao tác AST |
| fork-ts-checker-webpack-plugin | ❌ Không hoạt động | Phụ thuộc tsc programmatic API |
Giải pháp thay thế:
- Thay
ts-node→tsx(nhanh hơn, ESM-native). - Thay
ts-jest→vitest(dùng esbuild, nhanh hơn nhiều lần). - Nếu tool phụ thuộc programmatic API và chưa có replacement, giữ TS 6.x (xem Q67 để chạy song song).
67. Làm sao để chạy song song TS 6 và TS 7 trong cùng project?
Dùng npm package aliases để cài cả 2 phiên bản cùng lúc:
// package.json
{
"devDependencies": {
// TS 7 (default) cho IDE và dev build
"typescript": "npm:typescript@^7.0.0",
// TS 6 giữ lại cho tool chưa compatible
"typescript6": "npm:typescript@^6.0.0"
},
"scripts": {
"check": "tsgo --noEmit", // TS 7
"check:legacy": "tsc6 --noEmit", // TS 6 fallback
"build": "tsgo --checkers 4",
"test": "vitest" // không cần ts-jest nữa
}
}
# Dùng tsc6 cho TS 6
npx tsc6 --noEmit
# Dùng tsgo cho TS 7
npx tsgo --noEmit
⭐ CI strategy: chạy cả 2 checker (TS 6 + TS 7) để đảm bảo compatibility trong giai đoạn transition, dần dần bỏ TS 6 khi ecosystem đã sẵn sàng.
68. stableTypeOrdering là gì? Tại sao bắt buộc trong TS 7.0?
⭐ stableTypeOrdering là cơ chế đảm bảo thứ tự type trong union/intersection luôn deterministic — điều kiện tiên quyết cho parallel type-checking.
Vấn đề trước TS 7.0: Thứ tự member trong union type (vd: "a" | "b") có thể khác nhau giữa các lần chạy, phụ thuộc vào thứ tự file được parse trong pipeline tuần tự. Không ảnh hưởng đến type compatibility nhưng gây ra non-deterministic output.
TS 7.0 với parallelism: Các checker worker xử lý file song song, hoàn thành theo thứ tự khác nhau. Nếu không có stableTypeOrdering, kết quả merge union/intersection sẽ khác nhau giữa các lần chạy — dẫn đến:
- Declaration file (.d.ts) không ổn định.
- Cache invalidation sai.
- CI false positive/negative.
Cách hoạt động: Union và intersection member được sắp xếp theo canonical order (alphabetical cho string literal, structural ordering cho object type) trước khi merge.
⚠️ Trade-off: Một số pattern hiếm dựa vào thứ tự type cũ có thể bị ảnh hưởng (vd: code dùng union member indexing). Nhưng hầu hết code không bị ảnh hưởng.
69. Programmatic API trong TS 7.0 thay đổi ra sao?
⭐ Programmatic API cũ bị xóa hoàn toàn trong TS 7.0 — đây là breaking change lớn nhất cho ecosystem.
API cũ bị remove (TS 6.x trở về trước):
// TẤT CẢ function này bị xóa trong TS 7.0
import ts from "typescript";
ts.createProgram(...) // ❌
ts.createSourceFile(...) // ❌
ts.createCompilerHost(...) // ❌
ts.transform(...) // ❌
ts.factory.createCall(...) // ❌
// ... toàn bộ ts.* API
Lý do: API cũ được thiết kế cho kiến trúc single-thread, monolithic — không thể map sang kiến trúc multi-thread worker pool của tsgo.
API mới (dự kiến TS 7.1+):
- Thiết kế lại hoàn toàn: async-first, streaming, worker-aware.
- API mới tập trung vào các use case chính: programmatic type-check, emit, transform.
- Không còn cho phép thao tác AST trực tiếp như trước (để đảm bảo thread safety).
Tác động:
ts-morph,ts-node,ts-jest,fork-ts-checker-webpack-plugin— không hoạt động với TS 7.0.- Các tool này cần được rewrite để dùng API mới (dự kiến TS 7.1) hoặc fallback về TS 6.x.
70. Có nên migrate lên TS 7.0 ngay khi stable không?
⭐ Tùy ngữ cảnh — không có câu trả lời một-size-fits-all.
| Tình huống | Khuyến nghị | Lý do |
|---|---|---|
| App nội bộ / startup | ✅ NÊN migrate sớm | Hưởng speed 10x, DX tốt hơn, ít phụ thuộc ecosystem tooling |
| Library public | ⚠️ THẬN TRỌNG | Đợi ecosystem tooling ổn định, test downstream user |
| Monorepo lớn (>100k LOC) | ✅ CÂN NHẮC sớm | Speed boost đáng giá nhất với codebase lớn. Verify CI/CD pipeline kỹ |
| Đang dùng ts-node / ts-jest | ⚠️ CHỜ | Chưa có replacement ổn định. Chạy song song TS 6 (xem Q67) |
| Team nhỏ, ít test | ⚠️ CHỜ | Rủi ro cao nếu tooling break mà không có fallback |
Lộ trình an toàn:
- Tạo branch riêng, upgrade lên TS 7.0.
- Chạy benchmark (type-check time, build time) để đo improvement.
- Verify tất cả tooling: IDE, lint, test, CI, deploy.
- Nếu tất cả OK → merge. Nếu tooling break → đợi hoặc dùng Q67 (song song).
71. TS 7.0 có thay đổi gì về type system không?
⭐ Type system của TS 7.0 tương thích ngược với TS 6.x — không có breaking change lớn về type-level. Tuy nhiên có một số thay đổi đáng chú ý:
Thay đổi về type system:
- Unicode template literal fix: Sửa bug với surrogate pairs và combining characters trong template literal type. Trước đây một số ký tự Unicode đa byte bị xử lý sai khi dùng trong
inferpattern. stableTypeOrderingbắt buộc: Union/intersection luôn có thứ tự deterministic (xem Q68). Không ảnh hưởng đến tính đúng đắn của type, nhưng .d.ts output có thể thay đổi.- Reduced context sensitivity: Giảm một số edge case ambiguous parse — code vốn parse sai do context sensitivity giờ được bắt lỗi compile-time.
Không thay đổi:
- Conditional types, mapped types, infer — hoạt động y hệt.
- Generic constraint và inference.
- Discriminated union narrowing.
- Template literal type (ngoại trừ Unicode fix).
Kết luận: Nếu code của bạn type-check OK với TS 6.0 strict mode, gần như chắc chắn sẽ OK với TS 7.0. Thay đổi chủ yếu ở compiler infrastructure, không phải type system.
72. types: [] default mới ảnh hưởng gì?
⭐ Từ TS 7.0, types mặc định là [] — trình biên dịch KHÔNG tự động scan @types/* trong node_modules nữa.
Trước TS 7.0:
// tsconfig.json (TS 5.x / 6.x)
{
"compilerOptions": {
// Không set "types" → tự động scan TẤT CẢ @types/*
// Jest, Node, React... tự động có sẵn dưới dạng global type
}
}
// Code cũ — dùng global type mà không cần import
describe("test", () => { ... }); // ✅ auto từ @types/jest
process.env.NODE_ENV; // ✅ auto từ @types/node
TS 7.0:
{
"compilerOptions": {
// "types": [] là default — KHÔNG scan @types/*
// Phải khai báo rõ
"types": ["node", "jest", "react-dom"]
}
}
// Nếu không set "types": ["jest"] → lỗi
describe("test", () => { ... }); // ❌ Cannot find name 'describe'
Lợi ích:
- Type-checking scope chính xác hơn, tránh xung đột global type từ các package không liên quan.
- Build nhanh hơn (không scan hàng trăm @types package).
- Explicit dependency — dễ audit type nào đang được dùng.
Migration fix đơn giản: Thêm "types": ["node", "jest", ...] — liệt kê các @types package bạn thực sự cần.
73. Node.js type stripping vs tsgo — dùng cái nào?
⭐ Hai công cụ phục vụ 2 mục đích khác nhau — hiểu rõ để chọn đúng cho từng ngữ cảnh.
Node.js --strip-types (Node 23+) | tsgo (TS 7.0) | |
|---|---|---|
| Type-check? | ❌ Không — chỉ xóa syntax TS | ✅ Có — full type-check |
| Tốc độ | Gần như instant (chỉ parse + strip) | Nhanh hơn tsc 10x, nhưng vẫn chậm hơn strip |
| Output | JS (giữ nguyên format, chỉ xóa annotation) | JS (compile đầy đủ: downlevel, enum, decorator...) |
| Runtime | Node.js native | Go binary độc lập |
| Phù hợp | Dev loop nhanh, hot reload | CI, production build, type safety |
Best practice kết hợp:
// package.json
{
"scripts": {
// Dev: nhanh nhất có thể, không cần type-check mỗi lần save
"dev": "node --strip-types --watch src/index.ts",
// CI / pre-commit: type-check với tsgo (nhanh 10x)
"typecheck": "tsgo --noEmit",
// Production build: esbuild/swc strip type + tsgo check
"build": "tsgo --noEmit && esbuild src/index.ts --bundle --outdir=dist"
}
}
Nguyên tắc:
- Dev loop → Node.js
--strip-types(tốc độ tối đa, không cần type-check từng lần save). - CI / pre-push →
tsgo --noEmit(bắt lỗi type trước khi merge). - Production build → tsgo check + esbuild/swc bundle (an toàn + nhanh).