TypeScript 与异步实战:调用 AI 接口入门

12 阅读4分钟

为什么要学 TypeScript?

先聊 JavaScript 的一个老毛病。

我在网页上放了一个输入框,输入了 18,打算拿它做加法。结果一跑,发现 18 + 1 等于 "181"。没有报错,没有警告,程序安安静静地给出了一个错误答案。

这就是 JS 的弱类型问题。input.value 拿到的永远是字符串,而 + 这个符号在 JS 里既能做数字加法,也能做字符串拼接,全看两边是什么类型。所以 "18" + 1 它不崩溃,只是悄悄给你拼了个字符串。这类 bug 可以在系统里潜伏很久,很难排查。

TypeScript 就是来解决这个问题的。它是微软做的,本质上是 JS 的超集,意思是所有 JS 代码都是合法的 TS 代码,只是 TS 在此基础上加了类型约束。你可以给变量、函数参数和返回值标注类型,TS 在编译阶段就会帮你检查,把问题扼杀在运行之前。

typescript

const age: number = 123;       // 明确告诉 TS:这是数字
const name: string = "9527";   // 这是字符串

function add(a: number, b: number): number {
    return a + b;
}

如果不小心把字符串传进 add(),TS 编译时就会报错,而不是等到线上炸锅。这也是为什么 TS 现在已经是 AI Agent 开发的标配——越复杂的系统,越需要类型来兜底。

类型转换。如果从 input 拿到了字符串 "2",想把它变成数字,有几种写法:

typescript

Number("2")     // 显式转换,推荐,最清晰
parseInt("2")   // 也可以,常用于处理 "2px" 这类带单位的情况
+"2"            // 隐式转换,能用但可读性差,不推荐

Promise 和 async/await:异步

JS 是单线程的,遇到耗时操作(比如网络请求、定时器)不会傻等,而是先去干别的,等结果回来再处理。这就是异步。

ES6 引入了 Promise,就像一张"承诺书":我现在给你一个 Promise,等任务完成了,我会兑现(resolve)或者告诉你失败了(reject)。

用 Promise 手写一个 sleep 函数:

javascript

function sleep(t) {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve();  // t 毫秒后兑现承诺
        }, t);
    });
}

但如果异步操作多了,Promise 嵌套起来会很难看。async/await 解决了这个问题,它让异步代码读起来像同步:

javascript

async function main() {
    console.log('--start--');
    await sleep(2000);   // 在这里暂停 2 秒,再往下走
    console.log('--end--');
}

await 后面接一个 Promise,程序会等它完成再继续。注意 await 只能在 async 函数内部使用。


Bun:比 Node 更快的运行时

写好了 TS 文件,得有个地方运行它。传统上用 Node.js,但现在有个更好的选择:Bun

Bun 是一个更现代的 JS/TS 运行时,有几个特点值得记住:

  • 原生支持 TypeScript,不需要手动配置编译,直接 bun run index.ts 就能跑
  • 速度快,底层用 Zig 语言写的,启动和执行都比 Node 快
  • 内置包管理器,替代 npm,bun installnpm install 快得多
  • 零配置,开箱即用,适合快速起项目

用 axios 调用 AI 接口

把上面的知识组合起来,可以写一个真正调用 LLM 接口的程序。

先说 HTTP 请求。调用 API 有 GET 和 POST 两种常见方式。对于 AI 接口,要用 POST,原因有两个:GET 请求的参数暴露在 URL 里,API Key 会明文可见;而且 GET 有长度限制,发不了太多内容。POST 把数据放在请求体(body)里,更安全、也没有大小限制。

axios 是一个封装了原生 fetch 的 HTTP 请求框架,企业项目里用得很多,写起来更简洁,错误处理也更完善。

typescript

import axios from 'axios';
import dotenv from 'dotenv';
dotenv.config();  // 读取 .env 文件里的环境变量

async function chat() {
    try {
        const res = await axios.post(
            process.env.DEEPSEEK_BASE_URL,   // 接口地址
            {
                model: 'deepseek-v4-flash',
                messages: [{ role: 'user', content: '你好,介绍一下Bun' }]
            },
            {
                headers: {
                    'Content-Type': 'application/json',
                    Authorization: `Bearer ${process.env.DEEPSEEK_API_KEY}`
                }
            }
        );

        // axios 的响应会自动包一层 .data
        console.log(res.data.choices[0].message.content);

    } catch (err: any) {
        console.log(err.message);
    }
}

chat();

几个细节值得注意:

API Key 要放 .env 文件里,用 dotenv 读取,如果写在代码里,代码可能会传到 GitHub,Key 泄露就麻烦了。

一定要包 try/catch。调用 LLM 接口失败的可能性很多:网络超时、Key 无效、模型服务忙……不加错误处理,程序直接崩掉,什么信息都没有。


小结

这几个知识点串起来其实就是一条线:用 TypeScript 写出有类型保障的代码,用 async/await 处理异步的网络请求,用 axios 发送 HTTP POST 调用 AI 接口,用 Bun 直接运行整个项目,用 .env 保护敏感信息。

每一块单独看都不复杂,组合在一起就是一个完整的 AI Agent 最小骨架。