TypeScript总结
简介
TypeScript 是 JavaScript 的超集(Superset),由微软开发并开源。它在 JavaScript 的基础上添加了静态类型系统和基于类的面向对象编程特性。
只是在编译阶段帮你找出类型错误,编译后输出的仍然是纯 JS。
优势:
| 特性 | 说明 |
|---|---|
| 静态类型检查 | 在编译时发现类型错误,减少运行时 Bug |
| 更好的IDE支持 | 智能提示、代码补全、重构支持 |
| 增强的可维护性 | 大型项目更易理解和维护 |
| 现代JS特性 | 支持 ES6+ 语法,并编译为兼容的 JavaScript |
| 丰富的生态系统 | 几乎所有流行库都有类型定义, Vue / React / Node.js 主流框架全面支持 |
文件扩展名
| 扩展名 | 用途 |
|---|---|
.ts |
标准的 TypeScript 文件(最常用) |
.tsx |
支持 JSX 语法的 TypeScript 文件(用于 React 项目) |
.d.ts |
类型声明文件(只包含类型定义,无具体实现) |
.mts / .cts |
分别用于 ESM 和 CommonJS 模块的 TypeScript 文件(TS 4.7+) |
经过 TypeScript 编译器(tsc)编译后,会生成对应的 JavaScript 文件
语法
基础类型
// 原始类型
let isDone: boolean = false;
let count: number = 42;
let userName: string = "Alice";
// 数组
let numbers: number[] = [1, 2, 3];
let names: Array<string> = ["Tom", "Jerry"]; // 泛型写法
// 元组 - 固定长度和类型
let person: [string, number] = ["Alice", 25];
// 枚举
enum Color { Red, Green, Blue }
let c: Color = Color.Green;
// Any - 任意类型(慎用)any 会关闭类型检查,等于写回 JS。如果确实不知道类型,用 unknown,它要求你在使用前做类型检查。
let notSure: any = 4;
notSure = "maybe a string";
// Unknown - 类型安全的 any
let unknownValue: unknown = 4;
// 使用前需要类型检查或断言
// Void - 无返回值
function warnUser(): void {
console.log("This is a warning");
}
// Never - 永不返回(如抛出异常或无限循环)
function throwError(message: string): never {
throw new Error(message);
}
对象类型
// 内联对象类型
let user: {
name: string
age: number
email?: string // ? 表示可选属性
readonly id: number // readonly 表示只读
} = {
name: '张三',
age: 25,
id: 1001
}
user.email = 'a@b.com' // ✅ 可选属性可以后赋值
user.id = 2002 // ❌ 只读属性不能修改
接口(Interface)
定义对象的结构,是 TypeScript 最核心的特性之一:
interface User {
id: number
name: string
age: number
email?: string // 可选
readonly createdAt: Date // 只读
}
// 使用
const user: User = {
id: 1,
name: '张三',
age: 25,
createdAt: new Date()
}
// 缺少必填属性会报错
const bad: User = { id: 2, name: '李四' } // ❌ 缺少 age 和 createdAt
interface 继承
interface Animal {
name: string
speak(): void
}
interface Dog extends Animal {
breed: string
fetch(item: string): void
}
const myDog: Dog = {
name: '旺财',
breed: '柴犬',
speak() { console.log('汪汪') },
fetch(item) { console.log(`捡回 ${item}`) }
}
type — 类型别名
// type 能定义任何类型,不只是对象
type ID = string | number
type Status = 'pending' | 'done' | 'failed'
type Callback = (data: string) => void
// 对象类型(和 interface 类似)
type Point = {
x: number
y: number
}
// 交叉类型实现"继承"效果
type Point3D = Point & { z: number }
// 从已有类型映射出新类型
type ReadOnlyUser = Readonly<User>
type PartialUser = Partial<User> // 所有属性变可选
interface vs type 怎么选
| 特性 | interface | type |
|---|---|---|
| 扩展方式 | extends 继承 |
& 交叉 |
| 重复声明 | 自动合并(声明合并) | 不允许重复 |
| 能定义的范围 | 仅对象形状 | 任何类型(联合、元组、函数等) |
| 适用场景 | 定义类的形状、库的 API | 联合类型、工具类型、复杂类型运算 |
简单规则:定义对象结构用 interface,其他一切用 type。
联合类型与字面量类型
// 联合类型 — 多种类型选其一
let id: string | number = 'abc'
id = 123 // ✅
// 字面量类型 — 限定为固定值
let direction: 'up' | 'down' | 'left' | 'right' = 'up'
direction = 'diagonal' // ❌
// 组合使用
let status: 'loading' | 'success' | 'error' = 'loading'
类(Class)
class Person {
// 访问修饰符:public(默认)| private | protected
public name: string
private _age: number // 仅类内部可访问
protected id: number // 类内部 + 子类可访问
// 构造函数参数可以直接加修饰符(简写)
constructor(name: string, private _age: number, protected id: number) {
this.name = name
}
// getter / setter
get age(): number { return this._age }
set age(val: number) {
if (val >= 0) this._age = val
}
greet(): string {
return `我是 ${this.name},${this._age} 岁`
}
}
const p = new Person('张三', 25, 1)
p.name // ✅ public
p.age // ✅ 通过 getter
p._age // ❌ private,外部不可访问
p.id // ❌ protected,外部不可访问
继承与抽象类
// 抽象类 — 不能直接实例化,必须被继承
abstract class Shape {
abstract getArea(): number // 子类必须实现
describe(): string {
return `面积为 ${this.getArea().toFixed(2)}`
}
}
class Circle extends Shape {
constructor(public radius: number) { super() }
getArea(): number {
return Math.PI * this.radius ** 2
}
}
class Rectangle extends Shape {
constructor(public w: number, public h: number) { super() }
getArea(): number {
return this.w * this.h
}
}
const shapes: Shape[] = [new Circle(5), new Rectangle(3, 4)]
shapes.forEach(s => console.log(s.describe()))
枚举
// 数字枚举(默认从 0 开始)
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right // 3
}
const d: Direction = Direction.Up
// 字符串枚举(推荐,可读性更好)
enum Status {
Pending = 'pending',
Success = 'success',
Failed = 'failed'
}
function handle(status: Status) {
if (status === Status.Success) { /* ... */ }
}
// 实际开发中,更推荐用联合类型替代枚举(更轻量)
type Status2 = 'pending' | 'success' | 'failed'
枚举 vs 联合类型:枚举会生成额外的 JS 代码,而联合类型是零成本的类型级约束。如果没有反向映射的需求,优先用联合类型。
基本函数类型
// 普通函数
function greet(name: string): string {
return `你好,${name}`
}
// 箭头函数
const add = (a: number, b: number): number => a + b
// 无返回值
function log(msg: string): void {
console.log(msg)
}
// 永远不会正常返回
function fail(msg: string): never {
throw new Error(msg)
}
可选参数与默认值
// 可选参数用 ? ,必须放在最后
function createUser(name: string, age?: number): void {
console.log(name, age) // age 可能是 undefined
}
// 默认值参数(不需要 ?)
function request(url: string, method: string = 'GET'): void {
// method 不传时默认 'GET'
}
// 剩余参数
function sum(...nums: number[]): number {
return nums.reduce((a, b) => a + b, 0)
}
函数类型表达式
// 用 type 定义函数签名
type Fn = (x: number, y: number) => number
const multiply: Fn = (a, b) => a * b
// 回调函数类型
function fetchData(url: string, onSuccess: (data: any) => void): void {
// ...
}
fetchData('/api', (data) => {
console.log(data) // data 自动推断为 any
})
函数重载
function parse(input: string): number // 重载签名 1
function parse(input: number): string // 重载签名 2
function parse(input: string | number): string | number { // 实现签名
if (typeof input === 'string') return Number(input)
return String(input)
}
parse('123') // 返回类型推断为 number
parse(456) // 返回类型推断为 string
类型收窄与守卫
为什么需要类型收窄
联合类型的变量不能直接调用某个类型特有的方法,必须先"收窄"到具体类型:
function process(val: string | number) {
console.log(val.toUpperCase()) // ❌ number 没有 toUpperCase
}
收窄方式
// 1. typeof
function process(val: string | number) {
if (typeof val === 'string') {
console.log(val.toUpperCase()) // ✅ 此处 val 收窄为 string
} else {
console.log(val.toFixed(2)) // ✅ 此处 val 收窄为 number
}
}
// 2. 真值检查(排除 null / undefined / "" / 0 / false)
function len(s: string | null) {
if (s) {
console.log(s.length) // ✅ 此处 s 收窄为 string
}
}
// 3. in 操作符
type Fish = { swim: () => void }
type Bird = { fly: () => void }
function move(pet: Fish | Bird) {
if ('swim' in pet) {
pet.swim() // ✅ 收窄为 Fish
} else {
pet.fly() // ✅ 收窄为 Bird
}
}
// 4. instanceof
function format(val: Date | string) {
if (val instanceof Date) {
console.log(val.toISOString()) // ✅ 收窄为 Date
}
}
// 5. 自定义类型守卫(is 关键字)
function isFish(pet: Fish | Bird): pet is Fish {
return ('swim' in pet)
}
const pet: Fish | Bird = { swim: () void 0 }
if (isFish(pet)) {
pet.swim() // ✅ 自定义守卫让 TS 自动收窄
}
泛型(Generics)
泛型就是类型的变量——你先不确定具体类型,用占位符代替,等到使用时再确定。就像函数的参数一样,只不过参数是类型。
编写可复用的、类型安全的代码:
// 不用泛型 — 要为每种类型写一遍
function identityStr(x: string): string { return x }
function identityNum(x: number): number { return x }
// 用泛型 — 一个函数搞定所有类型
function identity<T>(x: T): T {
return x
}
identity('hello') // T 推断为 string,返回 string
identity(42) // T 推断为 number,返回 number
identity<boolean>(true) // 手动指定 T 为 boolean
// 泛型接口
interface ApiResponse<T> {
code: number
message: string
data: T
}
// 使用时传入具体类型
type UserResponse = ApiResponse<{ id: number; name: string }>
type ListResponse = ApiResponse<string[]>
// 实际数据
const res: UserResponse = {
code: 200,
message: 'ok',
data: { id: 1, name: '张三' }
}
res.data.name // ✅ 自动补全
res.data.age // ❌ 报错:{ id: number; name: string } 上不存在 age
泛型约束
有时候泛型不能是任意类型,需要加上约束:
// 约束 T 必须有 length 属性
interface HasLength { length: number }
function logLength<T extends HasLength>(val: T): void {
console.log(val.length)
}
logLength('hello') // ✅ string 有 length
logLength([1, 2]) // ✅ 数组有 length
logLength(123) // ❌ number 没有 length
// 约束 T 必须是某个类的子类型
function getById<T extends { id: number }>(items: T[], id: number): T | undefined {
return items.find(item => item.id === id)
}
常用内置工具类型
| 工具类型 | 作用 | 示例 |
|---|---|---|
Partial<T> |
所有属性变可选 | Partial<User> |
Required<T> |
所有属性变必填 | Required<User> |
Readonly<T> |
所有属性变只读 | Readonly<User> |
Pick<T, K> |
只保留部分属性 | Pick<User, 'id' | 'name'> |
Omit<T, K> |
排除部分属性 | Omit<User, 'password'> |
Record<K, V> |
构造键值对类型 | Record<string, number> |
Array<T> |
数组类型 | Array<string> |
interface User { id: number; name: string; email: string; age: number }
type UserUpdate = Partial<User>
// { id?: number; name?: string; email?: string; age?: number }
type UserPreview = Pick<User, 'id' | 'name'>
// { id: number; name: string }
type UserSafe = Omit<User, 'email'>
// { id: number; name: string; age: number }
type RoleMap = Record<string, string[]>
// { [key: string]: string[] }
const roles: RoleMap = {
admin: ['read', 'write', 'delete'],
user: ['read']
}
高级类型
// 联合类型(Union Types)
function padLeft(value: string, padding: string | number) {
if (typeof padding === "number") {
return Array(padding + 1).join(" ") + value;
}
return padding + value;
}
// 类型断言
let someValue: unknown = "this is a string";
let strLength: number = (someValue as string).length;
// 或 let strLength: number = (<string>someValue).length;
// 类型守卫
function isString(test: any): test is string {
return typeof test === "string";
}
// 映射类型
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Partial<T> = {
[P in keyof T]?: T[P];
};
// 实用工具类型
interface Todo {
title: string;
description: string;
}
type TodoPreview = Pick<Todo, "title">; // 选取属性
type TodoPartial = Partial<Todo>; // 所有属性可选
type TodoReadonly = Readonly<Todo>; // 所有属性只读
type TodoRequired = Required<Todo>; // 所有属性必选
模块与命名空间
// 导出
export interface StringValidator {
isValid(s: string): boolean;
}
export const numberRegexp = /^[0-9]+$/;
export default class ZipCodeValidator implements StringValidator {
isValid(s: string): boolean {
return s.length === 5 && numberRegexp.test(s);
}
}
// 导入
import { StringValidator } from "./StringValidator";
import ZipCodeValidator from "./ZipCodeValidator";
import * as validator from "./ZipCodeValidator"; // 命名空间导入
装饰器(Decorators)
// 类装饰器
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
}
// 方法装饰器
function log(target: any, key: string, descriptor: PropertyDescriptor) {
const original = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`Calling ${key} with`, args);
return original.apply(this, args);
};
}

浙公网安备 33010602011771号