# How Freebuff Handles Strict Typing with TypeScript: A Layered Type Safety Architecture

> Learn how Freebuff achieves strict typing with TypeScript using a layered architecture. Discover its approach to type safety, project references, and Zod runtime validation.

- Repository: [Codebuff/freebuff](https://github.com/CodebuffAI/freebuff)
- Tags: architecture
- Published: 2026-09-01

---

**Freebuff enforces rigorous type safety through a layered strategy combining TypeScript's strict compiler settings in [`tsconfig.base.json`](https://github.com/CodebuffAI/freebuff/blob/main/tsconfig.base.json), project references for cross-package validation, and Zod runtime schemas using `z.strictObject` to prevent data drift.**

Freebuff implements a "strict-by-default" typing model that guards against errors from development through production. By layering static analysis with runtime validation, the open-source AI agent framework ensures that type contracts remain intact across its monorepo architecture. This article examines the specific implementation details in the CodebuffAI/freebuff repository that enable comprehensive TypeScript strict typing.

## Compile-Time Strictness via tsconfig.base.json

Freebuff centralizes its type enforcement in the root [`tsconfig.base.json`](https://github.com/CodebuffAI/freebuff/blob/main/tsconfig.base.json) file, ensuring all packages inherit identical compiler safeguards.

### Strict Compiler Flags

The base configuration enables `strict: true`, which automatically activates `strictNullChecks`, `noImplicitAny`, `noImplicitReturns`, and `strictFunctionTypes`. Additional safeguards include `forceConsistentCasingInFileNames` to prevent cross-platform import errors. All project sub-configs extend this base file, meaning every package—whether `common`, `agents`, or `sdk`—compiles under identical strict rules.

```json
// tsconfig.base.json
{
  "compilerOptions": {
    "strict": true,
    "noImplicitReturns": true,
    "forceConsistentCasingInFileNames": true
  }
}

```

## Cross-Package Type Safety with Project References

The top-level [`tsconfig.json`](https://github.com/CodebuffAI/freebuff/blob/main/tsconfig.json) extends the base configuration and establishes a project reference graph that maintains type boundaries across package boundaries.

### Path Aliases and References

The configuration defines path aliases (`@codebuff/*`) that enable TypeScript to type-check imports across package boundaries. The `references` section treats each package as a separate compilation unit while preserving type safety across the monorepo. This prevents leaky abstractions where one package might inadvertently bypass another's type constraints.

```json
// tsconfig.json
{
  "extends": "./tsconfig.base.json",
  "compilerOptions": {
    "paths": {
      "@codebuff/*": ["packages/*/src"]
    }
  },
  "references": [
    { "path": "./packages/common" },
    { "path": "./packages/sdk" }
  ]
}

```

## Runtime Validation with Zod Schemas

Static typing alone cannot validate external data. Freebuff supplements compile-time checks with **Zod** schemas that enforce strict object shapes at runtime.

### Strict Object Validation

In [`common/src/types/mcp.ts`](https://github.com/CodebuffAI/freebuff/blob/main/common/src/types/mcp.ts), critical configuration objects use `z.strictObject` rather than `z.object`. This rejects unknown keys at runtime, preventing drift between TypeScript types and actual data received from external services or environment variables.

```typescript
// common/src/types/mcp.ts
import { z } from 'zod/v4';

export const mcpConfigSchema = z.strictObject({
  type: z.enum(['stdio', 'http']),
  command: z.string(),
  args: z.array(z.string())
});

// Runtime validation rejects extra properties
const parsed = mcpConfigSchema.parse({
  type: 'stdio',
  command: 'node',
  args: ['app.js'],
  unexpected: true  // Throws ZodError
});

```

## Utility Functions for Schema Debugging

Freebuff includes helper utilities to bridge the gap between Zod schemas and developer tooling.

### Schema Serialization

The `schemaToJsonStr` function in [`common/src/util/zod-schema.ts`](https://github.com/CodebuffAI/freebuff/blob/main/common/src/util/zod-schema.ts) converts Zod schemas into clean JSON representations. It strips internal `$schema` meta-properties to produce focused, shareable type documentation that matches the runtime contract exactly.

```typescript
// common/src/util/zod-schema.ts
export function schemaToJsonStr(schema: z.ZodTypeAny): string {
  const json = zodToJsonSchema(schema);
  delete (json as Record<string, unknown>).$schema;
  return JSON.stringify(json, null, 2);
}

```

## SDK-Level Type Contracts

The Freebuff SDK exposes only strictly typed interfaces derived from Zod schemas, ensuring consumers receive IntelliSense while maintaining runtime validation.

### Typed Public APIs

In [`sdk/src/tools/read-files.ts`](https://github.com/CodebuffAI/freebuff/blob/main/sdk/src/tools/read-files.ts) and related entry points, the SDK re-exports inferred types alongside their validation schemas. This creates "defensive programming" boundaries where callers get compile-time autocomplete, but the underlying implementation still validates input using the Zod parser.

```typescript
// sdk/src/index.ts
export { mcpConfigSchema as MCPConfigSchema } from '@codebuff/common/src/types/mcp';
export type MCPConfig = z.infer<typeof MCPConfigSchema>;

// Consumer usage gets both IntelliSense and runtime safety
import { MCPConfig } from '@codebuff/sdk';

const cfg: MCPConfig = { type: 'http', url: 'https://example.com' };

```

## Summary

- **Strict inheritance**: [`tsconfig.base.json`](https://github.com/CodebuffAI/freebuff/blob/main/tsconfig.base.json) enforces `strict: true` across all packages, preventing `any` types and unchecked nulls.
- **Monorepo safety**: Project references and `@codebuff/*` path aliases maintain type boundaries between packages.
- **Runtime enforcement**: `z.strictObject` in [`common/src/types/mcp.ts`](https://github.com/CodebuffAI/freebuff/blob/main/common/src/types/mcp.ts) rejects unknown keys, preventing configuration drift.
- **Developer tooling**: The `schemaToJsonStr` utility converts schemas to clean JSON for debugging and documentation.
- **Dual validation**: SDK functions expose Zod-derived types that provide both compile-time IntelliSense and runtime parsing guarantees.

## Frequently Asked Questions

### What specific TypeScript strict flags does Freebuff enable?

Freebuff enables `strict: true` in [`tsconfig.base.json`](https://github.com/CodebuffAI/freebuff/blob/main/tsconfig.base.json), which activates `strictNullChecks`, `noImplicitAny`, `noImplicitReturns`, and `strictFunctionTypes`. It also explicitly sets `forceConsistentCasingInFileNames` to ensure import paths match file system casing across operating systems.

### How does Freebuff prevent runtime data from violating TypeScript types?

Freebuff uses Zod schemas with `z.strictObject` (as seen in [`common/src/types/mcp.ts`](https://github.com/CodebuffAI/freebuff/blob/main/common/src/types/mcp.ts)) to parse external data. Unlike standard `z.object`, `strictObject` throws errors when unexpected properties exist, ensuring that runtime data conforms exactly to the TypeScript interface definitions.

### Why does Freebuff use project references instead of a single tsconfig?

Project references allow TypeScript to treat each package (`common`, `sdk`, `agents`) as a separate compilation unit while maintaining type safety across boundaries. This structure enforces that changes in one package correctly propagate type errors to dependent packages without requiring complete rebuilds of the entire monorepo.

### Where does the SDK expose typed interfaces derived from Zod schemas?

The SDK exposes these interfaces in entry points like [`sdk/src/index.ts`](https://github.com/CodebuffAI/freebuff/blob/main/sdk/src/index.ts) and specific tool files such as [`sdk/src/tools/read-files.ts`](https://github.com/CodebuffAI/freebuff/blob/main/sdk/src/tools/read-files.ts). These files export both the Zod schema objects and their inferred TypeScript types, allowing consumers to use `z.infer<typeof Schema>` for compile-time checking while the schema handles runtime validation.