
2026-08-03 · 阅读 5
TypeScript
TypeScript
TypeScript 基础类型系统 - 面试复习笔记
本文档整理了 TypeScript 基础类型系统的核心知识点,适合面试准备和日常复习使用。
目录
基础类型系统
高级特性
1. 接口 vs 类型别名
核心概念
接口(Interface):
- 用于定义对象的形状(结构)
- 可以被类实现(
implements) - 可以继承(
extends) - 支持声明合并
类型别名(Type Alias):
- 可以给任何类型起别名(不仅限于对象)
- 更灵活,支持联合类型、交叉类型、元组等
- 不能被类实现
- 不支持声明合并
代码示例
// 1. 基本对象定义 - 两者都可以
interface IUser {
name: string;
age: number;
}
type TUser = {
name: string;
age: number;
};
// 2. 类型别名可以定义任何类型
type ID = string | number; // 联合类型
type Tuple = [string, number]; // 元组
type Callback = (data: string) => void; // 函数类型
// 3. 接口可以继承
interface IAdmin extends IUser {
permissions: string[];
}
// 类型别名使用交叉类型实现继承
type TAdmin = TUser & {
permissions: string[];
};
// 4. 接口支持声明合并
interface Config {
host: string;
}
interface Config {
port: number;
}
// 最终 Config = { host: string; port: number; }
// 5. 类只能实现接口
class UserImpl implements IUser {
name = "John";
age = 30;
}
面试考点
问: 什么时候用接口,什么时候用类型别名?
答:
- 用接口: 定义对象的形状、需要被类实现、需要继承扩展
- 用类型别名: 联合类型、交叉类型、元组、函数类型、基本类型别名
2. 联合类型与交叉类型
核心概念
联合类型(Union Types):
- 表示"或"的关系,使用
|操作符 - 一个值可以是多种类型之一
- 常用于函数参数、返回值、变量定义
交叉类型(Intersection Types):
- 表示"且"的关系,使用
&操作符 - 一个值必须同时满足所有类型
- 常用于对象合并、Mixin 模式
代码示例
// 1. 联合类型 - "或"的关系
type ID = string | number;
type Status = 'pending' | 'success' | 'failed';
function printId(id: ID) {
console.log(id);
}
printId('abc'); // ✅
printId(123); // ✅
// 2. 交叉类型 - "且"的关系
interface Business {
name: string;
businessType: string;
}
interface Contact {
email: string;
phone: string;
}
type BusinessCard = Business & Contact;
const card: BusinessCard = {
name: '公司A',
businessType: '科技',
email: 'a@company.com',
phone: '123-456-7890'
};
// 3. 交叉类型的冲突情况
interface A {
name: string;
age: number;
}
interface B {
name: number; // 与 A 的 name 类型冲突
gender: string;
}
type Conflict = A & B;
// Conflict 的 name 属性类型是 string & number = never
面试考点
问: 联合类型如何进行类型收窄?
答: 使用类型守卫缩小类型范围。
// 方法1: typeof 类型守卫
function func1(value: string | number) {
if (typeof value === 'string') {
// value: string
} else {
// value: number
}
}
// 方法2: instanceof
function func2(value: Date | string) {
if (value instanceof Date) {
// value: Date
} else {
// value: string
}
}
// 方法3: in 操作符
interface A { a: string; }
interface B { b: number; }
function func3(value: A | B) {
if ('a' in value) {
// value: A
} else {
// value: B
}
}
// 方法4: 字面量类型判断
type Status = 'pending' | 'success';
function func4(status: Status) {
if (status === 'pending') {
// status: 'pending'
} else {
// status: 'success'
}
}
3. 泛型
核心概念
泛型(Generics):
- 参数化类型,在定义时不指定具体类型,使用时再确定
- 提供代码复用性和类型安全性
- 使用
<T>语法定义泛型参数
代码示例
// 1. 泛型函数
function identity<T>(arg: T): T {
return arg;
}
let output1 = identity<string>('hello'); // 明确指定 T 为 string
let output2 = identity(123); // 类型推断,T 为 number
// 2. 泛型接口
interface Container<T> {
value: T;
getValue(): T;
}
const stringContainer: Container<string> = {
value: 'hello',
getValue() { return this.value; }
};
// 3. 泛型类
class GenericBox<T> {
constructor(private content: T) {}
getContent(): T {
return this.content;
}
}
const numberBox = new GenericBox<number>(42);
// 4. 实际应用: 合并对象
function merge<T, U>(obj1: T, obj2: U): T & U {
return { ...obj1, ...obj2 };
}
const result = merge({ name: 'John' }, { age: 30 });
// result: { name: string } & { age: number }
// 5. 泛型约束
interface Lengthwise {
length: number;
}
function logLength<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // 可以访问 length 属性
return arg;
}
logLength('hello'); // ✅ 字符串有 length 属性
logLength([1, 2, 3]); // ✅ 数组有 length 属性
// logLength(123); // ❌ 数字没有 length 属性
面试考点
问: 泛型的作用是什么?
答:
- 让函数/类可以处理任意类型的数据
- 保持类型安全,返回值类型与参数类型一致
- 提高代码复用性,避免重复代码
4. 类型守卫
核心概念
类型守卫(Type Guards):
- 在运行时检查变量的类型
- 在代码块内缩小变量的类型范围
- 常用方式:
typeof、instanceof、in操作符、字面量判断
代码示例
// 1. typeof 类型守卫
function processValue(value: string | number) {
if (typeof value === 'string') {
console.log(value.toUpperCase()); // value: string
} else {
console.log(value.toFixed(2)); // value: number
}
}
// 2. instanceof 类型守卫(仅适用于类)
class Dog {
bark() { console.log('汪汪'); }
}
class Cat {
meow() { console.log('喵喵'); }
}
function makeSound(animal: Dog | Cat) {
if (animal instanceof Dog) {
animal.bark(); // animal: Dog
} else {
animal.meow(); // animal: Cat
}
}
// 3. in 操作符类型守卫
interface Admin {
name: string;
permissions: string[];
}
interface User {
name: string;
email: string;
}
function getInfo(user: Admin | User) {
if ('permissions' in user) {
console.log(user.permissions); // user: Admin
} else {
console.log(user.email); // user: User
}
}
// 4. 字面量类型守卫(适用于接口)
interface Bird {
type: 'bird';
fly(): void;
}
interface Fish {
type: 'fish';
swim(): void;
}
function move(animal: Bird | Fish) {
if (animal.type === 'bird') {
animal.fly(); // animal: Bird
} else {
animal.swim(); // animal: Fish
}
}
面试考点
问: 接口为什么不能用 instanceof?
答:
- 接口只存在于 TypeScript 编译阶段,编译成 JavaScript 后会被完全移除
- 运行时不存在接口,无法使用
instanceof检查 - 应该使用字面量判断或
in操作符
5. 类型推断
核心概念
类型推断(Type Inference):
- TypeScript 自动推断变量的类型,无需显式声明
- 根据初始值、上下文、返回值等推断类型
- 提高代码简洁性,但有时需要显式声明增加可读性
代码示例
// 1. 变量初始化推断
let name = 'Alice'; // TypeScript 推断为 string
let age = 30; // TypeScript 推断为 number
let isStudent = true; // TypeScript 推断为 boolean
// name = 123; // ❌ 不能将 number 赋值给 string
// 2. 函数返回值推断
function add(a: number, b: number) {
return a + b; // TypeScript 推断返回值是 number
}
const result = add(1, 2); // result: number
// 3. 数组元素类型推断
const numbers = [1, 2, 3]; // number[]
const mixed = [1, 'a', true]; // (number | string | boolean)[]
// 4. 上下文类型推断
const numbers2 = [1, 2, 3];
numbers2.forEach(num => {
console.log(num.toFixed(2)); // TypeScript 知道 num 是 number
});
// 5. 未初始化的变量
let x; // x: any (未初始化,TypeScript 推断为 any)
// 6. 联合类型的推断
const value = Math.random() > 0.5 ? 'hello' : 123;
// value: string | number (TypeScript 正确推断为联合类型)
面试考点
问: 什么时候需要显式声明类型?
答:
- 复杂类型,提高可读性
- 联合类型,TypeScript 无法推断
- 函数参数,TypeScript 无法推断
- 对象属性,明确接口约束
// 1. 复杂类型
interface User {
id: number;
name: string;
email: string;
}
const user: User = { id: 1, name: 'Alice', email: 'alice@example.com' };
// 2. 联合类型
let result: string | number;
// 3. 函数参数
function greet(name: string) {
console.log(`Hello, ${name}`);
}
6. 函数类型
核心概念
函数类型:
- 定义函数的参数类型和返回值类型
- 提供类型安全的函数调用
- 支持函数重载、可选参数、默认参数等特性
代码示例
// 1. 函数声明
function add(a: number, b: number): number {
return a + b;
}
// 2. 函数表达式
const subtract = (a: number, b: number): number => {
return a - b;
};
// 3. 类型别名定义函数类型
type MathOperation = (a: number, b: number) => number;
const multiply: MathOperation = (a, b) => a * b;
// 4. 接口定义函数类型
interface Calculator {
(a: number, b: number): number;
}
const divide: Calculator = (a, b) => a / b;
// 5. 可选参数
function greet(name: string, greeting?: string): string {
return `${greeting || 'Hello'}, ${name}!`;
}
greet('Alice'); // "Hello, Alice!"
greet('Alice', 'Hi'); // "Hi, Alice!"
// 6. 默认参数
function greet2(name: string, greeting: string = 'Hello'): string {
return `${greeting}, ${name}!`;
}
greet2('Bob'); // "Hello, Bob!"
// 7. 剩余参数
function sum(...numbers: number[]): number {
return numbers.reduce((total, num) => total + num, 0);
}
sum(1, 2, 3, 4); // 10
// 8. 函数重载
function formatValue(value: string): string;
function formatValue(value: number): string;
function formatValue(value: string | number): string {
if (typeof value === 'string') {
return value.toUpperCase();
} else {
return value.toFixed(2);
}
}
面试考点
问: 可选参数的位置规则?
答:
- 可选参数必须在必选参数后面
- 可选参数不能在必选参数前面
// ✅ 正确: 可选参数在后面
function greet(name: string, greeting?: string): string {
return `${greeting || 'Hello'}, ${name}!`;
}
// ❌ 错误: 可选参数在前面
// function greet2(greeting?: string, name: string): string { } // 编译错误!
7. 类类型
核心概念
类类型:
- TypeScript 为 ES6 类添加了类型系统
- 包括属性类型、方法类型、访问修饰符等
- 支持接口实现、泛型类、抽象类等特性
代码示例
// 1. 基本类类型定义
class User {
name: string;
age: number;
private id: number; // 私有属性
protected email: string; // 受保护属性
readonly createdAt: Date; // 只读属性
constructor(name: string, age: number, id: number) {
this.name = name;
this.age = age;
this.id = id;
this.createdAt = new Date();
}
greet(): string {
return `Hello, I'm ${this.name}`;
}
}
// 2. 访问修饰符
class Example {
public a: string; // 任何地方都可以访问
private b: number; // 只能在 Example 类内部访问
protected c: boolean; // 只能在 Example 类及其子类中访问
}
class ChildExample extends Example {
constructor() {
super();
this.a = 'hello'; // ✅ public 可以访问
// this.b = 123; // ❌ private 不能在子类访问
this.c = true; // ✅ protected 可以在子类访问
}
}
// 3. 类实现接口
interface IEntity {
id: number;
save(): void;
}
class Product implements IEntity {
id: number;
constructor(id: number) {
this.id = id;
}
save(): void {
console.log('Saving product...');
}
}
// 4. 泛型类
class Container<T> {
private value: T;
constructor(value: T) {
this.value = value;
}
getValue(): T {
return this.value;
}
setValue(value: T): void {
this.value = value;
}
}
const numberContainer = new Container<number>(42);
const stringContainer = new Container<string>('hello');
// 5. 抽象类
abstract class Animal {
abstract makeSound(): void; // 抽象方法,子类必须实现
move(): void { // 普通方法,子类可以直接使用
console.log('Moving...');
}
}
class Dog extends Animal {
makeSound(): void {
console.log('汪汪');
}
}
// 6. 实战案例: 队列类
class Queue<T> {
private items: T[] = [];
enqueue(item: T): void {
this.items.push(item);
}
dequeue(): T | undefined {
return this.items.shift();
}
peek(): T | undefined {
return this.items[0];
}
size(): number {
return this.items.length;
}
}
const numberQueue = new Queue<number>();
numberQueue.enqueue(1);
numberQueue.enqueue(2);
console.log(numberQueue.dequeue()); // 1
面试考点
问: 三种访问修饰符的区别?
答:
- public: 任何地方都可以访问(默认)
- private: 只能在类内部访问
- protected: 只能在类内部和子类中访问
8. 装饰器
核心概念
装饰器(Decorators):
- 一种特殊类型的声明,可以附加到类、方法、属性、参数上
- 用于修改或增强类和类的成员
- 本质是一个函数,在运行时被调用
- 常用于: 日志记录、性能监控、权限控制等
装饰器类型
// 1. 类装饰器
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class BankAccount {
constructor(public balance: number) {}
}
// 2. 方法装饰器
function log(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`调用方法: ${propertyKey}`);
const result = originalMethod.apply(this, args);
return result;
};
return descriptor;
}
class Calculator {
@log
add(a: number, b: number) {
return a + b;
}
}
// 3. 属性装饰器
function readonly(target: any, propertyKey: string) {
Object.defineProperty(target, propertyKey, {
writable: false
});
}
class Person {
@readonly
name: string = 'Alice';
}
// 4. 参数装饰器
function required(
target: any,
propertyKey: string,
parameterIndex: number
) {
console.log(`参数 ${parameterIndex} 是必需的`);
}
class UserService {
getUser(@required id: number, name: string) {
// ...
}
}
// 5. 装饰器工厂
function configurable(value: boolean) {
return function(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
descriptor.configurable = value;
return descriptor;
};
}
class Point {
@configurable(false)
get x() { return this._x; }
}
实际应用场景
// 1. 日志记录
function logExecution(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const original = descriptor.value;
descriptor.value = function(...args: any[]) {
const start = Date.now();
const result = original.apply(this, args);
const duration = Date.now() - start;
console.log(`${propertyKey} 执行时间: ${duration}ms`);
return result;
};
}
class DataProcessor {
@logExecution
processData(data: any) {
return data;
}
}
// 2. 权限控制
function requireAuth(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const original = descriptor.value;
descriptor.value = function(...args: any[]) {
if (!this.isAuthenticated) {
throw new Error('未授权访问');
}
return original.apply(this, args);
};
}
class AdminPanel {
isAuthenticated = false;
@requireAuth
deleteUser(userId: number) {
console.log(`删除用户 ${userId}`);
}
}
// 3. 缓存
function cache(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const original = descriptor.value;
const cacheMap = new Map();
descriptor.value = function(...args: any[]) {
const key = JSON.stringify(args);
if (cacheMap.has(key)) {
return cacheMap.get(key);
}
const result = original.apply(this, args);
cacheMap.set(key, result);
return result;
};
}
class ApiService {
@cache
fetchData(id: number) {
console.log('发起网络请求');
return { id, data: 'some data' };
}
}
面试考点
问: 装饰器的执行顺序是什么?
答: 装饰器工厂从上到下执行,装饰器本身从下到上执行(洋葱模型)
@first() // ① first工厂执行
@second() // ② second工厂执行
method() {}
// 装饰器执行顺序:
// ③ second装饰器执行
// ④ first装饰器执行
记忆口诀: 工厂从上到下,装饰器从下到上
9. 工具类型
核心概念
工具类型(Utility Types):
- TypeScript 内置的类型转换工具
- 用于快速创建新类型,避免重复定义
- 都是泛型类型,可以灵活组合使用
常用工具类型
// 1. Partial<T> - 将所有属性变为可选
interface User {
id: number;
name: string;
email: string;
}
type PartialUser = Partial<User>;
// { id?: number; name?: string; email?: string; }
// 实际应用: 更新用户信息
function updateUser(id: number, updates: Partial<User>) {
// 只需要传入要更新的字段
}
updateUser(1, { name: 'Alice' }); // ✅ 只更新名字
// 2. Required<T> - 将所有属性变为必需
type RequiredUser = Required<PartialUser>;
// { id: number; name: string; email: string; }
// 3. Readonly<T> - 将所有属性变为只读
type ReadonlyUser = Readonly<User>;
// { readonly id: number; readonly name: string; readonly email: string; }
// 4. Pick<T, K> - 从类型 T 中选取部分属性
type UserPreview = Pick<User, 'id' | 'name'>;
// { id: number; name: string; }
// 5. Omit<T, K> - 从类型 T 中排除部分属性
type UserBasic = Omit<User, 'email'>;
// { id: number; name: string; }
// 6. Record<K, T> - 创建对象类型
type UserRoles = Record<string, 'admin' | 'user' | 'guest'>;
// { [key: string]: 'admin' | 'user' | 'guest'; }
// 7. Extract<T, U> - 提取符合类型的类型
type T0 = Extract<'a' | 'b' | 'c', 'a' | 'f'>; // 'a'
// 8. Exclude<T, U> - 排除符合类型的类型
type T1 = Exclude<'a' | 'b' | 'c', 'a'>; // 'b' | 'c'
// 9. NonNullable<T> - 排除 null 和 undefined
type T2 = NonNullable<string | null | undefined>; // string
// 10. ReturnType<T> - 获取函数返回值类型
function getUser() {
return { id: 1, name: 'Alice' };
}
type User2 = ReturnType<typeof getUser>;
// { id: number; name: string; }
// 11. Parameters<T> - 获取函数参数类型
function createUser(name: string, age: number) {
return { name, age };
}
type CreateUserParams = Parameters<typeof createUser>;
// [string, number]
实战应用案例
interface Article {
id: number;
title: string;
content: string;
author: string;
createdAt: Date;
updatedAt: Date;
}
// 创建文章:用户只需要填写基础信息
type CreateArticle = Omit<Article, 'id' | 'createdAt' | 'updatedAt'>;
// 更新文章:用户可以修改内容,但不能修改 id 和时间
type UpdateArticle = Partial<Omit<Article, 'id' | 'createdAt' | 'updatedAt'>>;
// 列表展示:精简信息
type ArticleListItem = Pick<Article, 'id' | 'title' | 'author' | 'createdAt'>;
// 使用示例
function createArticle(data: CreateArticle): Article {
return {
id: Math.random(),
...data,
createdAt: new Date(),
updatedAt: new Date()
};
}
function updateArticle(id: number, updates: UpdateArticle): void {
// updates: { title?: string; content?: string; author?: string; }
}
面试考点
问: Partial
答:
Partial<T>: 将所有属性变为可选Omit<T, K>: 排除指定属性
interface User {
id: number;
name: string;
}
type PartialUser = Partial<User>; // { id?: number; name?: string; }
type OmitUser = Omit<User, 'id'>; // { name: string; }
10. 条件类型与映射类型
条件类型
条件类型(Conditional Types):
- 根据类型关系决定最终类型
- 使用
extends关键字进行条件判断 - 类似于三元运算符
T extends U ? X : Y
// 1. 基础条件类型
type IsString<T> = T extends string ? 'string' : 'not string';
type A = IsString<string>; // 'string'
type B = IsString<number>; // 'not string'
// 2. 提取函数返回值类型
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
function getString() { return 'hello'; }
type StringReturn = GetReturnType<typeof getString>; // string
// 3. 提取数组元素类型
type ElementType<T> = T extends (infer E)[] ? E : never;
type StringArray = ElementType<string[]>; // string
映射类型
映射类型(Mapped Types):
- 基于旧类型创建新类型
- 使用
in关键字遍历属性 - 可以批量转换属性类型
// 1. 基础映射类型
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
interface User {
name: string;
age: number;
}
type ReadonlyUser = Readonly<User>;
// { readonly name: string; readonly age: number; }
// 2. 可选属性映射
type Optional<T> = {
[P in keyof T]?: T[P];
};
type OptionalUser = Optional<User>;
// { name?: string; age?: number; }
// 3. 过滤属性类型
type ExtractByType<T, U> = {
[P in keyof T as T[P] extends U ? P : never]: T[P];
};
interface Product {
id: number;
name: string;
price: number;
description: string;
}
type StringProperties = ExtractByType<Product, string>;
// { name: string; description: string; }
// 4. 添加前缀
type AddPrefix<T, P extends string> = {
[K in keyof T as `${P}${string & K}`]: T[K];
};
type PrefixedUser = AddPrefix<User, 'user_'>;
// { user_name: string; user_age: number; }
面试考点
问: 条件类型中 infer 关键字的作用?
答: infer 用于在条件类型中推断类型,可以在 extends 子句中定义类型变量
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// infer R 表示推断返回值类型并赋值给 R
11. 工程化与配置
tsconfig.json 核心配置
{
"compilerOptions": {
// 基本选项
"target": "ES2020", // 编译目标版本
"module": "ESNext", // 模块系统
"lib": ["ES2020", "DOM"], // 编译时包含的库
// 严格模式
"strict": true, // 启用所有严格类型检查
"noImplicitAny": true, // 不允许隐式 any
"strictNullChecks": true, // 严格的 null 检查
// 模块解析
"moduleResolution": "node", // 模块解析策略
"baseUrl": "./", // 基础路径
"paths": {
"@/*": ["src/*"] // 路径别名
},
// 输出配置
"outDir": "./dist", // 输出目录
"rootDir": "./src", // 源代码目录
"declaration": true, // 生成 .d.ts 声明文件
"sourceMap": true, // 生成 sourceMap
// 其他选项
"esModuleInterop": true, // 允许 CommonJS 模块导入
"skipLibCheck": true, // 跳过库文件类型检查
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
声明文件
// 1. 全局声明文件 (global.d.ts)
declare const API_URL: string;
declare function log(message: string): void;
// 2. 模块声明文件
declare module 'my-library' {
export function greet(name: string): string;
export class Calculator {
add(a: number, b: number): number;
}
}
// 3. 为第三方库补充类型
declare module 'lodash' {
export function debounce<T extends (...args: any[]) => any>(
func: T,
wait?: number
): T;
}
// 4. 声明合并
interface Window {
myCustomProperty: string;
}
面试考点
问: strict: true 包含哪些严格模式选项?
答: strict: true 等同于同时启用以下所有选项:
{
"noImplicitAny": true, // 不允许隐式 any
"strictNullChecks": true, // 严格的 null 检查
"strictFunctionTypes": true, // 严格的函数类型检查
"strictBindCallApply": true, // 严格的 bind/call/apply 检查
"strictPropertyInitialization": true, // 严格的属性初始化检查
"noImplicitThis": true, // 不允许隐式 this
"alwaysStrict": true, // 总是使用严格模式
"useUnknownInCatchVariables": true // catch 子句变量为 unknown
}
问: 第三方库没有类型定义怎么办?
答:
- 安装官方类型定义包:
npm install --save-dev @types/library-name - 创建自定义声明文件:
src/types/library-name.d.ts - 临时使用 any 断言(不推荐)
总结
基础类型系统 - 面试高频考点
- 接口 vs 类型别名: 接口用于对象形状定义,类型别名更灵活
- 联合类型 vs 交叉类型: 联合类型是"或"的关系,交叉类型是"且"的关系
- 泛型的作用: 提高代码复用性,保持类型安全
- 类型守卫: 在运行时检查类型,缩小类型范围
- 类型推断: TypeScript 自动推断类型,但复杂情况需要显式声明
- 函数类型: 注意可选参数位置规则
- 类类型: 掌握访问修饰符和泛型类的使用
高级特性 - 面试高频考点
- 装饰器: 掌握装饰器类型和执行顺序(工厂从上到下,装饰器从下到上)
- 工具类型: 常用 Partial、Omit、Pick、Record 等工具类型的使用场景
- 条件类型与映射类型: infer 关键字和映射类型的基本用法
- 工程化配置: strict 模式和第三方库类型定义的处理方法
学习建议
- 理解概念: 先理解每个知识点的核心概念
- 多写代码: 通过实际代码加深理解
- 面试题练习: 通过面试题检验掌握程度
- 实际应用: 在项目中使用 TypeScript 加深印象