TypeScript 基础
大约 10 分钟约 2925 字
TypeScript 基础
简介
TypeScript 是 JavaScript 的超集,添加了静态类型系统。它编译为纯 JavaScript 运行,提供编译时类型检查、智能提示和代码重构能力。是现代前端开发的标配语言,广泛用于 Vue、React、Angular 项目。
特点
基本类型
原始类型与类型标注
// 基本类型
let username: string = "张三";
let age: number = 28;
let isActive: boolean = true;
let empty: null = null;
let notDefined: undefined = undefined;
// 类型推断 — 不需要显式标注
let message = "Hello"; // TypeScript 推断为 string
let count = 10; // 推断为 number
// 数组
let numbers: number[] = [1, 2, 3, 4, 5];
let names: Array<string> = ["张三", "李四"];
// 元组
let user: [number, string] = [1, "张三"];
// 枚举
enum OrderStatus {
Pending = 0,
Processing = 1,
Shipped = 2,
Completed = 3,
Cancelled = 4
}
let status: OrderStatus = OrderStatus.Processing;
// any — 逃逸类型检查(尽量避免)
let data: any = "任意值";
data = 42;
// unknown — 安全的 any
let input: unknown = "hello";
if (typeof input === "string") {
console.log(input.toUpperCase()); // OK
}
// void — 无返回值
function log(message: string): void {
console.log(message);
}
// never — 永远不会有返回值
function throwError(msg: string): never {
throw new Error(msg);
}接口与类型别名
Interface
// 定义对象形状
interface User {
id: number;
name: string;
email: string;
age?: number; // 可选属性
readonly createdAt: Date; // 只读属性
}
// 使用
const user: User = {
id: 1,
name: "张三",
email: "zhangsan@test.com",
createdAt: new Date()
};
// 函数类型
interface SearchFunc {
(keyword: string, page: number): SearchResult[];
}
// 继承
interface Employee extends User {
department: string;
salary: number;
}
// 索引签名
interface Dictionary<T> {
[key: string]: T;
}Type Alias
// 类型别名
type ID = number | string;
type Point = { x: number; y: number };
type Callback = (data: any) => void;
// 联合类型
type Status = "active" | "inactive" | "banned";
type Result<T> = {
success: boolean;
data?: T;
error?: string;
};
// 交叉类型
type UserWithRole = User & { role: string; permissions: string[] };
// 类型守卫
function isUser(value: User | string): value is User {
return typeof value === "object" && "id" in value;
}泛型
泛型函数和类
// 泛型函数
function identity<T>(value: T): T {
return value;
}
identity<string>("hello");
identity(42); // 自动推断
// 泛型约束
interface HasId {
id: number;
}
function findById<T extends HasId>(items: T[], id: number): T | undefined {
return items.find(item => item.id === id);
}
// 泛型 API 响应
interface ApiResponse<T> {
code: number;
message: string;
data: T;
timestamp: number;
}
// 使用
const userResponse: ApiResponse<User> = {
code: 0,
message: "成功",
data: { id: 1, name: "张三", email: "zhangsan@test.com", createdAt: new Date() },
timestamp: Date.now()
};
// 泛型类
class Repository<T extends HasId> {
private items: T[] = [];
add(item: T): void {
this.items.push(item);
}
getById(id: number): T | undefined {
return this.items.find(item => item.id === id);
}
getAll(): T[] {
return [...this.items];
}
}
const userRepo = new Repository<User>();
userRepo.add({ id: 1, name: "张三", email: "z@test.com", createdAt: new Date() });实用工具类型
高级工具类型
// Exclude — 从联合类型中排除
type Status = 'active' | 'inactive' | 'pending' | 'deleted'
type ActiveStatus = Exclude<Status, 'deleted' | 'pending'> // 'active' | 'inactive'
// Extract — 从联合类型中提取
type StringMethods = 'toUpperCase' | 'toLowerCase' | 'trim' | 'split'
type NoParamMethods = Extract<StringMethods, 'toUpperCase' | 'toLowerCase'>
// NonNullable — 排除 null 和 undefined
type UserData = { name: string | null; age: number | undefined }
type NonNullableUser = NonNullable<UserData['name']> // string
// Readonly — 所有属性变只读
type FrozenConfig = Readonly<{ apiUrl: string; timeout: number }>
// Parameters — 获取函数参数类型
function createUser(name: string, email: string, role: 'admin' | 'user'): void {}
type CreateUserParams = Parameters<typeof createUser>
// [name: string, email: string, role: 'admin' | 'user']
// ConstructorParameters — 获取构造函数参数类型
class User {
constructor(name: string, age: number) {}
}
type UserConstructorParams = ConstructorParameters<typeof User>
// [name: string, age: number]条件类型与映射类型
// 条件类型
type IsString<T> = T extends string ? 'yes' : 'no'
type A = IsString<string> // 'yes'
type B = IsString<number> // 'no'
// infer 关键字 — 类型推断
type ReturnTypeOf<T> = T extends (...args: any[]) => infer R ? R : never
type UnpackPromise<T> = T extends Promise<infer U> ? U : T
type FnReturn = ReturnTypeOf<() => boolean> // boolean
type PromiseValue = UnpackPromise<Promise<string>> // string
// 提取数组元素类型
type ArrayElement<T> = T extends (infer E)[] ? E : never
type Numbers = ArrayElement<number[]> // number
// 映射类型 — 批量转换属性
type Readonly<T> = {
readonly [K in keyof T]: T[K]
}
type Optional<T, K extends keyof T> = {
[P in keyof T]?: P extends K ? T[P] : T[P]
}
// 将所有属性变为可空
type Nullable<T> = {
[K in keyof T]: T[K] | null
}
// 深度 Partial
type DeepPartial<T> = {
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K]
}
// 实际应用 — API 请求/响应类型
interface User {
id: number
name: string
email: string
profile: {
avatar: string
bio: string
}
}
// 创建用户请求 — 不需要 id
type CreateUserDTO = Omit<User, 'id'>
// 更新用户请求 — 所有字段可选
type UpdateUserDTO = DeepPartial<Omit<User, 'id'>>
// 用户列表响应
type UserListResponse = {
items: User[]
total: number
page: number
}类型守卫与断言
// 自定义类型守卫
interface Dog {
type: 'dog'
bark(): void
}
interface Cat {
type: 'cat'
meow(): void
}
type Pet = Dog | Cat
// 类型谓词 — value is Dog
function isDog(pet: Pet): pet is Dog {
return pet.type === 'dog'
}
function handlePet(pet: Pet) {
if (isDog(pet)) {
pet.bark() // TypeScript 知道这里是 Dog
} else {
pet.meow() // TypeScript 知道这里是 Cat
}
}
// typeof 守卫
function processValue(value: string | number) {
if (typeof value === 'string') {
console.log(value.toUpperCase()) // string
} else {
console.log(value.toFixed(2)) // number
}
}
// in 操作符守卫
interface Admin {
permissions: string[]
}
interface Guest {
visitCount: number
}
function checkAccess(user: Admin | Guest) {
if ('permissions' in user) {
console.log(user.permissions) // Admin
} else {
console.log(user.visitCount) // Guest
}
}
// 类型断言 — 确定类型时使用(谨慎使用)
const input = document.getElementById('myInput') as HTMLInputElement
input.value = 'Hello'
// 非空断言
const name = user!.name // 确信 user 不是 null/undefined装饰器与类类型
// 类属性与方法装饰器
function readonly(target: any, key: string, descriptor: PropertyDescriptor) {
descriptor.writable = false
return descriptor
}
function log(target: any, key: string, descriptor: PropertyDescriptor) {
const original = descriptor.value
descriptor.value = function (...args: any[]) {
console.log(`调用 ${key},参数:`, args)
const result = original.apply(this, args)
console.log(`返回值:`, result)
return result
}
return descriptor
}
class Calculator {
@readonly
PI = 3.14159
@log
add(a: number, b: number): number {
return a + b
}
}
// 抽象类与接口实现
interface Serializable {
toJSON(): string
fromJSON(json: string): void
}
abstract class BaseEntity {
constructor(public id: number) {}
abstract validate(): boolean
toString(): string {
return `${this.constructor.name}#${this.id}`
}
}
class UserEntity extends BaseEntity implements Serializable {
constructor(
id: number,
public name: string,
public email: string
) {
super(id)
}
validate(): boolean {
return !!(this.name && this.email.includes('@'))
}
toJSON(): string {
return JSON.stringify({ id: this.id, name: this.name, email: this.email })
}
fromJSON(json: string): void {
const data = JSON.parse(json)
this.name = data.name
this.email = data.email
}
}模块与命名空间
// types/api.d.ts — 全局类型声明
declare namespace API {
interface Response<T = any> {
code: number
message: string
data: T
}
interface PaginationParams {
page: number
pageSize: number
keyword?: string
}
interface PaginationResult<T> {
items: T[]
total: number
page: number
pageSize: number
}
}
// 使用全局类型
async function fetchUsers(params: API.PaginationParams): Promise<API.Response<API.PaginationResult<User>>> {
const response = await fetch(`/api/users?page=${params.page}&pageSize=${params.pageSize}`)
return response.json()
}
// 模块声明 — 为第三方库添加类型
declare module 'some-untyped-library' {
export function init(options: { apiKey: string }): void
export function track(event: string, data?: Record<string, any>): void
}
// 扩展已有模块的类型
declare module 'vue' {
interface ComponentCustomProperties {
$loading: boolean
$notify: (message: string) => void
}
}TypeScript 配置
// tsconfig.json — 推荐配置
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": false,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"skipLibCheck": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.d.ts"],
"exclude": ["node_modules", "dist"]
}常见类型陷阱
// 1. 对象引用 vs 值
const a = { name: 'hello' } as const // { readonly name: 'hello' }
const b: { name: string } = { name: 'hello' }
// 2. 数组的 any vs unknown
const arr: any[] = [1, 'hello', {}] // 可以赋任何值
const safeArr: unknown[] = [1, 'hello'] // 更安全
// 3. 枚举的反向映射
enum Status { Active = 'ACTIVE', Inactive = 'INACTIVE' }
const s = Status.Active // 'ACTIVE'
const key = Status['ACTIVE'] // 'Active'(数字枚举才有反向映射)
// 4. keyof typeof — 获取对象的键类型
const config = {
apiUrl: 'https://api.example.com',
timeout: 5000,
retries: 3,
}
type ConfigKey = keyof typeof config // 'apiUrl' | 'timeout' | 'retries'
function getConfig<K extends keyof typeof config>(key: K): typeof config[K] {
return config[key]
}
// 5. 满足(satisfies)操作符 — TypeScript 4.9+
const record = {
id: 1,
name: '张三',
email: 'zhangsan@test.com',
} satisfies User
// 类型检查通过,但保留了字面量类型推断
// 6. 模板字面量类型
type EventName = 'click' | 'focus' | 'blur'
type EventHandler = `on${Capitalize<EventName>}`
// 'onClick' | 'onFocus' | 'onBlur'Utility Types
// Partial — 所有属性变可选
type PartialUser = Partial<User>;
// Required — 所有属性变必填
type RequiredUser = Required<User>;
// Pick — 选取部分属性
type UserPreview = Pick<User, "id" | "name">;
// Omit — 排除部分属性
type UserWithoutEmail = Omit<User, "email">;
// Record — 键值对
type UserMap = Record<string, User>;
// ReturnType — 获取函数返回类型
function createUser() { return { id: 1, name: "张三" }; }
type CreatedUser = ReturnType<typeof createUser>;
// 实际应用
interface CreateUserRequest {
name: string;
email: string;
}
type UpdateUserRequest = Partial<CreateUserRequest>;
// API 函数
async function getUsers(): Promise<ApiResponse<User[]>> {
const response = await fetch("/api/users");
return response.json();
}
async function createUser(data: CreateUserRequest): Promise<ApiResponse<User>> {
const response = await fetch("/api/users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data)
});
return response.json();
}枚举与字面量类型
// 1. 数字枚举
enum Direction {
Up = 0,
Down = 1,
Left = 2,
Right = 3
}
// 2. 字符串枚举(推荐)
enum UserRole {
Admin = "ADMIN",
Editor = "EDITOR",
Viewer = "VIEWER"
}
// 3. 常量枚举(编译时内联)
const enum HttpStatus {
OK = 200,
NotFound = 404,
ServerError = 500
}
// 4. 字面量联合类型(替代枚举)
type Method = "GET" | "POST" | "PUT" | "DELETE";
type Theme = "light" | "dark" | "system";
function request(url: string, method: Method) {
// TypeScript 会检查 method 的值
}
request("/api/users", "GET"); // OK
request("/api/users", "PATCH"); // 编译错误!优点
缺点
总结
TypeScript 是现代前端开发的标准语言。掌握基本类型、接口、泛型和工具类型即可覆盖大部分开发场景。核心原则:能用类型推断就不手写标注,用接口定义 API 契约,用泛型复用代码,用工具类型简化类型定义。
关键知识点
- 先判断主题更偏浏览器原理、框架机制、工程化还是性能优化。
- 前端问题很多看似是页面问题,实际源头在构建、缓存、状态流或接口协作。
- 真正成熟的前端方案一定同时考虑首屏、交互、可维护性和线上诊断。
- 前端主题最好同时看浏览器原理、框架机制和工程化约束。
项目落地视角
- 把组件边界、状态归属、网络层规范和错误处理先定下来。
- 上线前检查包体积、缓存命中、接口失败路径和关键交互降级策略。
- 如果主题和性能有关,最好用 DevTools、Lighthouse 或埋点验证。
- 对关键页面先建立状态流和数据流,再考虑组件拆分。
常见误区
- 只盯框架 API,不理解浏览器和运行时成本。
- 把状态、请求和 UI 更新混成一层,后期难维护。
- 线上问题出现时没有日志、埋点和性能基线可对照。
- 只追框架新特性,不分析实际渲染成本。
进阶路线
- 继续补齐 SSR、边缘渲染、设计系统和监控告警能力。
- 把主题和后端接口约定、CI/CD、缓存策略一起思考。
- 沉淀组件规范、页面模板和性能基线,减少团队差异。
- 继续补齐设计系统、SSR/边缘渲染、监控告警和组件库治理。
适用场景
- 当你准备把《TypeScript 基础》真正落到项目里时,最适合先在一个独立模块或最小样例里验证关键路径。
- 适合中后台应用、门户站点、组件库和实时交互页面。
- 当需求涉及状态流、路由、网络缓存、SSR/CSR 或性能治理时,这类主题很关键。
落地建议
- 先定义组件边界和状态归属,再落地 UI 细节。
- 对核心页面做首屏、体积、缓存和错误路径检查。
- 把安全、兼容性和可访问性纳入默认交付标准。
排错清单
- 先用浏览器 DevTools 看请求、性能面板和控制台错误。
- 检查依赖版本、构建配置、环境变量和静态资源路径。
- 如果是线上问题,优先确认缓存、CDN 和构建产物是否一致。
复盘问题
- 如果把《TypeScript 基础》放进你的当前项目,最先要验证的输入、输出和失败路径分别是什么?
- 《TypeScript 基础》最容易在什么规模、什么边界条件下暴露问题?你会用什么指标或日志去确认?
- 相比默认实现或替代方案,采用《TypeScript 基础》最大的收益和代价分别是什么?
