Skip to content
第 94 / 250 章前端⏱ 10 分钟阅读

第 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;  // false

2.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,默认 nullundefined 只能赋值给 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] 一定是 number

4.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]固定类型和长度
anyany逃生舱,慎用
unknownunknown安全的 any
voidvoid函数无返回值
nevernever永远不会发生

动手练习

  1. 类型体操 A:定义一个变量,可以是 stringnull,但是不能是 undefined
  2. 类型体操 B:写一个函数返回 [number, string, boolean] 元组
  3. 类型体操 C:写一个 process 函数,参数是 unknown,分别处理 string / number / 其他情况

推荐阅读


下一章第 95 章:联合类型与字面量类型

本站基于 VitePress 构建 · 由 Codebook 团队维护