TypeScript

Advanced types — Generic, Conditional, Mapped, Utility

Generic + constraint, conditional types, mapped types, infer, template literal, decorator, utility types.

1. Generic — cốt lõi

function identity<T>(x: T): T {
  return x;
}

identity<number>(42);
identity("hello");                   // type inferred

// Generic function với nhiều param
function pair<A, B>(a: A, b: B): [A, B] {
  return [a, b];
}

// Generic class
class Box<T> {
  constructor(public value: T) {}
  map<U>(fn: (x: T) => U): Box<U> {
    return new Box(fn(this.value));
  }
}

// Generic interface
interface Repo<T> {
  findById(id: number): T | undefined;
  add(item: T): void;
}

2. Generic constraint — extends

// T phải có property "length"
function longest<T extends { length: number }>(a: T, b: T): T {
  return a.length >= b.length ? a : b;
}

longest("foo", "bars");              // OK — string có length
longest([1, 2], [1, 2, 3]);          // OK — array có length
longest(1, 2);                       // ❌ — number không có length

// keyof constraint — key của object
function get<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}
const user = { id: 1, name: "Alice" };
get(user, "name");                   // string
get(user, "age");                    // ❌ — "age" không phải key của user
Câu PV cực hay:"keyof T là gì?"

"keyof T trả về union các key của T dưới dạng string literal type. Ví dụ keyof { id: number; name: string } = "id" | "name". Dùng để type-safe access property qua tên key — phổ biến với pattern get/set theo path."

3. Generic default

interface ApiResponse<TData = unknown, TError = Error> {
  data: TData;
  error?: TError;
}

const r1: ApiResponse = { data: "x" };           // TData = unknown
const r2: ApiResponse<User> = { data: {...} };

4. Conditional types — T extends U ? X : Y

// Cơ bản
type IsString<T> = T extends string ? "yes" : "no";
type A = IsString<"hello">;          // "yes"
type B = IsString<42>;               // "no"

// Distributive — apply cho từng union member
type ToArray<T> = T extends any ? T[] : never;
type C = ToArray<string | number>;   // string[] | number[]  (không phải (string|number)[])

// Non-distributive — wrap trong tuple
type ToArrayBox<T> = [T] extends [any] ? T[] : never;
type D = ToArrayBox<string | number>; // (string | number)[]
Tại sao conditional với generic naked phân phối qua union? Vì TS coi T extends X ? ... : ... với T "naked" là distributive — chạy cho từng member rồi gộp.

5. infer — đặt biến type trong conditional

// Lấy return type của function
type ReturnTypeOf<F> = F extends (...args: any) => infer R ? R : never;

type T1 = ReturnTypeOf<() => string>;          // string
type T2 = ReturnTypeOf<typeof Math.random>;    // number

// Lấy parameter type
type ParamsOf<F> = F extends (...args: infer P) => any ? P : never;
type T3 = ParamsOf<(a: number, b: string) => void>;   // [number, string]

// Unbox Promise
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
type T4 = UnwrapPromise<Promise<string>>;      // string
type T5 = UnwrapPromise<number>;               // number

// Unbox Array
type Element<T> = T extends (infer E)[] ? E : never;
type T6 = Element<number[]>;                   // number
type T7 = Element<[string, number]>;           // string | number
infer cực hay trong PV junior/senior. Khi hiểu được pattern này → bạn đọc được mọi utility type built-in.

6. Mapped types — biến đổi shape

// Cơ bản — map qua key
type Readonly<T> = { readonly [K in keyof T]: T[K] };
type Partial<T> = { [K in keyof T]?: T[K] };
type Required<T> = { [K in keyof T]-?: T[K] };          // remove ?
type Mutable<T> = { -readonly [K in keyof T]: T[K] };   // remove readonly

// Key remapping (TS 4.1+) — `as`
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
// Getters<{ name: string; age: number }>
// = { getName: () => string; getAge: () => number }

7. Template literal types

type Greeting = `Hello, ${string}!`;
const g1: Greeting = "Hello, world!";       // OK
const g2: Greeting = "Bye";                 // ❌

// Combine union
type Color = "red" | "blue";
type Size = "small" | "large";
type Variant = `${Color}-${Size}`;
// "red-small" | "red-large" | "blue-small" | "blue-large"

// Tách string
type SplitFirst<S extends string> =
  S extends `${infer Head}/${infer Tail}` ? Head : never;
type T = SplitFirst<"users/123/profile">;   // "users"
Template literal type + infer cho phép parse string lúc compile — type-safe API path, SQL builder, etc. Power user feature.
TS 7.0 fix Unicode: Template literal type với infer giờ xử lý đúng emoji / surrogate pairs:
type HeadTail<S> = S extends `${infer Head}${infer Tail}` ? [Head, Tail] : never;

// TS 7.0: HeadTail<"👋🌍"> => ["👋", "🌍"] — đúng!
// TS cũ:     HeadTail<"👋🌍"> => ["\uD83D", "\uDC4B🌍"] — sai do surrogate pairs

8. Utility types built-in (phải thuộc)

UtilityTác dụng
Partial<T>Mọi field thành optional
Required<T>Mọi field thành bắt buộc
Readonly<T>Mọi field thành readonly
Pick<T, K>Chọn subset key K
Omit<T, K>Bỏ subset key K
Record<K, V>Object với key K, value V
Exclude<T, U>T loại U (cho union)
Extract<T, U>T giao U
NonNullable<T>T loại null/undefined
ReturnType<F>Return type của function F
Parameters<F>Tuple parameter của F
Awaited<T>Unbox Promise lồng nhau
InstanceType<C>Instance của constructor class
Uppercase<S> / Lowercase<S> / Capitalize<S> / Uncapitalize<S>Biến đổi string literal
interface User { id: number; name: string; age: number; }

type UserPreview = Pick<User, "id" | "name">;      // { id; name }
type UserNoAge = Omit<User, "age">;                // { id; name }
type UserPartial = Partial<User>;                  // tất cả optional
type UserName = User["name"];                      // string  (lookup type)
type UserKeys = keyof User;                        // "id" | "name" | "age"

type Status = "active" | "inactive" | "deleted";
type Live = Exclude<Status, "deleted">;            // "active" | "inactive"
type Dead = Extract<Status, "deleted">;            // "deleted"

async function fetch(): Promise<User> { ... }
type R = Awaited<ReturnType<typeof fetch>>;        // User
Câu PV nhiều khả năng:"Pick, Omit, Partial định nghĩa thế nào?" — Hỏi để check bạn có hiểu mapped + conditional types không.
type Pick<T, K extends keyof T> = { [P in K]: T[P] };
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
type Partial<T> = { [P in keyof T]?: T[P] };

9. Index access type — T[K]

interface User { id: number; name: string; address: { city: string } }

type Name = User["name"];                  // string
type Id = User["id"];                      // number
type City = User["address"]["city"];       // string nested

// Combine với keyof — lookup mọi value type
type UserValue = User[keyof User];         // string | number | { city: string }

// Array element
type Items = string[];
type Item = Items[number];                 // string

// Tuple element
type Tup = [string, number, boolean];
type T0 = Tup[0];                          // string
type Any = Tup[number];                    // string | number | boolean

10. Decorator (TC39 stage 3, stable TS 5.0+, metadata stable 5.9)

// Class decorator
function logged<T extends { new (...args: any[]): {} }>(target: T) {
  return class extends target {
    constructor(...args: any[]) {
      super(...args);
      console.log(`Created ${target.name}`);
    }
  };
}

@logged
class Service { }

// Method decorator (TC39 stage 3 syntax)
function logTime(originalMethod: any, context: ClassMethodDecoratorContext) {
  return function (this: any, ...args: any[]) {
    console.time(String(context.name));
    const result = originalMethod.apply(this, args);
    console.timeEnd(String(context.name));
    return result;
  };
}

class Calculator {
  @logTime
  sum(a: number, b: number) { return a + b; }
}
Có 2 dạng decorator:
  • Legacy / experimental (experimentalDecorators: true + emitDecoratorMetadata) — dùng cho NestJS/Angular/TypeORM truyền thống.
  • TC39 stage 3 — chuẩn ECMAScript, stable từ TS 5.0. Khác signature.
Frameworks đang migrate dần — biết cả 2.

11. Module augmentation & declaration merging

// Mở rộng module ngoài
declare module "express" {
  interface Request {
    user?: { id: number; email: string };
  }
}

// Sau đó dùng req.user trong route handler — TS biết kiểu
app.get("/me", (req, res) => {
  res.json(req.user);                       // typed
});

12. Common patterns — phải biết

Branded type / nominal type

type UserId = number & { __brand: "UserId" };
type OrderId = number & { __brand: "OrderId" };

function getUser(id: UserId) { ... }

const uid = 1 as UserId;
const oid = 2 as OrderId;
getUser(uid);          // OK
getUser(oid);          // ❌ — chống nhầm ID

Type-safe object key iteration

// Object.keys trả string[] — TS không biết key cụ thể
function typedKeys<T extends object>(obj: T): (keyof T)[] {
  return Object.keys(obj) as (keyof T)[];
}

for (const key of typedKeys(user)) {
  user[key];           // typed correctly
}

Builder pattern type-safe

class QueryBuilder<T = {}> {
  constructor(private state: T = {} as T) {}
  
  where<K extends string, V>(key: K, value: V): QueryBuilder<T & Record<K, V>> {
    return new QueryBuilder({ ...this.state, [key]: value } as T & Record<K, V>);
  }
  
  build(): T { return this.state; }
}

const q = new QueryBuilder()
  .where("name", "Alice")
  .where("age", 25)
  .build();
// q: { name: string; age: number }

13. TypeScript 7.0 — Go compiler & song song hóa type-checking

TS 7.0 là bản nâng cấp lớn nhất từ trước đến nay: toàn bộ compiler được viết lại bằng Go, thay thế JavaScript cũ. Điểm nổi bật nhất là type-checking song song — nhiều luồng kiểm tra type cùng lúc.

CLI flags mới

# Type-checking song song
tsgo --checkers 8          # 8 worker thread kiểm tra type (mặc định 4)

# Build song song cho monorepo
tsgo --builders 4          # 4 project reference builder song song

# Chạy đơn luồng (khi debug)
tsgo --singleThreaded

# Kết hợp: --checkers 4 --builders 4 = tối đa 16 type-checker đồng thời

Cách parallel type-checking hoạt động

  • Mỗi worker nhận input file giống hệt nhau
  • Phân chia công việc deterministic — cùng kết quả mỗi lần chạy
  • stableTypeOrdering: true bắt buộc — đảm bảo kết quả nhất quán giữa các worker
  • Parsingemitting cũng được song song hóa tự động
  • Dùng nhiều checker → dùng nhiều RAM hơn — cần tune cẩn thận
  • Hiệu ứng cấp số nhân với --builders × --checkers

Performance tuning

Tình huốngGợi ý
Dự án nhỏ (< 500 file)--checkers 2 — đủ dùng, tiết kiệm RAM
Dự án vừa (500 – 5000 file)--checkers 4 (mặc định)
Dự án lớn (> 5000 file)--checkers 8 hoặc cao hơn, monitor RAM
Monorepo nhiều package--builders 4 cho project references
CI/CD pipeline--checkers 8 --builders 2 — ưu tiên type-check từng project
Local dev--incremental + --watch — rebuild nhanh

Chiến lược monorepo

# Build toàn bộ monorepo với project references
tsgo --build tsconfig.json --builders 4

# Chỉ type-check dự án hiện tại (không build dependency)
tsgo --noEmit --checkers 8

# Watch mode — rebuild khi file thay đổi (dùng @parcel/watcher port)
tsgo --watch --incremental

stableTypeOrdering — union ordering bắt buộc

Quan trọng cho advanced types: Trong TS 7.0, stableTypeOrderingluôn bật, không tắt được.
  • Union types có thứ tự deterministic giữa mọi lần build
  • Conditional types phụ thuộc thứ tự union sẽ chạy nhất quán
  • Có thể lộ bug nếu code của bạn vô tình dựa vào thứ tự union cũ (không ổn định)
// TS 7.0: union ordering luôn deterministic
type A = "a" | "b" | "c";   // luôn "a" | "b" | "c" (theo thứ tự khai báo)
type B = "c" | "b" | "a";   // luôn "a" | "b" | "c" (TS sắp xếp canonical)

// Conditional distributive qua union — kết quả nhất quán giữa mọi lần build
type Filter<T, U> = T extends U ? T : never;
type R = Filter<"a" | "b" | "c", "b" | "c">;  // "b" | "c" — deterministic

Programmatic API — CẢNH BÁO

🚨 TS 7.0 không có Compiler API cũ!
  • ts.createProgram, ts.createSourceFile, ts.transformđã bị xóa
  • Các tool như ts-morph, ts-jest, ts-node hiện không hoạt động với tsgo
  • Compiler API mới sẽ có trong TS 7.1
  • Giải pháp tạm thời: giữ TS 6 cho tool cần API, dùng tsgo cho build và type-check
# Build & type-check — dùng tsgo
npx tsgo --noEmit

# Tool cần Compiler API — dùng TS 6
npx tsc --version   # vẫn là TS 6.x

Build mode cải tiến

# Watch mode (viết lại bằng Go, dùng @parcel/watcher port)
tsgo --watch

# Build kèm declaration files
tsgo --declaration

# Build incremental
tsgo --incremental

# Project references (hỗ trợ đầy đủ)
tsgo --build tsconfig.json

14. TS 6.0 — tính năng chuyển tiếp

noUncheckedSideEffectImports

// tsconfig.json — mặc định bật từ TS 6.0+
{
  "compilerOptions": {
    "noUncheckedSideEffectImports": true
  }
}

Bắt lỗi import side-effect không kiểm soát:

// ❌ Bị bắt nếu file import không có type/value export
import "./polyfills";           // side-effect import — phải khai báo rõ

// ✅ OK nếu polyfills.ts có export ít nhất 1 type/value
import "./setup";               // setup.ts export hàm init()

Temporal API — kiểu dựng sẵn

// Temporal API (Stage 3) — có sẵn lib typing trong TS 6.0+
const now = Temporal.Now.plainDateISO();       // Temporal.PlainDate
const zoned = Temporal.Now.zonedDateTimeISO("Asia/Saigon"); // Temporal.ZonedDateTime
const dur = Temporal.Duration.from({ hours: 2, minutes: 30 }); // Temporal.Duration

RegExp.escape()

// Escape string để dùng trong RegExp — có typing sẵn
const escaped = RegExp.escape(".$?*+{}|()[]"); // "\\.\\$\\?\\*\\+\\{\\}\\|\\(\\)\\[\\]"
const re = new RegExp(escaped);                 // RegExp

Iterable & AsyncIterable trên DOM types

// DOM collections giờ implement Iterable / AsyncIterable
const nodes = document.querySelectorAll("div");
for (const el of nodes) { }           // ✅ Iterable<HTMLDivElement>

const stream = response.body;         // ReadableStream
for await (const chunk of stream) { } // ✅ AsyncIterable<Uint8Array>
Câu PV:"TS 7.0 thay đổi gì lớn nhất?"

"Compiler viết lại bằng Go, hỗ trợ type-checking song song với --checkers. stableTypeOrdering bắt buộc khiến union ordering deterministic. Compiler API cũ bị xóa — tool như ts-morph/ts-node chưa hoạt động cho đến TS 7.1. Build mode có --watch, --incremental, --declaration chạy qua tsgo."

© 2026 .NET Fresher Guide. All rights reserved.

Về Trang Web

Hướng dẫn toàn diện để chuẩn bị phỏng vấn vị trí .NET Fresher với nội dung từ lý thuyết đến thực hành.