摘要:类型是 TypeScript 的核心。本文从最基础的类型(number、string、boolean)讲起,逐步深入到数组、元组、枚举、any、unknown、void、null/undefined、never 等类型。你还会学到类型注解、类型推导、联合类型、字面量类型等实用概念。
一、前言
在上一篇中,我们已经搭建好了 TypeScript 环境,并写了一个简单的 sayHello 函数,初步体验了类型检查的魅力。
今天,我们将系统学习 TypeScript 的所有基础类型。掌握了这些,你就能给绝大多数的 JavaScript 代码加上准确的类型注解,让编辑器为你保驾护航。
二、类型注解(Type Annotation)与类型推导(Type Inference)
2.1 类型注解
类型注解就是手动 给变量、参数、返回值指定类型。语法是在标识符后面加上 : 类型。
TypeScript
// 变量注解
let age: number = 25;
let name: string = "小明";
let isStudent: boolean = true;
// 参数和返回值注解
function multiply(x: number, y: number): number {
return x * y;
}
如果不小心赋错类型,TypeScript 会立刻报错:
TypeScript
age = "二十五"; // ❌ 类型"string"不能赋值给类型"number"
2.2 类型推导
TypeScript 非常智能,如果你没有写类型注解,它会根据初始值自动推导出类型。
TypeScript
let message = "Hello"; // 推导为 string 类型
message = 42; // ❌ 不能将 number 赋给 string
这种自动推导极大减少了我们写类型的工作量。推荐的实践是:简单变量让 TS 推导,函数参数和返回值显式注解。
三、原始类型:number、string、boolean
这三种类型对应 JavaScript 的原始值。
3.1 number
包括整数、浮点数、负数、NaN、Infinity 等。
TypeScript
let intNum: number = 42;
let floatNum: number = 3.14;
let negative: number = -10;
let notANumber: number = NaN;
let infinity: number = Infinity;
3.2 string
可以使用单引号、双引号或模板字符串。
TypeScript
let single: string = 'Hello';
let double: string = "World";
let template: string = `Hello, ${single} ${double}!`;
3.3 boolean
只有 true 和 false。
TypeScript
let isDone: boolean = false;
let isGreater: boolean = 10 > 5; // true
小贴士 :在 TypeScript 中,number、string、boolean 都是小写,不要写成大写 Number、String、Boolean(它们是 JavaScript 的包装对象类型,几乎用不到)。
四、数组与元组
4.1 数组
有两种写法:
TypeScript
// 方式一:类型后加 []
let list1: number[] = [1, 2, 3];
// 方式二:泛型 Array<类型>
let list2: Array<string> = ["a", "b", "c"];
如果数组中既有数字又有字符串,可以用联合类型(后面会讲)或者 any[](不推荐)。
4.2 元组(Tuple)
元组是固定长度、每个位置类型已知的数组。这是 JavaScript 没有的概念。
TypeScript
let person: [string, number] = ["张三", 25];
// 第一个必须是 string,第二个必须是 number
// 可以单独访问
let name = person[0]; // string 类型
let age = person[1]; // number 类型
// 越界访问会报错
person[2] = "extra"; // ❌ 长度为 2 的元组不能添加元素
元组常用于表示一对值,比如键值对、坐标、函数返回值等。
五、枚举(enum):让代码更语义化
枚举用来定义一组命名常量 。在 JavaScript 中没有原生枚举,TS 提供了 enum 关键字。
5.1 数字枚举
TypeScript
enum Direction {
Up, // 默认值 0
Down, // 1
Left, // 2
Right // 3
}
let move: Direction = Direction.Up;
console.log(move); // 输出 0
console.log(Direction[0]); // 输出 "Up"(反向映射)
你也可以手动赋值:
TypeScript
enum StatusCode {
OK = 200,
NotFound = 404,
InternalError = 500
}
5.2 字符串枚举
TypeScript
enum Color {
Red = "RED",
Green = "GREEN",
Blue = "BLUE"
}
字符串枚举不能反向映射,但可读性更好。
5.3 枚举的使用场景
比如表示一周的天数、订单状态、用户角色等。
TypeScript
enum UserRole {
Admin = "admin",
Editor = "editor",
Viewer = "viewer"
}
function checkPermission(role: UserRole) {
if (role === UserRole.Admin) {
console.log("拥有所有权限");
}
}
注意:枚举编译后会生成一个 JavaScript 对象,会稍微增加代码体积。对于简单场景,也可以使用联合类型替代(后面会提到)。
六、any、unknown、void、null / undefined、never
这五种类型是 TS 中的特殊类型,各有用途。
6.1 any ------ 关闭类型检查
当你暂时不知道类型,或者想迁就旧 JS 代码时,可以用 any。一旦使用了 any,TS 就不再对该变量做任何检查。
TypeScript
let notSure: any = 4;
notSure = "字符串"; // 可以
notSure = true; // 可以
notSure.toFixed(); // 运行时可能出错,但 TS 不会阻止
缺点 :any 会破坏 TS 的保护,建议尽量少用。如果要表示"类型未知",更推荐使用 unknown。
6.2 unknown ------ 安全的 any
unknown 表示"我还不确定是什么类型",你不能直接使用它的属性或方法,必须先收窄类型。
TypeScript
let value: unknown = "Hello";
// ❌ 报错:value 可能是其他类型,不能直接调用字符串方法
// console.log(value.toUpperCase());
// 正确做法:类型收窄
if (typeof value === "string") {
console.log(value.toUpperCase()); // 现在安全了
}
unknown 比 any 更安全,推荐用于处理动态内容(如 API 响应)。
6.3 void ------ 没有返回值
用于函数没有返回值时。实际上,void 表示函数返回 undefined 或 null。
TypeScript
function logMessage(msg: string): void {
console.log(msg);
// 没有 return,或者 return; / return undefined;
}
变量也可以声明为 void 类型,但只能赋 undefined 或 null(如果 strictNullChecks 关闭),实际很少这样用。
6.4 null 和 undefined
在 TypeScript 中,null 和 undefined 既是值,也是类型。
TypeScript
let u: undefined = undefined;
let n: null = null;
默认情况下,null 和 undefined 可以赋值给任何其他类型(例如 let num: number = undefined),这很容易引发问题。所以强烈建议 在 tsconfig.json 中开启 "strictNullChecks": true,这样它们就只能赋值给自身或 void。
TypeScript
// tsconfig.json
{
"compilerOptions": {
"strictNullChecks": true
}
}
开启后:
TypeScript
let age: number = undefined; // ❌ 不能将 undefined 赋给 number
6.5 never ------ 永远不会发生的类型
never 表示函数永远不会正常返回(比如抛出异常或无限循环),或者一个永远不可能有值的变量。
TypeScript
// 抛出错误的函数,返回值类型是 never
function throwError(message: string): never {
throw new Error(message);
}
// 无限循环
function infiniteLoop(): never {
while (true) {}
}
never 是所有类型的子类型,可以赋值给任何类型;但没有类型是 never 的子类型(除了 never 自身)。
七、联合类型(Union Types)与类型收窄
7.1 联合类型
一个变量可能拥有多种类型之一,用竖线 | 分隔。
TypeScript
let id: string | number;
id = "abc123"; // 合法
id = 10086; // 合法
id = true; // ❌ 不能是 boolean
函数参数也常用联合类型:
TypeScript
function formatValue(value: string | number): string {
// 类型收窄前不能直接调用字符串或数字特有方法
if (typeof value === "string") {
return value.toUpperCase();
} else {
return value.toFixed(2);
}
}
7.2 类型收窄(Type Narrowing)
当使用联合类型时,TS 需要你通过某些方式缩窄具体类型,才能安全地操作。
常用方法:
-
typeof类型守卫 -
Array.isArray() -
in操作符 -
自定义类型守卫(后续文章会讲)
TypeScript
function printLength(input: string | string[]) {
if (typeof input === "string") {
console.log(input.length); // ✅ 此时 input 为 string
} else {
console.log(input.length); // ✅ 此时 input 为 string[]
}
}
八、字面量类型
字面量类型是指一个具体的值 作为类型。例如 "hello" 可以作为类型,这意味着这个变量只能赋值为 "hello"。
TypeScript
let greeting: "hello" = "hello";
greeting = "world"; // ❌ 不能将 "world" 赋给类型 "hello"
字面量类型通常与联合类型结合,模拟枚举效果:
TypeScript
type Direction = "up" | "down" | "left" | "right";
let move: Direction = "up"; // 合法
move = "north"; // ❌ 不在联合中
这种"字符串字面量联合类型"比 enum 更轻量,编译后不产生额外代码,在 TS 社区很流行。
还可以有数字字面量类型和布尔字面量类型:
TypeScript
let dice: 1 | 2 | 3 | 4 | 5 | 6 = 4;
let isTrue: true = true;
九、类型别名(type)
当同一个联合类型或复杂类型在多处使用时,可以用 type 给它起个名字。
TypeScript
// 定义别名
type UserID = string | number;
type Status = "pending" | "success" | "error";
function handleUser(id: UserID, status: Status) {
// ...
}
类型别名还可以用于对象类型(类似接口,后面会详细讲):
TypeScript
type Point = {
x: number;
y: number;
};
let p: Point = { x: 10, y: 20 };
提示 :
type和interface有很多重叠,但各有侧重。简单的对象和联合类型用type,需要继承或实现的用interface。
十、总结
TypeScript 的类型系统是结构化的(鸭子类型),只要结构匹配即可,不需要显式声明继承关系。
开启 strictNullChecks 能避免大量 null/undefined 引发的错误。
优先使用类型推导,但函数参数和返回值建议显式注解。
any 是逃生舱,尽量用 unknown 代替。
如果这篇文章帮你解决了实操上的困惑,别忘记点击点赞、分享 ,也可以留言告诉我你遇到的其它问题,我会尽快回复。动手练习是掌握编程最快的方法,请务必亲手敲一遍本文的所有示例代码,并截图保存你的成果。你的关注是我坚持原创和细节共享的力量来源,谢谢大家。