TypeScript 从零基础到精通(二):基础类型与类型系统

摘要:类型是 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

只有 truefalse

TypeScript 复制代码
let isDone: boolean = false;
let isGreater: boolean = 10 > 5;  // true

小贴士 :在 TypeScript 中,numberstringboolean 都是小写,不要写成大写 NumberStringBoolean(它们是 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());  // 现在安全了
}

unknownany 更安全,推荐用于处理动态内容(如 API 响应)。

6.3 void ------ 没有返回值

用于函数没有返回值时。实际上,void 表示函数返回 undefinednull

TypeScript 复制代码
function logMessage(msg: string): void {
  console.log(msg);
  // 没有 return,或者 return; / return undefined;
}

变量也可以声明为 void 类型,但只能赋 undefinednull(如果 strictNullChecks 关闭),实际很少这样用。

6.4 null 和 undefined

在 TypeScript 中,nullundefined 既是值,也是类型。

TypeScript 复制代码
let u: undefined = undefined;
let n: null = null;

默认情况下,nullundefined 可以赋值给任何其他类型(例如 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 };

提示typeinterface 有很多重叠,但各有侧重。简单的对象和联合类型用 type,需要继承或实现的用 interface


十、总结

TypeScript 的类型系统是结构化的(鸭子类型),只要结构匹配即可,不需要显式声明继承关系。

开启 strictNullChecks 能避免大量 null/undefined 引发的错误。

优先使用类型推导,但函数参数和返回值建议显式注解。

any 是逃生舱,尽量用 unknown 代替。


如果这篇文章帮你解决了实操上的困惑,别忘记点击点赞、分享 ,也可以留言告诉我你遇到的其它问题,我会尽快回复。动手练习是掌握编程最快的方法,请务必亲手敲一遍本文的所有示例代码,并截图保存你的成果。你的关注是我坚持原创和细节共享的力量来源,谢谢大家。

相关推荐
你怎么知道我是队长1 小时前
CRC校验C语言实现-CRC8、CRC16、CRC16的直接计算法、查表法
c语言·前端·javascript
meilindehuzi_a2 小时前
深入理解 JavaScript 执行机制:从编译阶段到调用栈底层实现
开发语言·javascript·ecmascript
小雨下雨的雨2 小时前
基于鸿蒙PC Electron框架技术完成的表单验证技术详解
前端·javascript·华为·electron·前端框架·鸿蒙
提子拌饭1332 小时前
饮料含糖量查询应用 - 鸿蒙PC用Electron框架完整实现
前端·javascript·华为·electron·前端框架·鸿蒙
hsg772 小时前
简述:Jensen Huang‘s Footsteps网站全内容分析
前端·javascript·数据库
大家的林语冰3 小时前
Angular 王者归来,第 22 个主版本亮相,一大波前沿技术再度引领潮流!
前端·javascript·前端框架
老毛肚3 小时前
jeecgboot TS + Vue 模板化 03
前端·javascript·vue.js
小林ixn3 小时前
揭秘JavaScript面向对象:从栈模拟队列到原型链的深度剖析
javascript
FlyWIHTSKY3 小时前
React 19 + Next.js 16(App Router)项目中集成 MSW
开发语言·javascript·vue.js