TypeScript 的核心价值在于类型系统。本章学习所有基础类型。
1.1 类型注解语法
TypeScript 使用 : 类型 的语法为变量添加类型注解:
let 变量名: 类型 = 值;
1.2 原始类型
JavaScript 中的原始类型在 TypeScript 中都有对应:
// 字符串
let name: string = "张三";
// 数字(整数和浮点数都是 number)
let age: number = 25;
let price: number = 9.99;
// 布尔值
let isDone: boolean = true;
// null 和 undefined
let n: null = null;
let u: undefined = undefined;
// bigint(ES2020)
let big: bigint = 100n;
// symbol
let sym: symbol = Symbol("id");
⚠️ 注意:
string不等于String。小写的string是 TS 类型,大写的String是 JS 构造函数。始终使用小写。
1.3 数组
两种写法,效果完全一样:
// 写法一:类型[](推荐)
let numbers: number[] = [1, 2, 3];
let names: string[] = ["a", "b", "c"];
// 写法二:Array<类型>(泛型写法)
let numbers2: Array<number> = [1, 2, 3];
数组中的元素必须是指定的类型:
let list: number[] = [1, 2, 3];
list.push(4); // ✅ 正确
list.push("hello"); // ❌ 错误:string 不能赋值给 number
1.4 元组(Tuple)
元组是固定长度、固定类型的数组:
// 定义一个元组:第一个是 string,第二个是 number
let person: [string, number] = ["张三", 25];
// 访问
console.log(person[0]); // "张三"
console.log(person[1]); // 25
// 错误示例
let wrong: [string, number] = [25, "张三"]; // ❌ 类型顺序不对
let wrong2: [string, number] = ["张三"]; // ❌ 缺少元素
可选元素的元组
let tuple: [string, number?] = ["hello"]; // ✅ 第二个元素可选
常见用途:函数返回多个值
function useState(initial: number): [number, (n: number) => void] {
let value = initial;
const setValue = (n: number) => { value = n; };
return [value, setValue];
}
const [count, setCount] = useState(0);
1.5 枚举(Enum)
枚举用于定义一组命名常量:
数字枚举
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right, // 3
}
let dir: Direction = Direction.Up;
console.log(dir); // 0
// 也可以指定起始值
enum Status {
Active = 1,
Inactive, // 2(自动递增)
Deleted, // 3
}
字符串枚举(推荐)
enum Color {
Red = "RED",
Green = "GREEN",
Blue = "BLUE",
}
let c: Color = Color.Red;
console.log(c); // "RED"
💡 字符串枚举更直观,调试时更容易阅读。推荐在实际项目中使用字符串枚举。
const 枚举(编译优化)
const enum Fruit {
Apple = "APPLE",
Banana = "BANANA",
}
let f = Fruit.Apple; // 编译后直接变成 "APPLE",没有额外对象
1.6 特殊类型
any —— 任意类型(逃生舱)
let value: any = 42;
value = "hello"; // ✅
value = true; // ✅
value.foo.bar; // ✅ 编译不报错,但运行时可能崩溃!
⚠️ 尽量避免使用
any,它相当于放弃了类型检查。
unknown —— 安全的 any
let value: unknown = 42;
value = "hello"; // ✅ 可以赋任何值
// 但不能直接使用,必须先缩小类型
// value.toUpperCase(); // ❌ 错误
// 正确做法:先检查类型
if (typeof value === "string") {
console.log(value.toUpperCase()); // ✅
}
💡 需要表示"不确定的类型"时,优先用
unknown而非any。
void —— 没有返回值
function log(msg: string): void {
console.log(msg);
// 没有 return,或者 return undefined
}
never —— 永远不会有值
// 函数永远不会正常结束
function throwError(msg: string): never {
throw new Error(msg);
}
// 无限循环
function infinite(): never {
while (true) {}
}
never 的另一个重要用途 —— 穷尽检查:
type Shape = "circle" | "square" | "triangle";
function getArea(shape: Shape) {
switch (shape) {
case "circle":
return Math.PI * 10 * 10;
case "square":
return 10 * 10;
case "triangle":
return (10 * 5) / 2;
default:
// 如果漏掉了某个 case,这里会报编译错误
const exhaustiveCheck: never = shape;
return exhaustiveCheck;
}
}
1.7 类型推断
TypeScript 能自动推断类型,不需要处处写注解:
let name = "张三"; // 推断为 string
let age = 25; // 推断为 number
let isDone = true; // 推断为 boolean
let list = [1, 2, 3]; // 推断为 number[]
// 以下写法是多余的(类型可以被推断出来)
let name: string = "张三"; // 不需要显式写 :string
什么时候需要手动写类型注解?
// 1. 变量声明时没有赋值
let name: string;
name = "张三";
// 2. 函数参数(必须写)
function greet(name: string) {
console.log(`Hello, ${name}`);
}
// 3. 函数返回值比较复杂时(建议写)
function parse(json: string): { name: string; age: number } {
return JSON.parse(json);
}
1.8 类型断言
当你比 TypeScript 更了解某个值的类型时,使用类型断言:
// 写法一:as 语法(推荐)
let input = document.getElementById("myInput") as HTMLInputElement;
input.value = "hello";
// 写法二:尖括号语法(在 JSX 中不能用)
let input2 = <HTMLInputElement>document.getElementById("myInput");
非空断言 !
function getLength(str: string | undefined) {
// 你确定 str 一定有值时
return str!.length;
}
⚠️ 断言只是告诉编译器"相信我",不做运行时检查。用错了会导致运行时错误。
1.9 字面量类型
变量的值本身也可以作为类型:
// 字符串字面量类型
let direction: "up" | "down" | "left" | "right" = "up";
direction = "down"; // ✅
direction = "north"; // ❌ 错误
// 数字字面量类型
let dice: 1 | 2 | 3 | 4 | 5 | 6 = 3;
// 布尔字面量
let yes: true = true;
const 和字面量类型的关系
let str1 = "hello"; // 类型是 string
const str2 = "hello"; // 类型是 "hello"(字面量类型)
// 因为 const 不可变,所以 TS 推断为更精确的字面量类型
📝 练习
- 声明一个变量
score,类型为number,赋值为 100 - 声明一个元组
user,包含用户名 (string) 和年龄 (number) - 定义一个枚举
HttpStatus,包含OK = 200,NotFound = 404,ServerError = 500 - 写一个函数
divide(a: number, b: number),返回两数相除的结果,b 为 0 时抛出错误 - 用
unknown类型接收一个值,通过类型缩小后调用对应方法
// 参考答案
// 1
let score: number = 100;
// 2
let user: [string, number] = ["张三", 25];
// 3
enum HttpStatus {
OK = 200,
NotFound = 404,
ServerError = 500,
}
// 4
function divide(a: number, b: number): number {
if (b === 0) throw new Error("除数不能为 0");
return a / b;
}
// 5
function processValue(val: unknown) {
if (typeof val === "string") {
console.log(val.toUpperCase());
} else if (typeof val === "number") {
console.log(val.toFixed(2));
}
}