从JS的"坑"到TS的"墙",再到Bun与AI:打造健壮的全栈应用
前端开发中,JavaScript 的灵活往往伴随着意想不到的"坑"。TypeScript 为我们筑起了一道类型"墙",而新一代运行时 Bun 则带来了极致的性能与开箱即用的体验。本文通过几个真实场景,带你理解这些技术是如何协同工作,甚至帮你轻松接入 AI 大模型。
一、Bun:比 Node 更快、开箱即用的 JS/TS 运行时
Bun 是一个全新的 JavaScript/TypeScript 运行时,它内置了包管理器 、测试器 、打包器 ,并且零配置直接支持 TypeScript。
Bun 的性能远超 Node,已被 Anthropic 收购并用于 Claude Code 底层。
安装 Bun(Windows PowerShell)
powershell
powershell -c "irm bun.sh/install/windows | iex"
安装完成后,你可以直接运行 .ts 文件,无需任何编译步骤:
bash
bun index.ts
Bun 兼容 npm 的大部分生态,但速度更快,启动时间更短。
二、TypeScript:为 JS 筑起一道类型之墙
TypeScript 是 JavaScript 的超集,它添加了静态类型系统。我们可以在开发阶段就捕获类型错误,而不是等到运行时。
基础类型注解
typescript
const nickname: string = "9527";
const age: number = 18; // 如果写成 age = "18",TS 会立即报错
console.log(`我是${nickname}, 我今年${age}岁`);
函数参数和返回值类型约束
typescript
function add(a: number, b: number): number {
return a + b;
}
let a = 1;
let b = "2";
// 编译错误:类型"string"的参数不能赋给类型"number"的参数
// add(a, b);
// 正确做法:显式转换
let c: number = add(a, Number(b)); // 使用 Number 构造函数
let d: number = add(a, parseInt(b)); // 使用 parseInt
let e: number = add(a, +b); // 使用一元加运算符(隐式转换,但意图明确)
TS 不会改变 JS 运行时的行为,但它会在编译阶段(或编辑器中)提醒你类型不匹配,迫使你处理类型转换,从而避免很多低级的运行时错误。
三、JavaScript 的易错性:从输入框到加法陷阱
1. 输入框里的"数字"其实是字符串
打开浏览器,一个简单的输入框:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Input 示例</title>
</head>
<body>
<input type="text" id="ipt">
<script>
const ipt = document.getElementById("ipt");
ipt.addEventListener("change", function(event) {
console.log(event.target.value); // 输出 "123"
console.log(typeof event.target.value); // 输出 "string"
});
</script>
</body>
</html>
明明输入的是数字,但 event.target.value 永远是 string 类型。如果你直接拿去做加法运算,结果会变成字符串拼接,而不是数学求和。
2. 加法与字符串拼接的"二义性"
javascript
function add(a, b) {
return a + b;
}
let a = 1;
let b = "2";
console.log(add(a, b)); // 输出 "12",而不是 3
+ 既可以做数值加法,也可以做字符串拼接。当两个操作数类型不一致时,JavaScript 会优先进行字符串拼接。这类隐式类型转换往往导致难以追踪的逻辑错误。
四、异步编程的优雅方案:Promise + async/await
JavaScript 是单线程非阻塞的,异步操作离不开回调。而 Promise 和 async/await 让异步代码写起来像同步一样直观。
封装一个 sleep 函数
javascript
function sleep(t) {
// 返回一个 Promise,在 t 毫秒后 resolve
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, t);
});
}
async function main() {
console.log('--start--');
await sleep(2000); // 等待 2 秒,异步任务同步化
console.log('--end--');
}
main();
await 后面必须跟一个 Promise,它会暂停当前 async 函数的执行,直到 Promise 状态变为 resolved。这种模式尤其适用于延时操作 、轮询 以及后续要讲到的 AI 接口调用。
五、实战:用 Bun + TypeScript 调用 DeepSeek API
现在我们来做一个真正有"深度"的事情------调用大语言模型接口。我们将使用 axios 发送 HTTP 请求,并用 dotenv 管理 API Key。
1. 初始化项目并安装依赖
bash
bun init -y
bun add axios dotenv
2. 创建 .env 文件
env
DEEPSEEK_BASE_URL=https://api.deepseek.com
DEEPSEEK_API_KEY=sk-xxxxx
3. 编写 index.ts
typescript
import axios from 'axios';
import { version } from 'bun';
import dotenv from 'dotenv';
dotenv.config(); // 加载 .env 到 process.env
console.log(process.env.DEEPSEEK_BASE_URL); // 打印基础 URL,用于调试
async function chat() {
try {
// 发起 POST 请求到 /chat/completions 端点
const res = await axios.post(
`${process.env.DEEPSEEK_BASE_URL}/chat/completions`,
{
model: 'deepseek-chat',
messages: [
{
role: 'user',
content: '你好,介绍一下 Bun'
}
]
},
{
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.DEEPSEEK_API_KEY}`
}
}
);
// axios 将响应数据放在 res.data 中
const reply = res.data.choices[0].message.content;
console.log(reply);
} catch (err) {
// 网络错误、超时、API Key 无效等都会被捕获
console.error(err.message);
}
}
chat();
4. 运行
bash
bun index.ts
你会看到 DeepSeek 模型返回的一段关于 Bun 的介绍文字。
关键点解读
- 环境变量 :
API Key绝不能写死在代码里,通过dotenv加载.env文件,既安全又便于不同环境切换。 - HTTP 请求细节 :
- 请求行:POST /chat/completions HTTP/1.1
- 请求头:
Content-Type: application/json和Authorization: Bearer <key> - 请求体:JSON 格式的 model 和 messages
- 错误处理 :网络波动、服务端限流、Key 失效等都可能导致请求失败,
try-catch是必不可少的防御手段。 - 类型安全:虽然示例中没显式写 TS 类型,但你可以为响应数据定义 interface,让编辑器提供智能提示。
六、总结:从自由到规范,从本地到云端
| 问题/场景 | 解决方案 | 核心收益 |
|---|---|---|
| JS 隐式类型转换导致 bug | TypeScript 静态类型检查 | 开发阶段捕获错误,提升健壮性 |
| 异步代码回调地狱 | Promise + async/await | 代码线性书写,逻辑清晰 |
| 传统运行时性能瓶颈 | Bun 运行时 + 零配置 TS 支持 | 极速启动,开发体验爆表 |
| 调用 AI API | axios + dotenv + 错误处理 | 安全、可维护地与大模型交互 |
Bun 和 TypeScript 的组合,让 JavaScript 生态同时拥有了高性能 和高可靠性。而接入大语言模型的门槛也远比想象的低------不过是一个 POST 请求。当你把类型安全、异步模式和现代的运行时工具链结合在一起,就能构建出既健壮又智能的应用。
本文所有代码示例均可在本地运行,建议你亲手试一试:安装 Bun,申请 DeepSeek API Key,然后运行
index.ts,感受 AI 与现代化 JS/TS 开发流程的无缝衔接。
思考题:如果输入框里的"数字"需要参与数学运算,你会用哪种方式转换类型?为什么 TypeScript 无法完全避免运行时的类型错误?欢迎在评论区讨论。