【声明】本博客所有内容均为个人业余时间创作,所述技术案例均来自公开开源项目(如Github,Apache基金会),不涉及任何企业机密或未公开技术,如有侵权请联系删除
标题
161、【Agent】【OpenCode】TuiThreadCmd(联合类型)
背景
上篇 blog
【Agent】【OpenCode】TuiThreadCmd(箭头函数声明)
继续分析了 fetch 函数定义,接着介绍了 fetch 函数类型语法,提到了联合类型 |,表明这个参数可以是多种类型,接着解析了整个函数类型,并提到了箭头 => 的两种用法:类型定义和实际函数实现,这两种用法在编译器眼里和运行时结果上是两个完全不同的东西,类型定义里的 => 是描述形状(编译后消失);实现里的 => 是创建函数(编译后变成真代码),接着对比了编译后的产物(最本质的区别), 能放在哪里(语法位置不同),以及为什么 TS 要这么设计,下面继续分析
OpenCode
上篇 blog 提到了
javascript
const myFetch: typeof fetch = async (input, init?) => { ... }
// ^^^^^^^^^^^^^^^^ 类型描述(用 =>)
// ^^^^^^^^^^^^^^^^^^^^ 实际实现(也用 =>)
这里有个点,在类型定义的时候同时定义了函数实现,这里语法上没问题,因为这行代码被 = 号清晰地劈成了两个完全独立的世界:
javascript
const myFetch: typeof fetch = async (input, init?) => { ... }
// ─────┬───── ───────┬────── ───────────────┬─────────────
// 变量声明 类型注解(纯描述) 值赋值(真代码)
- 左边
typeof fetch:这是类型注解 。它只告诉编译器这个变量应该长什么样 ,编译后直接消失 。这里面的=>是类型符号。 - 右边
async (...) => {...}:这是箭头函数表达式 。它是一个真实的 JS 值,被赋值给了myFetch。这里面的=>是 JS 语法。
⚠️ 关键纠正
这里并没有在类型定义里写实现,只是在声明变量的同时,给它标注了类型,并赋予了初始值,这三件事在同一行完成,但彼此互不干扰。
🧪 验证方法:删掉类型注解
javascript
// 删掉 : typeof fetch,程序照样跑,只是失去了类型检查
const myFetch = async (input, init?) => { ... }
这证明了右边的函数实现跟左边的类型定义没有语法上的绑定关系 。类型注解只是一个可选的标签,贴在赋值语句上而已。
📌 对比:什么才是在类型定义里写实现?(这才是非法的)
javascript
// ❌ 这才叫"在类型定义里混入实现"------TS 直接报错
type MyFetch = (input: RequestInfo) => { return new Response() }
// 类型位置出现了函数体,非法!
// ✅ 正确的做法永远是:类型归类型,实现归实现
type MyFetch = (input: RequestInfo) => Response // 纯类型
const myFetch: MyFetch = (input) => new Response() // 纯实现
💡 一句话总结
const x: Type = value 是 "声明 + 标注 + 赋值" 三合一语法糖 ,这里不是在类型里写了实现,而是把类型标注和值赋值写在了同一行里 。: 右边是给编译器看的说明书,= 右边是给运行时用的真代码,两者井水不犯河水。
回归到原生 fetch 的类型声明

这里关于联合类型 | 有人可能会有疑问:如果传进来的东西一会儿是 A 一会儿是 B,在函数里面怎么知道传入参数到底是啥?万一用错了方法不就报错了?
TypeScript 早就想到了这一点,它提供了一个核心机制:类型收窄。下面详细分析
1. 不能盲操作,但可以先判断再操作
当拿到一个 RequestInfo | URL 类型的参数时,TS 确实会禁止直接调用它们各自独有的方法:
javascript
function handle(input: RequestInfo | URL) {
// ❌ TS 报错!因为 URL 没有 .method 属性
console.log(input.method)
// ❌ TS 报错!因为 RequestInfo(字符串) 没有 .pathname 属性
console.log(input.pathname)
}
这时候看起来确实崩溃了吧?但只要做一个判断,TS 就会自动帮把类型收窄到确定的那一个:
javascript
function handle(input: RequestInfo | URL) {
// ✅ 用 instanceof 判断
if (input instanceof URL) {
// 在这个 if 块里,TS 知道 input 一定是 URL
console.log(input.pathname) // ✅ 有提示,不报错
} else {
// 在 else 块里,TS 知道 input 一定是 RequestInfo
console.log(input.toString()) // ✅ 安全
}
}
2. 常见的收窄方式
TS 非常聪明,它能识别多种判断语法:
| 判断方式 | 适用场景 | 示例 |
|---|---|---|
instanceof |
区分不同的类/构造函数 | input instanceof URL |
typeof |
区分基本类型 vs 对象 | typeof input === "string" |
in |
检查某个属性是否存在 | "pathname" in input |
| 字面量比较 | 区分字符串/数字字面量 | type === "GET" |
| 自定义类型守卫 | 复杂对象区分 | isURL(input) |
3. 回到 fetch 的实际场景
原生 fetch 内部就是这么处理的(伪代码):
javascript
async function fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
let url: string
if (input instanceof URL) {
url = input.href // TS 知道这里是 URL
} else if (input instanceof Request) {
url = input.url // TS 知道这里是 Request
} else {
url = String(input) // TS 知道这里是 string
}
// 后续统一用 url 这个 string 变量处理...
}
💡 核心认知转变
联合类型不是混乱,而是精确地表达了多种可能性。
- 没有联合类型 :只能写
any,放弃所有类型检查,运行时才爆炸 💥 - 有联合类型 :TS 强制开发者在每个分支里做正确的判断,编译期就杜绝了用错方法的可能 🛡️
所以 RequestInfo | URL 不会导致问题,反而是在保护开发者------只要按规矩先判断再使用,TS 就会在每一个分支里进行完美的类型提示和安全保障。这正是 TypeScript 比 JavaScript 安全的核心原因之一。
OK,本篇先到这里,如有疑问,欢迎评论区留言讨论,祝各位功力大涨,技术更上一层楼!!!更多内容见下篇 blog