Bun TypeScript Support
Bun has native TypeScript support, allowing you to run .ts and .tsx files directly without any configuration. This chapter introduces Bun's TypeScript features and best practices.
Out of the Box
Running TypeScript Directly
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: "Zhang San",
email: "zhangsan@example.com"
};
console.log(greet(user));Run:
bash
bun app.ts
# Output: Hello, Zhang San!No need to:
- Install typescript
- Configure ts-node
- Pre-compilation step
TypeScript Configuration
Auto-generate tsconfig.json
bash
# Auto-create during project initialization
bun init
# Or create manually
bun tsc --initRecommended Configuration
json
{
"compilerOptions": {
// Target and module
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
// Strict mode
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
// Path configuration
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
},
// JSX support
"jsx": "react-jsx",
// Other options
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
// Output directory
"outDir": "./dist",
"rootDir": "./src",
// Bun types
"types": ["bun-types"]
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}Bun Type Definitions
bash
# Install Bun type definitions
bun add -d @types/bunGet complete Bun API type hints:
typescript
// Now you have complete type support
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!");
}
});Type Checking
Running Type Checks
Bun runtime does not perform type checking (for speed), you need to run it separately:
bash
# Use TypeScript compiler to check
bun tsc --noEmit
# Or configure in package.json
{
"scripts": {
"typecheck": "tsc --noEmit",
"dev": "bun --watch src/index.ts",
"build": "bun run typecheck && bun build src/index.ts --outdir dist"
}
}Type Error Example
typescript
// error.ts
function add(a: number, b: number): number {
return a + b;
}
// TypeScript will report an error, but Bun will still run
add("1", 2); // Type errorbash
# Bun will run (ignoring type errors)
bun error.ts
# TypeScript check will report errors
bun tsc --noEmit
# error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.TSX / JSX Support
React Components
tsx
// App.tsx
import React from "react";
interface Props {
name: string;
age?: number;
}
const Greeting: React.FC<Props> = ({ name, age }) => {
return (
<div>
<h1>Hello, {name}!</h1>
{age && <p>Age: {age}</p>}
</div>
);
};
export default Greeting;Server-Side Rendering
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 Configuration
json
// tsconfig.json
{
"compilerOptions": {
// React 17+ new JSX transform
"jsx": "react-jsx",
"jsxImportSource": "react",
// Or use classic transform
// "jsx": "react",
// Use Preact
// "jsx": "react-jsx",
// "jsxImportSource": "preact"
}
}Type Imports
Importing Types
typescript
// types.ts
export interface User {
id: number;
name: string;
}
export type Status = "active" | "inactive" | "pending";
// main.ts
// Type import (not included in runtime code)
import type { User, Status } from "./types";
// Or mixed import
import { type User, type Status } from "./types";
function processUser(user: User, status: Status) {
console.log(`${user.name}: ${status}`);
}Inline Type Imports
typescript
// Ensure types are only used in type context
import { type User } from "./types";Generics
Generic Functions
typescript
function identity<T>(value: T): T {
return value;
}
const num = identity<number>(42);
const str = identity("hello"); // Type inference
// Generic constraints
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { name: "Zhang San", age: 25 };
const name = getProperty(user, "name"); // stringGeneric Classes
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");Generic Interfaces
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;
}
}Decorators
Enabling Decorators
json
// tsconfig.json
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}Class Decorators
typescript
function Logger(constructor: Function) {
console.log(`Class ${constructor.name} was created`);
}
@Logger
class MyService {
constructor() {
console.log("MyService instantiated");
}
}
// Output:
// Class MyService was created
// MyService instantiatedMethod Decorators
typescript
function Log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`Calling ${propertyKey}, args:`, args);
const result = originalMethod.apply(this, args);
console.log(`${propertyKey} returned:`, 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);
// Output:
// Calling add, args: [2, 3]
// add returned: 5Utility Types
Common Utility Types
typescript
interface User {
id: number;
name: string;
email: string;
age?: number;
}
// Partial - all properties optional
type PartialUser = Partial<User>;
// Required - all properties required
type RequiredUser = Required<User>;
// Readonly - all properties readonly
type ReadonlyUser = Readonly<User>;
// Pick - select some properties
type UserBasic = Pick<User, "id" | "name">;
// Omit - exclude some properties
type UserWithoutEmail = Omit<User, "email">;
// Record - create object type
type UserMap = Record<number, User>;
// Extract / Exclude - type filtering
type Status = "active" | "inactive" | "pending";
type ActiveStatus = Extract<Status, "active" | "pending">;
type InactiveStatus = Exclude<Status, "active">;Custom Utility Types
typescript
// Deep readonly
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object
? DeepReadonly<T[K]>
: T[K];
};
// Non-nullable fields
type NonNullableFields<T> = {
[K in keyof T]: NonNullable<T[K]>;
};
// Function parameter types
type FunctionParams<T> = T extends (...args: infer P) => any ? P : never;Type Guards
Type Guard Functions
typescript
interface Dog {
kind: "dog";
bark(): void;
}
interface Cat {
kind: "cat";
meow(): void;
}
type Animal = Dog | Cat;
// Type guard
function isDog(animal: Animal): animal is Dog {
return animal.kind === "dog";
}
function handleAnimal(animal: Animal) {
if (isDog(animal)) {
animal.bark(); // TypeScript knows this is Dog
} else {
animal.meow(); // TypeScript knows this is Cat
}
}in Operator
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
}
}Building TypeScript Projects
Compilation Output
bash
# Use Bun to bundle (transpile to JavaScript)
bun build src/index.ts --outdir dist
# Generate type definitions
bun tsc --emitDeclarationOnlyComplete Build Process
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"
}
}Summary
This chapter introduced:
- ✅ Bun's native TypeScript support
- ✅ tsconfig.json configuration
- ✅ TSX/JSX support
- ✅ Type checking and type imports
- ✅ Generics and decorators
- ✅ Utility types and type guards
- ✅ Building TypeScript projects
Next Steps
Continue reading Environment Variables to learn about Bun's environment configuration management.