一份结构化、重点突出的 TypeScript 学习笔记
一、类型系统基础
1.1 类型声明 vs 类型推断
类型声明:显式指定变量类型
typescript
let a: string
let b: number
let c: boolean
function demo(x: number, y: number): number {
return x + y
}
类型推断:TypeScript 自动推断类型
typescript
let d = -99 // TS 推断为 number 类型
d = false // ❌ 警告:不能将类型 "boolean" 分配给类型 "number"
核心要点:
- 类型声明更加明确,适合复杂类型和公共 API
- 类型推断简化代码,适合局部变量和简单场景
- 推荐优先使用类型推断,必要时显式声明
二、类型总览
2.1 TypeScript 新增类型
| 类型 | 含义 | 使用场景 |
|---|---|---|
any |
任意类型,放弃类型检查 | 临时处理、迁移 JS 代码 |
unknown |
类型安全的 any | 接收外部数据、类型不确定时 |
never |
永不存在的值 | 函数永不返回、穷尽检查 |
void |
空或 undefined | 函数无返回值 |
tuple |
固定长度数组 | 坐标点、键值对 |
enum |
枚举 | 固定选项集合 |
2.2 JavaScript 类型 vs TypeScript 类型
JS 原始类型 :string、number、boolean、null、undefined、bigint、symbol、object
TS 新增能力:
- 所有 JS 类型
- 四个新类型:
void、never、unknown、any、enum、tuple - 自定义类型:
type、interface
三、常用类型详解
3.1 字面量类型
定义:值只能是特定的字面量
typescript
let a: '你好' // a 的值只能是字符串 "你好"
let b: 100 // b 的值只能是数字 100
// 联合字面量类型
let gender: '男' | '女' // 值只能为 "男" 或 "女"
应用场景:限制变量为特定值集合,提高类型安全性
3.2 any 类型
特点:放弃类型检查,变量可以赋任何值
typescript
let a: any
a = 100
a = '你好'
a = false
// 以上均无警告
// ⚠️ 危险:any 类型可以赋值给任意类型变量
let x: string
x = a // 无警告,但运行时可能出错
使用建议:
- ❌ 避免滥用 any
- ✅ 仅用于临时处理、迁移旧代码
- ✅ 优先使用 unknown 替代
3.3 unknown 类型(⭐ 重点)
特点:类型安全的 any,必须类型缩小或断言后才能使用
typescript
let a: unknown
a = 100
a = 'hello'
let x: string
x = a // ❌ 警告:不能将类型 "unknown" 分配给类型 "string"
三种安全使用方式:
typescript
// 方式1:类型判断(推荐)
if (typeof a === 'string') {
x = a // ✅ 安全
}
// 方式2:类型断言 as
x = a as string
// 方式3:尖括号断言
x = <string>a
关键区别:
typescript
let str1: string = 'hello'
str1.toUpperCase() // ✅ 无警告
let str2: any = 'hello'
str2.toUpperCase() // ✅ 无警告(但不安全)
let str3: unknown = 'hello'
str3.toUpperCase() // ❌ 警告:需要类型缩小或断言
(str3 as string).toUpperCase() // ✅ 正确使用
3.4 never 类型
含义 :永不存在的值,连 undefined、null、''、0 都不行
使用场景1:函数永不返回
typescript
function demo(): never {
throw new Error('程序异常退出')
}
使用场景2:类型收窄的穷尽检查
typescript
let a: string = 'hello'
if (typeof a === 'string') {
a.toUpperCase()
} else {
console.log(a) // TS 推断此处 a 是 never,因为不可能执行
}
使用场景3:switch 穷尽检查
typescript
enum Color { Red, Blue, Green }
function getColorName(c: Color): string {
switch (c) {
case Color.Red: return '红色'
case Color.Blue: return '蓝色'
case Color.Green: return '绿色'
default:
const exhaustiveCheck: never = c // 如果新增颜色忘记处理,会报错
return exhaustiveCheck
}
}
3.5 void 类型
含义:空或 undefined,常用于函数返回值
typescript
function demo1(): void { } // ✅ 无返回
function demo2(): void { return } // ✅ 返回 undefined
function demo3(): void { return undefined } // ✅ 显式返回 undefined
function demo4(): void { return 666 } // ❌ 不能返回 number
3.6 object 与 Object
object:任何非原始值类型(对象、函数、数组等)
typescript
let a: object
a = {} // ✅
a = { name: '张三' } // ✅
a = [1, 3, 5] // ✅
a = function(){} // ✅
a = null // ❌
a = 1 // ❌
Object:Object 的实例对象,范围太大,几乎不用
typescript
let a: Object
a = 1 // ✅ 包装对象是 Object 实例
a = true // ✅ 包装对象是 Object 实例
a = '你好' // ✅ 包装对象是 Object 实例
实际开发推荐写法:
typescript
// 限制具体对象
let person: { name: string, age?: number }
// 允许可选属性和任意额外属性
let car: { price: number; color: string; [k: string]: any }
// 限制函数类型
let demo: (a: number, b: number) => number
// 限制数组类型
let arr1: string[] // 等价于 Array<string>
let arr2: number[] // 等价于 Array<number>
3.7 tuple(元组)
定义:固定长度、固定类型的数组
typescript
let t: [string, number]
t = ['hello', 123] // ✅
t = ['hello', 123, false] // ❌ 长度不匹配
应用场景:
- 坐标点:
[number, number] - 键值对:
[string, any] - 函数返回多个值
3.8 enum(枚举)
定义:一组命名的常量集合
typescript
enum Color {
Red, // 默认 0
Blue, // 默认 1
Black, // 默认 2
Gold // 默认 3
}
enum Color2 {
Red = 6, // 指定初始值
Blue, // 7(自动递增)
Black, // 8
Gold // 9
}
枚举编译结果:
javascript
// Color 编译后:
{
0: 'Red',
1: 'Blue',
2: 'Black',
3: 'Gold',
Red: 0,
Blue: 1,
Black: 2,
Gold: 3
}
实际应用:
typescript
let phone: { name: string, price: number, color: Color }
phone = {
name: '华为Mate60',
price: 6500,
color: Color.Red
}
if (phone.color === Color.Red) {
console.log('手机是红色的')
}
四、自定义类型
4.1 type(类型别名)
typescript
enum Gender { Male, Female }
type Grade = 1 | 2 | 3 // 字面量联合类型
type Student = {
name: string
age: number
gender: Gender
grade: Grade
}
let s1: Student = {
name: '张三',
age: 18,
gender: Gender.Male,
grade: 1
}
五、抽象类
5.1 基本概念
抽象类特点:
- 不能实例化(不能 new)
- 可以被继承
- 可以包含抽象方法和普通方法
typescript
abstract class Person {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
// 抽象方法:子类必须实现
abstract speak(): void
// 普通方法:子类可直接继承
walk() {
console.log('我在行走中....')
}
}
class Teacher extends Person {
constructor(name: string, age: number) {
super(name, age)
}
speak() {
console.log(`我是老师,我的名字是${this.name}`)
}
}
// const p1 = new Person('周杰伦', 38) // ❌ 抽象类不能实例化
const t1 = new Teacher('刘老师', 40) // ✅
六、接口(Interface)
6.1 接口基础
定义:限制类、对象的结构
typescript
interface Person {
name: string
age: number
speak(): void
}
class Teacher implements Person {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
speak() {
console.log(`你好!我是老师:`, this.name)
}
}
6.2 接口可重复声明
typescript
interface PersonInter {
name: string
age: number
}
interface PersonInter {
speak(): void
}
// 等价于合并:
// interface PersonInter {
// name: string
// age: number
// speak(): void
// }
6.3 接口 vs 类型别名
| 特性 | 接口 | 类型别名 |
|---|---|---|
| 用途 | 定义对象结构、类契约 | 定义任意类型 |
| 可扩展 | ✅ 可重复声明合并 | ❌ 不能重复声明 |
| 实现类 | ✅ implements |
❌ 不能实现 |
| 联合类型 | ❌ 不支持 | ✅ 支持 |
typescript
// 接口可以当类型使用
let person: Person = {
name: '张三',
age: 18,
speak() {
console.log('你好!')
}
}
// 类型别名只能当类型
type Grade = 1 | 2 | 3 // ✅
// interface Grade = 1 | 2 | 3 // ❌ 不支持
6.4 接口 vs 抽象类
| 特性 | 接口 | 抽象类 |
|---|---|---|
| 方法类型 | 只能有抽象方法 | 可有抽象方法和普通方法 |
| 实现方式 | implements |
extends |
| 多继承 | ✅ 可实现多个接口 | ❌ 单继承 |
| 默认实现 | ❌ 无 | ✅ 可有默认实现 |
七、属性修饰符
| 修饰符 | 含义 | 可访问范围 |
|---|---|---|
readonly |
只读属性 | 任何地方都不能修改 |
public |
公开 | 类、子类、对象均可访问 |
protected |
受保护 | 类、子类可访问 |
private |
私有 | 仅类内部可访问 |
typescript
class Person {
public name: string
protected age: number
private id: string
readonly gender: '男' | '女'
constructor(name: string, age: number, id: string) {
this.name = name
this.age = age
this.id = id
this.gender = '男'
}
}
const p = new Person('张三', 18, '123')
p.name = '李四' // ✅ public 可修改
p.age = 20 // ❌ protected 仅类和子类可访问
p.id = '456' // ❌ private 仅类内部可访问
p.gender = '女' // ❌ readonly 不可修改
八、泛型(Generics)
8.1 泛型基础
定义:定义函数或类时,类型不确定,使用类型变量表示
typescript
function test<T>(arg: T): T {
return arg
}
// 不指定类型,TS 自动推断
test(10) // T 推断为 number
// 指定具体类型
test<number>(10)
8.2 多个泛型
typescript
function test<T, K>(a: T, b: K): K {
return b
}
test<number, string>(10, 'hello')
8.3 泛型类
typescript
class MyClass<T> {
prop: T
constructor(prop: T) {
this.prop = prop
}
}
const instance = new MyClass<string>('hello')
8.4 泛型约束
限制泛型的范围
typescript
interface Demo {
length: number
}
// T 必须有 length 属性
function test<T extends Demo>(arg: T): number {
return arg.length
}
test(10) // ❌ number 无 length
test({ name: '张三' }) // ❌ 无 length 属性
test('123') // ✅ string 有 length
test({ name: '张三', length: 10 }) // ✅
九、核心概念对比总结
9.1 any vs unknown
| 特性 | any | unknown |
|---|---|---|
| 类型安全 | ❌ 不安全 | ✅ 类型安全 |
| 赋值给其他类型 | ✅ 任意类型 | ❌ 需要类型缩小 |
| 调用方法 | ✅ 可调用 | ❌ 需断言或类型缩小 |
| 推荐使用 | ⚠️ 尽量避免 | ✅ 推荐 |
9.2 type vs interface
优先使用 interface 的场景:
- 定义对象结构
- 类需要实现的契约
- 需要声明合并
优先使用 type 的场景:
- 联合类型、交叉类型
- 元组、映射类型
- 基本类型别名
十、学习要点与最佳实践
10.1 类型设计原则
-
优先使用具体类型,避免 any
typescript// ❌ 不好 function process(data: any) { } // ✅ 更好 function process(data: unknown) { if (typeof data === 'string') { } } // ✅ 最好 function process(data: string | number) { } -
善用类型推断,减少冗余声明
typescript// ❌ 冗余 let a: number = 10 const arr: number[] = [1, 2, 3] // ✅ 简洁 let a = 10 const arr = [1, 2, 3] -
利用联合类型和字面量类型提高类型精度
typescripttype Direction = 'up' | 'down' | 'left' | 'right' type Status = 'pending' | 'success' | 'error'
10.2 常见陷阱
-
混淆 Object 和 object
Object:几乎所有类型(包装对象)object:非原始类型
-
误用 any 导致类型污染
typescriptlet a: any = 'hello' let b: string = a // ❌ 类型污染,a 可能不是 string -
忽略枚举的双向映射
typescriptenum Color { Red = 0 } Color.Red // 0 Color[0] // 'Red'(反向映射)
十一、快速复习清单
类型系统:
- 类型声明 vs 类型推断
- JS 类型 + TS 新增类型
- 字面量类型的使用
关键类型:
- any(放弃检查) vs unknown(类型安全)
- never(永不返回) vs void(无返回值)
- tuple(固定数组) vs enum(枚举)
高级特性:
- type(类型别名) vs interface(接口)
- 抽象类(可实例化?) vs 接口(只能抽象方法?)
- 属性修饰符:public、protected、private、readonly
- 泛型:类型变量、泛型约束
设计原则:
- 优先具体类型,避免 any
- 善用类型推断
- 联合类型 + 字面量类型提高精度