编程进阶网 编程进阶网
首页
  • 在线工具
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 加解密
  • 时间日期
  • 网络工具
  • 颜色设计
  • 二维码
  • 开发实用
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++编程技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 在线工具
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 加解密
  • 时间日期
  • 网络工具
  • 颜色设计
  • 二维码
  • 开发实用
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++编程技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • Android提升进阶

  • iOS开发和进阶

  • Web开发和进阶

    • README
    • HTML工具手册

    • CSS样式与布局

    • JavaScript核心

    • TypeScript入门

      • README
      • TS类型系统基础
        • 1. 案例引入
          • 1.1 一段看似无害的 JS 代码
          • 1.2 TypeScript 的答案
        • 2. 类型系统概览
          • 2.1 JS 动态类型 vs TS 静态类型的本质区别
          • 2.2 TS 类型层级金字塔
        • 3. 七种基本类型
          • 3.1 number:不止是"数字"
          • 3.2 string:模板字面量类型的基础
          • 3.3 boolean:不仅仅是 true/false
          • 3.4 null 与 undefined:两位特殊的"空"
          • 3.5 symbol:唯一标识符
          • 3.6 bigint:处理超大整数
        • 4. 数组与元组
          • 4.1 数组的两种声明方式
          • 4.2 元组(Tuple):定长定类型的数组
          • 4.3 元组越界与可选元素
        • 5. any、unknown、void、never 四兄弟
          • 5.1 any:逃离类型检查
          • 5.2 unknown:安全版本的 any
          • 5.3 void:没有返回值
          • 5.4 never:永不存在的类型
        • 6. 枚举(Enum)
          • 6.1 数字枚举
          • 6.2 字符串枚举
          • 6.3 常量枚举(const enum)
          • 6.4 枚举 vs 联合类型:选型建议
        • 7. 字面量类型
          • 7.1 字符串字面量类型
          • 7.2 数字字面量类型与布尔字面量类型
          • 7.3 const 断言(as const)
        • 8. 联合类型与交叉类型
          • 8.1 联合类型(Union):或
          • 8.2 交叉类型(Intersection):且
          • 8.3 联合与交叉的组合使用
        • 9. 类型推断与类型断言
          • 9.1 类型推断:TS 自动推导
          • 9.2 类型断言(Type Assertions)
          • 9.3 非空断言(!)
        • 10. 速查表
      • TS接口与对象类型
      • TS函数与类实战
      • TS泛型编程实战
      • TS高级类型编程
      • TS类型守卫机制
      • TS模块系统详解
      • TS工程配置实践实践
    • Vue高级进阶

    • Web工程化实践

  • Linux应用开发

  • IoT智能硬件开发

  • Apps
  • Web开发和进阶
  • TypeScript入门
杨充
2025-06-24
目录

TS类型系统基础

# 01.TS类型系统基础

从 JavaScript 的自由王国到 TypeScript 的类型约束——理解 TS 类型系统的设计哲学与基础类型体系。

# 1. 案例引入

# 1.1 一段看似无害的 JS 代码

// 一个简单的用户服务函数
function formatUser(user) {
    return `${user.name} - ${user.age}岁`;
}

// 某天同事这样调用:
formatUser({ name: "张三", age: "25" });           // "张三 - 25岁" ✅ 碰巧对
formatUser({ name: "李四" });                       // "李四 - undefined岁" ❌ Bug A
formatUser(null);                                    // ❌ TypeError: Cannot read properties of null
formatUser({ userName: "王五", userAge: 30 });      // "undefined - undefined岁" ❌ Bug C

线上事故:后端 API 字段从 age: number 改为 age: string,前端没有报错,但在计算总年龄时 "25" + "30" = "2530",导致整个报表数据异常——排查了 3 小时。

# 1.2 TypeScript 的答案

function formatUser(user: { name: string; age: number }): string {
    return `${user.name} - ${user.age}岁`;
}

formatUser({ name: "张三", age: "25" });  // ❌ 编译错误!age 应为 number
formatUser({ name: "李四" });              // ❌ 编译错误!缺少 age
formatUser(null);                           // ❌ 编译错误!null 不能赋给 object

TS 的核心价值:将运行时错误提前到编译时。本文带你理解 TS 类型系统的基石——从每种基础类型的设计意图到实战应用。


# 2. 类型系统概览

# 2.1 JS 动态类型 vs TS 静态类型的本质区别

JavaScript(动态类型):
  变量 → 运行时绑定类型 → 可以随时改变
  let x = 1;
  x = "hello";        // ✅ 完全合法,但危险的灵活性

TypeScript(静态类型):
  变量 → 编译时确定类型 → 类型不可变
  let x: number = 1;
  x = "hello";        // ❌ 编译错误!保护代码质量

# 2.2 TS 类型层级金字塔

              unknown (顶级类型——接收一切)
             ↗
    any ──── unknown (any 放弃检查,unknown 保留安全)
             ↘
    ┌─────────┬─────────┐
    │  object │ 基本类型  │
    │  Array  │ number   │
    │  tuple  │ string   │
    │  { }    │ boolean  │
    │         │ symbol   │
    │         │ bigint   │
    └────┬────┴────┬─────┘
         │         │
         ├── null ─┤
         │   undefined
         │
      never (底部类型——绝不可能)

疑惑:any 和 unknown 都能接收任意值,有什么区别? 结论:any 关闭了类型检查——可以调用任意方法、访问任意属性;unknown 保留了安全性——必须先收窄类型才能使用。后文详解。


# 3. 七种基本类型

# 3.1 number:不止是"数字"

let decimal: number = 42;
let hex: number = 0x2a;          // 十六进制
let binary: number = 0b101010;   // 二进制
let octal: number = 0o52;        // 八进制

let big: number = 1_000_000;     // 大数分隔符(ES2021)

// TS 的 number 涵盖整数 + 浮点数,都是 64 位双精度 IEEE 754
// 与 JS 一致:0.1 + 0.2 !== 0.3 的问题依然存在

# 3.2 string:模板字面量类型的基础

let name: string = "张三";
let greeting: string = `你好, ${name}`;

// TS 独有的字面量类型(后文高级类型会详细展开)
const hello = "hello";  // 类型被推断为字面量 "hello",而非 string

# 3.3 boolean:不仅仅是 true/false

let isDone: boolean = false;

// 类型守卫中 boolean 是基础判断依据
// 但注意:TS 不会自动把 truthy/falsy 当作 boolean
let flag = 42;
// if (flag) { ... }    // ✅ 运行时可以,但 flag 类型是 number,不是 boolean

# 3.4 null 与 undefined:两位特殊的"空"

// 默认 strictNullChecks: false → null/undefined 可赋给任何类型(不安全)
// 默认 strictNullChecks: true  → null/undefined 有自己的类型,不能赋给其他类型

let u: undefined = undefined;
let n: null = null;

// 严格模式下的"可空类型"
let nameOrNull: string | null = null;   // 显式联合 null
let ageOrUndef: number | undefined = undefined;

// 类型守卫检查后才能安全使用
function greet(name: string | null) {
    if (name === null) {
        return "匿名用户";
    }
    return `你好, ${name}`;  // name 被收窄为 string
}

# 3.5 symbol:唯一标识符

const sym1 = Symbol("id");
const sym2 = Symbol("id");
console.log(sym1 === sym2);  // false——永远唯一

// 作为对象属性键,避免属性冲突
const user = {
    [Symbol("id")]: 1,
    name: "张三"
};

// unique symbol:字面量级别的 symbol 类型
const UniqueKey: unique symbol = Symbol("myKey");
// UniqueKey 的类型不是 symbol,而是 typeof UniqueKey(一个字面量类型)

# 3.6 bigint:处理超大整数

// bigint 和 number 不兼容,不能混用
let big: bigint = 9007199254740991n;
let num: number = 42;

// big + num;  // ❌ bigint 不能与 number 运算
big + BigInt(num);  // ✅ 显式转换

# 4. 数组与元组

# 4.1 数组的两种声明方式

// 语法 1:T[]
let numbers: number[] = [1, 2, 3];
let strings: string[] = ["a", "b", "c"];

// 语法 2:Array<T>(泛型写法,第 04 篇详解)
let numbers2: Array<number> = [1, 2, 3];

// 语法 1 更简洁,且对只读数组支持更好
let readonlyNumbers: readonly number[] = [1, 2, 3];
// readonlyNumbers.push(4);  // ❌ 只读

// 二维数组
let matrix: number[][] = [
    [1, 2],
    [3, 4]
];

# 4.2 元组(Tuple):定长定类型的数组

// 元组的长度和每个位置类型都是固定的
let user: [string, number] = ["张三", 25];
user[0];  // string
user[1];  // number

// 超过长度赋值会报错(严格模式)
// user[2] = true;  // ❌ 长度超出

// 元组的核心价值:函数返回多值
function getUser(): [string, number] {
    return ["张三", 25];
}
const [name, age] = getUser();  // 解构时类型自动推断

# 4.3 元组越界与可选元素

// 带可选元素的元组
let opt: [string, number?] = ["hello"];
opt = ["hello", 42];

// 带 rest 元素的元组
let rest: [string, ...number[]] = ["hello", 1, 2, 3];

// 具名元组(可读性更好,但只是标记作用)
let named: [name: string, age: number] = ["张三", 25];

# 5. any、unknown、void、never 四兄弟

# 5.1 any:逃离类型检查

let danger: any = 4;
danger = "hello";       // ✅
danger.foo.bar();       // ✅ 编译通过,运行时炸!
danger();

// any 的"传染性"
let safe: number = danger;  // ✅ any 污染了 safe 的类型
// 合法的使用场景:
// 1. JS→TS 迁移时的临时方案
// 2. 确实无法确定类型的第三方库返回值
// 3. 控制台调试

# 5.2 unknown:安全版本的 any

let unknown: unknown = 4;
unknown = "hello";       // ✅ 可以收任何值

// 区别来了:不能直接使用 unknown
// unknown.foo;          // ❌ 对象类型为 unknown
// unknown();            // ❌ 不能调用

// 必须先类型收窄
if (typeof unknown === "string") {
    console.log(unknown.toUpperCase());  // ✅ 收窄为 string
}

// unknown 是安全的"万能类型"——必须验证后才能使用

# 5.3 void:没有返回值

function log(msg: string): void {
    console.log(msg);
    // 没有 return 语句,或 return undefined
}

// void 主要用于回调函数的返回值位置
type Callback = () => void;
let cb: Callback = () => {
    return 42;  // 赋值给 void 类型时,返回值被忽略,不会报错
};

# 5.4 never:永不存在的类型

// never 表示"永远不会发生"的值

// 场景 1:总是抛出异常的函数
function throwError(): never {
    throw new Error("出错了");
}

// 场景 2:死循环
function infiniteLoop(): never {
    while (true) {}
}

// 场景 3:穷尽性检查(第 06 篇详述)
type Shape = "circle" | "square";
function area(shape: Shape): number {
    switch (shape) {
        case "circle": return Math.PI;
        case "square": return 4;
        default:
            const exhausted: never = shape;  // shape 应为 never
            return exhausted;
    }
}

四兄弟对比表:

类型 赋值给其他类型 可以调用方法 适用场景
any 可以(污染一切) 可以 临时迁移/放弃检查
unknown 不可以(安全) 不可以 接受未知值但需要验证
void — — 函数无返回值
never 可以赋给任何类型 — 不可能出现的分支

# 6. 枚举(Enum)

# 6.1 数字枚举

enum Direction {
    Up,      // 0
    Down,    // 1
    Left,    // 2
    Right    // 3
}

// 反向映射(数字枚举特有)
console.log(Direction.Up);     // 0
console.log(Direction[0]);     // "Up"

// 自定义起始值
enum Status {
    Success = 200,
    NotFound = 404,
    ServerError = 500
}

# 6.2 字符串枚举

enum Color {
    Red = "#FF0000",
    Green = "#00FF00",
    Blue = "#0000FF"
}

// 没有反向映射
console.log(Color.Red);  // "#FF0000"
// console.log(Color["#FF0000"]);  // ❌ 不存在

# 6.3 常量枚举(const enum)

const enum Size {
    S = 1,
    M = 2,
    L = 3
}

let size = Size.M;  // 编译后直接内联为 let size = 2; 不会有 Size 对象
// 适用:频繁使用且代码体积敏感的场景
// 注意:const enum 不能用于 .d.ts 声明文件中给外部使用

# 6.4 枚举 vs 联合类型:选型建议

// 枚举方案
enum HttpMethod {
    GET = "GET",
    POST = "POST",
    DELETE = "DELETE"
}
function request(method: HttpMethod) {}
request(HttpMethod.GET);  // 引入额外对象

// 字面量联合类型方案(更轻量)
type HttpMethod2 = "GET" | "POST" | "DELETE";
function request2(method: HttpMethod2) {}
request2("GET");  // 直接使用字符串,零运行时开销

// 选型原则:
// 有反向映射需求、需要语义化常量 → enum
// 仅需要类型约束、零开销 → 字面量联合类型

# 7. 字面量类型

# 7.1 字符串字面量类型

// 不仅约束类型为 string,还约束具体值
const hello = "Hello";  // 类型: "Hello"(不是 string)

let str: "foo" = "foo";
// str = "bar";  // ❌

// 与联合类型配合——替代枚举的轻量方案
type Alignment = "left" | "center" | "right";
function align(direction: Alignment) {}

align("left");    // ✅
// align("top");  // ❌

# 7.2 数字字面量类型与布尔字面量类型

type Dice = 1 | 2 | 3 | 4 | 5 | 6;
let roll: Dice = 3;
// roll = 7;  // ❌

type Truthy = true;
let flag: Truthy = true;
// flag = false;  // ❌

# 7.3 const 断言(as const)

// 普通声明 → 宽泛类型
let config1 = { host: "localhost", port: 3000 };
// config1.host: string, config1.port: number

// as const → 最窄类型(字面量 + readonly)
let config2 = { host: "localhost", port: 3000 } as const;
// config2.host: "localhost", config2.port: 3000
// config2.host = "other";  // ❌ readonly

// 数组 → readonly tuple
let colors = ["red", "green", "blue"] as const;
// colors: readonly ["red", "green", "blue"]

# 8. 联合类型与交叉类型

# 8.1 联合类型(Union):或

// A | B 表示值可以是 A 类型或者 B 类型
type ID = string | number;

function printID(id: ID) {
    // id.length;  // ❌ 只有 string 有 length
    if (typeof id === "string") {
        console.log(id.length);  // ✅ 收窄为 string
    } else {
        console.log(id);         // id 被收窄为 number
    }
}

// 联合类型是 TS 处理 JS 动态性的核心武器
type Result = { success: true; data: object } | { success: false; error: string };
function handle(result: Result) {
    if (result.success) {
        console.log(result.data);
    } else {
        console.log(result.error);
    }
}

# 8.2 交叉类型(Intersection):且

// A & B 表示值必须同时满足 A 和 B
type HasName = { name: string };
type HasAge = { age: number };
type Person = HasName & HasAge;
// { name: string; age: number }

// 与 interface extends 的区别:交叉类型可以合并基本类型
// 但 number & string 的结果是 never——不存在同时是两者的值

# 8.3 联合与交叉的组合使用

// 接口的交叉合并
interface Colorful { color: string; }
interface Circle { radius: number; }
type ColorfulCircle = Colorful & Circle;

const cc: ColorfulCircle = { color: "red", radius: 10 };

// 交叉 + 联合的关键用法
type Union = { a: string } | { b: number };  // 可以是 {a} 或 {b}
type Intersection = { a: string } & { b: number };  // 必须同时有 {a, b}

# 9. 类型推断与类型断言

# 9.1 类型推断:TS 自动推导

// 大部分情况下不需要显式标注类型
let x = 42;              // number
let arr = [1, 2, 3];     // number[]
let obj = { a: 1 };      // { a: number }

// 最佳类型通用类型
let mixed = [1, "hello"];  // (string | number)[]

// 上下文推断
window.onmousedown = function (e) {
    console.log(e.button);  // e 被推断为 MouseEvent——基于上下文
};

# 9.2 类型断言(Type Assertions)

// 告诉编译器"我知道这个类型是什么"
// 语法 1:as Type
let value: unknown = "hello";
let len1 = (value as string).length;

// 语法 2:<Type>(不推荐,与 JSX 冲突)
let len2 = (<string>value).length;

// 双重断言(极度危险,仅用于极端情况)
// let bad = (value as unknown as number);  // 绕过类型检查

// 适当的使用场景:DOM 元素获取
const canvas = document.getElementById("myCanvas") as HTMLCanvasElement;
const ctx = canvas.getContext("2d");
// 此处 canvas 不会是 null——开发者已知 DOM 存在

# 9.3 非空断言(!)

function getValue(): string | undefined {
    return Math.random() > 0.5 ? "hello" : undefined;
}

let v = getValue();
// console.log(v.length);  // ❌ v 可能为 undefined
console.log(v!.length);     // ✅ 你告诉 TS:v 一定不是 null/undefined

// 使用前提:你 100% 确定值不为空,否则运行时依然崩

# 10. 速查表

类型 示例 关键特性
number 42, 0x2a, 0b101010 64 位浮点,含整数和浮点
string "hello", \${x}`` 模板字面量类型的基础
boolean true, false 不自动含 truthy/falsy
null null strictNullChecks 下独立类型
undefined undefined 同上
symbol Symbol("id") unique symbol 是字面量
bigint 9007199254740991n 不能与 number 混用
T[] number[] 数组
[string, number] ["A", 1] 元组:定长定类型
any — 逃离 TS,传染性
unknown — 安全的 top type
void — 函数无返回值
never — 永不存在的类型
"left" \| "right" — 字面量联合
A & B — 交叉:同时满足
A \| B — 联合:满足其一
as const — 最窄推断 + readonly

下一篇:02.TS接口与对象类型

上次更新: 2026/06/24, 12:59:24
README
TS接口与对象类型

← README TS接口与对象类型→

最近更新
01
CSS选择器入门
06-23
02
CSS定位与层级
06-23
03
CSS盒模型详解
06-23
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 鄂ICP备2024073355号-1 | 鄂ICP备2024073355号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式