第 94 章:基本类型系统
学习目标
- 掌握 TS 全部原始类型(number/string/boolean 等)
- 理解数组、元组、any、unknown 的本质区别
- 学会 void / never / unknown 的使用场景
- 理解类型系统自顶向下的层次结构
一、TS 类型全景图
二、原始类型(Primitive Types)
2.1 number
typescript
let decimal: number = 6; // 十进制
let hex: number = 0xf00d; // 十六进制
let binary: number = 0b1010; // 二进制
let octal: number = 0o744; // 八进制
let float: number = 3.14; // 浮点数
let negative: number = -1; // 负数
let infinity: number = Infinity; // 无穷大
let notANumber: number = NaN; // 非数字JS 浮点陷阱:
0.1 + 0.2 !== 0.3(结果 0.30000000000000004),TS 不会帮你修。
2.2 string
typescript
let firstName: string = 'Tom';
let lastName: string = "Smith";
let fullName: string = `${firstName} ${lastName}`; // 模板字符串
let chinese: string = '你好,TypeScript';
let emoji: string = '😀🎉';2.3 boolean
typescript
let isActive: boolean = true;
let hasPermission: boolean = false;
let result: boolean = 1 > 2; // false2.4 null 和 undefined
typescript
let nothing: null = null;
let notDefined: undefined = undefined;
// 严格模式下 ❌
let x: number = null; // ❌ Type 'null' is not assignable to type 'number'
let y: string = undefined; // ❌开启 strictNullChecks 后,默认 null 和 undefined 只能赋值给 null/undefined 类型本身。
typescript
// ✅ 允许 null 的写法
let nickname: string | null = 'Tom';
nickname = null; // OK
// ✅ 允许 undefined 的写法
let middleName: string | undefined = undefined;2.5 symbol 和 bigint
typescript
// symbol:唯一标识符
const KEY: symbol = Symbol('key');
const obj = { [KEY]: 'secret value' };
console.log(obj[KEY]); // 'secret value'
// bigint:超大整数(Number.MAX_SAFE_INTEGER 之外)
const huge: bigint = 9007199254740993n; // 必须以 n 结尾
const another: bigint = BigInt(100);企业级使用频率:symbol / bigint 很少用,了解即可。
三、数组(Array)
3.1 两种写法
typescript
// 写法 1:类型 + []
let numbers: number[] = [1, 2, 3];
let names: string[] = ['Tom', 'Jerry'];
// 写法 2:泛型 Array<T>
let flags: Array<boolean> = [true, false, true];3.2 数组方法的类型安全
typescript
let nums: number[] = [1, 2, 3];
nums.push(4); // ✅
nums.push('5'); // ❌ Argument of type 'string' is not assignable
nums.toUpperCase(); // ❌ Property 'toUpperCase' does not exist on type 'number'
nums.map(n => n * 2); // ✅ 推断 (n: number) => number
nums.filter(n => n > 1); // ✅ 推断 number[]3.3 多维数组
typescript
let matrix: number[][] = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
let cube: number[][][] = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]];3.4 只读数组
typescript
const readonlyNums: readonly number[] = [1, 2, 3];
readonlyNums.push(4); // ❌ Property 'push' does not exist
readonlyNums[0] = 99; // ❌ Index signature in type 'readonly number[]'
readonlyNums.length; // ✅ 读取仍然允许四、元组(Tuple)
元组是固定长度、固定类型的数组。
4.1 为什么需要元组
typescript
// ❌ 用数组:类型不够精确
let user1: (string | number)[] = ['Tom', 18, 'tom@example.com'];
// 访问后不知道是 string 还是 number
// ✅ 用元组:精确表达
let user2: [string, number, string] = ['Tom', 18, 'tom@example.com'];
// user2[0] 一定是 string
// user2[1] 一定是 number4.2 元组使用场景
typescript
// 场景 1:函数返回多个值
function getUserInfo(): [string, number] {
return ['Tom', 18];
}
const [name, age] = getUserInfo();
// 场景 2:key-value 对
const entries: [string, number][] = [
['Java', 1],
['TypeScript', 2],
['Vue', 3]
];
// 场景 3:React useState
// const [count, setCount] = useState<number>(0);4.3 元组的限制
typescript
let point: [number, number] = [1, 2];
point.push(3); // ⚠️ 编译通过,运行时破坏元组约束
point[2]; // ❌ Type 'undefined' cannot be used as index type注意:元组在 TS 层面是"编译期类型",运行时仍是数组。需要
readonly tuple或外部库(如immer)防止 push。
4.4 可选元素与剩余元素
typescript
// 可选元素
let optional: [string, number?] = ['Tom']; // 第二个元素可选
// 剩余元素
let rest: [string, ...number[]] = ['Tom', 1, 2, 3, 4];五、特殊类型
5.1 any:放弃类型检查
typescript
let x: any = 'Tom';
x = 123; // ✅ 任何类型都可以
x.foo.bar.baz; // ✅ 编译不报错(运行就崩)为什么设计 any? 类型迁移期、第三方库无类型、JSON 解析等场景的"逃生舱"。
陷阱:滥用 any 等于写 JS,丧失 TS 价值。
5.2 unknown:类型安全的 any
typescript
let value: unknown = 'Tom';
// ✅ 可以赋值给任何类型
let s1: string = value; // ❌ Type 'unknown' is not assignable to type 'string'
let s2: any = value; // ✅ unknown 可以赋给 any
// ✅ 但必须先"收窄"才能用
if (typeof value === 'string') {
console.log(value.toUpperCase()); // ✅ 这里 value 被收窄为 string
}最佳实践:能用 unknown 就不用 any。
5.3 void:没有返回值
typescript
function log(msg: string): void {
console.log(msg);
// 没有 return,或者 return undefined
}
let result: void = log('hello'); // ✅ undefined 是 void 的子类型使用场景:函数无返回值。
5.4 never:永远不会发生
typescript
// 抛出异常的函数
function throwError(message: string): never {
throw new Error(message);
}
// 无限循环
function infiniteLoop(): never {
while (true) {}
}
// 穷尽性检查
type Status = 'success' | 'error';
function handle(s: Status) {
switch (s) {
case 'success': return 'ok';
case 'error': return 'failed';
default:
const _exhaustive: never = s; // 如果漏掉 case,编译报错
return _exhaustive;
}
}never 是所有类型的子类型,可以赋值给任何类型。
5.5 any / unknown / never 对比
| 类型 | 含义 | 何时用 |
|---|---|---|
any | 任何类型 | 迁移期、JSON 解析、第三方库无类型 |
unknown | 类型未知的"安全"版本 | 解析外部数据(API、JSON) |
never | 永远不会发生 | 抛错、穷尽性检查 |
六、类型断言
typescript
// 方式 1:尖括号(不推荐,与 JSX 冲突)
const value = someValue as string;
// 方式 2:as 语法(推荐)
const value = <string>someValue;合法使用场景:
typescript
// 1. 收窄到具体类型
function process(x: string | number) {
if (typeof x === 'string') {
console.log((x as string).toUpperCase()); // 不必要,TS 已经收窄
}
}
// 2. 解析 JSON
const data = JSON.parse(rawText) as User;
// 3. DOM 操作
const input = document.querySelector('#myInput') as HTMLInputElement;
input.value = 'Tom';警告:滥用类型断言 = 欺骗编译器 = 失去类型保护。
七、对象类型(Object)
7.1 基础定义
typescript
let user: { name: string; age: number } = {
name: 'Tom',
age: 18
};
// 可选属性
let config: { host: string; port?: number } = {
host: 'localhost'
// port 可选
};7.2 索引签名
typescript
// 任意字符串 key,value 是 string
let dict: { [key: string]: string } = {
name: 'Tom',
email: 'tom@example.com'
};
// 数字索引(数组的另一种写法)
let scores: { [index: number]: string } = ['A', 'B', 'C'];⚠️ 索引签名的陷阱:所有匹配的属性必须兼容。
八、类型边界与对象字面量
typescript
// 对象字面量有"额外属性检查"
function greet(user: { name: string }) {
console.log(user.name);
}
greet({ name: 'Tom', age: 18 }); // ❌ Object literal may only specify known properties解决方式:
typescript
// 1. 改成变量
const u = { name: 'Tom', age: 18 };
greet(u); // ✅
// 2. 用类型断言
greet({ name: 'Tom', age: 18 } as { name: string });
// 3. 放宽函数参数
function greet(user: { name: string; [key: string]: any }) { ... }九、本章小结
| 类型 | 例子 | 备注 |
|---|---|---|
| 原始类型 | number, string, boolean | 与 JS 基本一致 |
| 数组 | number[] / Array<number> | 类型一致 |
| 元组 | [string, number] | 固定类型和长度 |
| any | any | 逃生舱,慎用 |
| unknown | unknown | 安全的 any |
| void | void | 函数无返回值 |
| never | never | 永远不会发生 |
动手练习
- 类型体操 A:定义一个变量,可以是
string或null,但是不能是undefined - 类型体操 B:写一个函数返回
[number, string, boolean]元组 - 类型体操 C:写一个
process函数,参数是unknown,分别处理string/number/ 其他情况
推荐阅读
- 📖 TypeScript Handbook - Basic Types — 官方基础类型文档
- 📖 TypeScript Deep Dive - Type System — 类型系统深入
- 🌐 TypeScript Playground — 试一下各种类型
下一章:第 95 章:联合类型与字面量类型 →