Bun TypeScript 支持
Bun 原生支持 TypeScript,无需任何配置即可直接运行 .ts 和 .tsx 文件。本章介绍 Bun 的 TypeScript 特性和最佳实践。
开箱即用
直接运行 TypeScript
typescript
// app.ts
interface User {
id: number;
name: string;
email: string;
}
function greet(user: User): string {
return `Hello, ${user.name}!`;
}
const user: User = {
id: 1,
name: "张三",
email: "zhangsan@example.com"
};
console.log(greet(user));运行:
bash
bun app.ts
# 输出: Hello, 张三!无需:
- 安装 typescript
- 配置 ts-node
- 预编译步骤
TypeScript 配置
自动生成 tsconfig.json
bash
# 初始化项目时自动创建
bun init
# 或手动创建
bun tsc --init推荐配置
json
{
"compilerOptions": {
// 目标和模块
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
// 严格模式
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
// 路径配置
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
},
// JSX 支持
"jsx": "react-jsx",
// 其他选项
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
// 输出目录
"outDir": "./dist",
"rootDir": "./src",
// Bun 类型
"types": ["bun-types"]
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}Bun 类型定义
bash
# 安装 Bun 类型定义
bun add -d @types/bun获得完整的 Bun API 类型提示:
typescript
// 现在有完整的类型支持
const file = Bun.file("./data.txt");
const content: string = await file.text();
const server = Bun.serve({
port: 3000,
fetch(request: Request): Response {
return new Response("Hello!");
}
});类型检查
运行类型检查
Bun 运行时不进行类型检查(为了速度),需要单独运行:
bash
# 使用 TypeScript 编译器检查
bun tsc --noEmit
# 或在 package.json 中配置
{
"scripts": {
"typecheck": "tsc --noEmit",
"dev": "bun --watch src/index.ts",
"build": "bun run typecheck && bun build src/index.ts --outdir dist"
}
}类型错误示例
typescript
// error.ts
function add(a: number, b: number): number {
return a + b;
}
// TypeScript 会报错,但 Bun 仍会运行
add("1", 2); // 类型错误bash
# Bun 会运行(忽略类型错误)
bun error.ts
# TypeScript 检查会报错
bun tsc --noEmit
# error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.TSX / JSX 支持
React 组件
tsx
// App.tsx
import React from "react";
interface Props {
name: string;
age?: number;
}
const Greeting: React.FC<Props> = ({ name, age }) => {
return (
<div>
<h1>你好, {name}!</h1>
{age && <p>年龄: {age}</p>}
</div>
);
};
export default Greeting;服务端渲染
tsx
// server.tsx
import { renderToString } from "react-dom/server";
import App from "./App";
const server = Bun.serve({
port: 3000,
fetch(request) {
const html = renderToString(<App name="Bun" />);
return new Response(`
<!DOCTYPE html>
<html>
<head><title>Bun React SSR</title></head>
<body>
<div id="root">${html}</div>
</body>
</html>
`, {
headers: { "Content-Type": "text/html" }
});
}
});JSX 配置
json
// tsconfig.json
{
"compilerOptions": {
// React 17+ 新 JSX 转换
"jsx": "react-jsx",
"jsxImportSource": "react",
// 或使用经典转换
// "jsx": "react",
// 使用 Preact
// "jsx": "react-jsx",
// "jsxImportSource": "preact"
}
}类型导入
导入类型
typescript
// types.ts
export interface User {
id: number;
name: string;
}
export type Status = "active" | "inactive" | "pending";
// main.ts
// 类型导入(不会包含在运行时代码中)
import type { User, Status } from "./types";
// 或混合导入
import { type User, type Status } from "./types";
function processUser(user: User, status: Status) {
console.log(`${user.name}: ${status}`);
}内联类型导入
typescript
// 确保类型只在类型上下文使用
import { type User } from "./types";泛型
泛型函数
typescript
function identity<T>(value: T): T {
return value;
}
const num = identity<number>(42);
const str = identity("hello"); // 类型推断
// 泛型约束
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { name: "张三", age: 25 };
const name = getProperty(user, "name"); // string泛型类
typescript
class Container<T> {
private value: T;
constructor(value: T) {
this.value = value;
}
getValue(): T {
return this.value;
}
setValue(value: T): void {
this.value = value;
}
}
const numberContainer = new Container<number>(42);
const stringContainer = new Container("hello");泛型接口
typescript
interface Repository<T> {
findById(id: number): Promise<T | null>;
findAll(): Promise<T[]>;
save(entity: T): Promise<T>;
delete(id: number): Promise<boolean>;
}
interface User {
id: number;
name: string;
}
class UserRepository implements Repository<User> {
private users: User[] = [];
async findById(id: number): Promise<User | null> {
return this.users.find(u => u.id === id) || null;
}
async findAll(): Promise<User[]> {
return this.users;
}
async save(user: User): Promise<User> {
this.users.push(user);
return user;
}
async delete(id: number): Promise<boolean> {
const index = this.users.findIndex(u => u.id === id);
if (index !== -1) {
this.users.splice(index, 1);
return true;
}
return false;
}
}装饰器
启用装饰器
json
// tsconfig.json
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}类装饰器
typescript
function Logger(constructor: Function) {
console.log(`类 ${constructor.name} 被创建`);
}
@Logger
class MyService {
constructor() {
console.log("MyService 实例化");
}
}
// 输出:
// 类 MyService 被创建
// MyService 实例化方法装饰器
typescript
function Log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`调用 ${propertyKey},参数:`, args);
const result = originalMethod.apply(this, args);
console.log(`${propertyKey} 返回:`, result);
return result;
};
return descriptor;
}
class Calculator {
@Log
add(a: number, b: number): number {
return a + b;
}
}
const calc = new Calculator();
calc.add(2, 3);
// 输出:
// 调用 add,参数: [2, 3]
// add 返回: 5工具类型
常用工具类型
typescript
interface User {
id: number;
name: string;
email: string;
age?: number;
}
// Partial - 所有属性可选
type PartialUser = Partial<User>;
// Required - 所有属性必需
type RequiredUser = Required<User>;
// Readonly - 所有属性只读
type ReadonlyUser = Readonly<User>;
// Pick - 选择部分属性
type UserBasic = Pick<User, "id" | "name">;
// Omit - 排除部分属性
type UserWithoutEmail = Omit<User, "email">;
// Record - 创建对象类型
type UserMap = Record<number, User>;
// Extract / Exclude - 类型过滤
type Status = "active" | "inactive" | "pending";
type ActiveStatus = Extract<Status, "active" | "pending">;
type InactiveStatus = Exclude<Status, "active">;自定义工具类型
typescript
// 深度只读
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object
? DeepReadonly<T[K]>
: T[K];
};
// 非空属性
type NonNullableFields<T> = {
[K in keyof T]: NonNullable<T[K]>;
};
// 函数参数类型
type FunctionParams<T> = T extends (...args: infer P) => any ? P : never;类型守卫
类型守卫函数
typescript
interface Dog {
kind: "dog";
bark(): void;
}
interface Cat {
kind: "cat";
meow(): void;
}
type Animal = Dog | Cat;
// 类型守卫
function isDog(animal: Animal): animal is Dog {
return animal.kind === "dog";
}
function handleAnimal(animal: Animal) {
if (isDog(animal)) {
animal.bark(); // TypeScript 知道这是 Dog
} else {
animal.meow(); // TypeScript 知道这是 Cat
}
}in 运算符
typescript
interface Admin {
role: "admin";
permissions: string[];
}
interface User {
role: "user";
profile: object;
}
function handlePerson(person: Admin | User) {
if ("permissions" in person) {
console.log(person.permissions); // Admin
} else {
console.log(person.profile); // User
}
}构建 TypeScript 项目
编译输出
bash
# 使用 Bun 打包(转译为 JavaScript)
bun build src/index.ts --outdir dist
# 生成类型定义
bun tsc --emitDeclarationOnly完整构建流程
json
{
"scripts": {
"typecheck": "tsc --noEmit",
"build": "bun run typecheck && bun build src/index.ts --outdir dist --target node",
"build:types": "tsc --emitDeclarationOnly --outDir dist",
"prepublish": "bun run build && bun run build:types"
}
}小结
本章介绍了:
- ✅ Bun 原生 TypeScript 支持
- ✅ tsconfig.json 配置
- ✅ TSX/JSX 支持
- ✅ 类型检查和类型导入
- ✅ 泛型和装饰器
- ✅ 工具类型和类型守卫
- ✅ 构建 TypeScript 项目
下一步
继续阅读 环境变量 了解 Bun 的环境配置管理。