类型系统基础
🎯 引言
学会这篇文章,你将能掌握 TypeScript 的核心类型系统基础,能够用类型来描述数据结构,写出更安全、更可维护的代码。了解基本类型、数组和元组、特殊类型及类型注解等,对于前端开发中使用 TypeScript 提升代码质量大有帮助。
🟢 基本类型:string、number、boolean、null、undefined
TypeScript 里最基础的类型就是 JavaScript 中常见的几种类型。它们都是用来描述变量存储的数据的“形状”。
const myName: string = '小明';
const age: number = 25;
const isStudent: boolean = true;
const emptyValue: null = null;
const notAssigned: undefined = undefined;
- string 用来表示文本,比如名字、地址等。
- number 表示数字,既可以是整数,也可以是浮点数。
- boolean 是布尔值,只有两个状态:true 或 false。
- null 和 undefined 是两个特殊的值,null 表示“空”,undefined 表示“未定义”。
如何理解 null 和 undefined 呢?可以把 null 想象成“空盒子”,里面没有东西,而 undefined 是“盒子还没放东西”。
🟠 数组与元组
数组是存储一组相同类型元素的集合。元组是固定长度、每个元素类型可以不同的数组。
// 数组:存储一组数字
const scores: number[] = [98, 87, 92];
// 也可以用泛型写法
const names: Array<string> = ['小红', '小明', '小刚'];
// 元组:长度固定,每个位置类型不同
const user: [string, number] = ['小明', 25];
数组很直观,就是一列同类型数据。元组更适合描述“记录”,比如用户信息第一项是名字字符串,第二项是年龄数字。
🔵 any、unknown、void、never
这几个类型是 TypeScript 里比较特殊的类型。
let anything: any = 123;
anything = '变成字符串了';
anything = true; // any 允许赋值任何类型
let notSure: unknown = 10;
notSure = 'hello'; // unknown 也可以赋任何值,但不能随意操作
function sayHello(): void {
console.log('Hello');
}
function throwError(): never {
throw new Error('出错了!');
}
- any 表示“任何类型”,关闭了类型检查,有点像放弃类型安全。建议少用。
- unknown 表示“未知类型”,相比 any 更安全,使用前需要做类型检查。
- void 通常用作函数没有返回值时的返回类型。
- never 表示永远不会有返回值的类型,常用于抛出异常或死循环。
生活中的比喻是,any 就像是一张白纸,你可以画任何东西;unknown 是一张盖着的纸,你得先掀开看清楚才能操作。
⚙️ 函数参数与返回值类型
在前端开发中,函数是核心组成部分。给函数参数和返回值标注类型,可以让代码更严谨,减少运行时错误。
function add(x: number, y: number): number {
return x + y;
}
这个例子中,函数 add 的参数 x 和 y 都是 number 类型,返回值也是 number。当你调用时传入非数字类型,编译器会报错。
🟣 类型注解 vs 类型推断
TypeScript 的强大之处之一是它会自动推断变量的类型,但你也可以手动写类型注解。
// 类型注解:明确说明类型
const count: number = 10;
// 类型推断:没有写注解,TS 自动推断类型
const message = '你好,TS'; // 自动推断为 string
// 函数参数类型注解
function sum(a: number, b: number): number {
return a + b;
}
- 类型注解是显式告诉编译器变量是什么类型。
- 类型推断是编译器通过变量的初始值自动判断类型。
举个例子,TypeScript 就像你的贴心助理,帮你猜测变量是什么类型,但当猜不准时,你要告诉它准确信息。
🟤 类型别名(type)与接口(interface)基础介绍
什么是类型别名(Type Alias)?
类型别名使用 type 关键字来为一个类型起一个名字。可以是基本类型、联合类型、元组、对象等。
type UserID = string;
type User = {
id: UserID;
name: string;
age?: number;
};
什么是接口(Interface)?
接口是 TypeScript 提供的一种用于定义对象结构的方式,特别适合面向对象编程中的接口定义。使用 interface 关键字。
interface User {
id: string;
name: string;
age?: number;
}
接口也支持继承和合并,非常适合扩展型的结构设计。
场景 1:定义对象结构 interface 更合适
interface User {
id: number;
name: string;
age?: number;
}
为什么用 interface:
- 更直观地表达“这是一个对象结构”;
- 支持继承、合并;
- 更适合被他人扩展,尤其是在写库或声明文件时。
场景 2:定义联合类型必须用 type
type Status = 'loading' | 'success' | 'error';
为什么用 type:
interface不能用于联合类型;type能描述字符串字面量的组合、可选值等。
场景 3:定义元组必须用 type
type Point = [number, number];
为什么用 type:
- 只有
type可以定义元组[x, y]; interface不能直接表达这种有序固定长度的数组结构。
场景 5:需要扩展或合并多个结构必须用 interface
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
或者声明合并:
interface User {
id: number;
}
interface User {
name: string;
}
// 合并后等同于:{ id: number; name: string }
type 不能这么做(重复声明会报错)。
什么是枚举(enum)?
枚举(enum)是一种特殊的类型,用来定义一组命名的常量。它能帮你把一堆相关的值统一管理,代码也更清晰。枚举主要分为三种类型。
数字枚举(Numeric Enums)
数字枚举是最常见的形式,枚举成员会自动从 0 开始编号,也可以手动指定初始值,后面的成员依次递增。
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right, // 3
}
console.log(Direction.Left); // 2
字符串枚举(String Enums)
字符串枚举的值必须显式赋值,因为它不会自动递增。字符串枚举更适合那些你希望值和名字保持一一对应,且能直接阅读的场景。
enum Role {
Admin = 'ADMIN',
User = 'USER',
Guest = 'GUEST',
}
异构枚举(Heterogeneous Enums,数字和字符串混合)
异构枚举同时包含数字和字符串类型的成员,一般不推荐使用,因为会降低代码的统一性和可读性。
enum Result {
Success = 1,
Fail = 'FAIL',
}
🧾 小节总结
- TypeScript 基本类型包括 string、number、boolean、null、undefined,都是描述变量基本数据形态。
- 数组用来存储同类型集合,元组是长度固定、类型不同的数组。
- 特殊类型 any 表示任意类型但不安全,unknown 更安全,void 代表无返回值,never 代表永远不返回。
- 类型注解显式声明类型,类型推断自动推断,合理搭配提升代码安全易读。
- 类型别名和接口都是给复杂类型起名字,但接口支持继承和实现,更适合面向对象设计。
❓ 知识问答(Q&A)
Q:TypeScript 中,any 和 unknown 有什么区别?
A:any 关闭类型检查,可以随意操作;unknown 更安全,使用前必须先做类型检查。
Q:什么情况下适合用元组而不是数组?
A:当数据长度固定且每个位置类型不同,比如用户信息时,适合用元组。
Q:接口和类型别名的主要区别是什么?
A:接口支持继承和实现,适合面向对象设计;类型别名更灵活,能定义联合类型等。
🧪 小练习
练习 1:写一个类型别名,表示一个商品对象,包含名字(字符串)、价格(数字)、是否有库存(布尔)。
type Product = {
// 在这里写你的代码
};
const phone: Product = {
// 在这里初始化 phone 对象
};
练习 2:写一个函数,接受一个元组类型参数,表示人的名字和年龄,函数返回字符串介绍该人。
function introduce(person: [string, number]): string {
// 在这里写你的代码
}
console.log(introduce(['小明', 25])); // 输出示例:我是小明,今年25岁。
🎉 恭喜你已经掌握 TypeScript 的基本类型、数组与元组、特殊类型、类型注解与推断,以及类型别名和接口基础技能啦!继续练习,提升你的前端开发能力吧!
