npm.io
1.2.0 • Published 2d ago

ts-fake

Licence
MIT
Version
1.2.0
Deps
0
Size
19 kB
Vulns
0
Weekly
9
Stars
1

ts-fake

npm version npm downloads License: MIT CI Coverage Bundle Size TypeScript

Type-safe test utility for creating fakes of TypeScript interfaces and objects in unit tests.

Installation

npm install --save-dev ts-fake

Quick Start

import { fake } from "ts-fake";

interface User {
  id: number;
  name: string;
  email: string;
}

// Create a type-safe fake with only the properties you need
const fakeUser = fake<User>({
  id: 1,
  name: "Test User",
});

// Use in your tests - email is undefined but type-safe
expect(fakeUser.id).toBe(1);
expect(fakeUser.name).toBe("Test User");

Features

  • Type-safe: Full TypeScript support with compile-time type checking
  • Deep partial support: Nested objects work seamlessly
  • Minimal boilerplate: Only specify the properties you need for your test
  • Zero dependencies: Lightweight and fast
  • Framework agnostic: Works with any testing framework (Jest, Vitest, Mocha, etc.)

Usage

Basic Example
import { fake } from "ts-fake";

interface Product {
  id: string;
  name: string;
  price: number;
  inStock: boolean;
}

// Only provide the fields you need for your test
const testProduct = fake<Product>({
  id: "test-123",
  name: "Test Product",
});

// Use in your tests
expect(testProduct.id).toBe("test-123");
Nested Objects
interface Address {
  street: string;
  city: string;
  country: string;
}

interface Customer {
  id: string;
  name: string;
  address: Address;
}

// Deep partial support - only specify what you need
const testCustomer = fake<Customer>({
  id: "cust-123",
  name: "Jane Smith",
  address: {
    city: "New York",
  },
});
Advanced Usage
// Arrays and complex structures
interface Product {
  id: string;
  name: string;
}

interface Order {
  id: string;
  user: User;
  items: Product[];
  total: number;
}

const testOrder = fake<Order>({
  id: "order-1",
  user: {
    id: 1,
    name: "Test User",
  },
  items: [
    { id: "prod-1", name: "Widget" },
    { id: "prod-2", name: "Gadget" },
  ],
});

API Documentation

fake<T>(partial?: DeepPartial<T>): T

Creates a type-safe fake object of type T.

Parameters:

  • partial (optional): A deep partial object containing the properties you want to set. If omitted, returns an empty object typed as T.

Returns:

  • A complete object of type T with the provided properties

Type Safety:

  • TypeScript will enforce that all provided properties match the interface
  • The returned object is typed as T, allowing it to be used anywhere T is expected
  • Supports deep partial objects - you can partially specify nested properties

Example:

// Empty fake
const emptyUser = fake<User>();

// Partial fake
const partialUser = fake<User>({ id: 1 });

// Deep partial fake
const customer = fake<Customer>({
  address: { city: "NYC" }, // Only city, not full Address
});

Why Use ts-fake?

Creating test data in TypeScript often involves choosing between several imperfect approaches. ts-fake provides a better alternative.

The Problem
interface User {
  id: number;
  name: string;
  email: string;
  createdAt: Date;
  updatedAt: Date;
  preferences: UserPreferences;
  roles: Role[];
}

// Your test only needs id and name
Alternative Approaches (and their problems)

1. Type Assertion via unknown: {} as unknown as T

const user = { id: 1, name: "Test" } as unknown as User;

Bypasses all type checking - typos won't be caught
Can pass invalid properties without errors
No IDE autocomplete

2. Using Partial<T> directly

const user: Partial<User> = { id: 1, name: "Test" };
doSomething(user); // ❌ Type error: Partial<User> is not assignable to User

Can't pass to functions expecting User
Requires casting anyway: user as User
Loses type safety when casting

3. Providing the full interface

const user: User = {
  id: 1,
  name: "Test",
  email: "test@example.com",
  createdAt: new Date(),
  updatedAt: new Date(),
  preferences: { theme: "dark", language: "en" },
  roles: [],
};

Verbose and tedious
Brittle - breaks when interface changes
Obscures what the test actually needs

4. Refactoring with Interface Segregation Principle (ISP)

interface UserIdentity {
  id: number;
  name: string;
}

function doSomething(user: UserIdentity) { ... }

Not always practical - you don't control all interfaces
Over-engineering for test purposes
Doesn't help with third-party types

The ts-fake Solution
const user = fake<User>({ id: 1, name: "Test" });
doSomething(user); // ✅ Works perfectly

Type-safe: Properties are validated against the interface
Minimal: Only specify what your test needs
Flexible: Works with any interface, including third-party types
Maintainable: Tests don't break when unused properties change
Clear intent: Shows exactly what the test depends on
IDE support: Full autocomplete and type checking

Compatibility

Tool Supported Verified in CI
TypeScript >=4.7.0 4.7.2¹, 5.0.2² and latest
Node.js >=20.0.0 20.x, 22.x, 24.x, 26.x

¹ 4.7 is the floor because stable resolution of the package's exports-conditioned type declarations (node16/nodenext) landed in TypeScript 4.7. 4.7.2 is the lowest installable release (no 4.7.0/4.7.1 stable was published). ² TypeScript never published a 5.0.0/5.0.1 stable, so 5.0.2 is the lowest installable release at that point in the range.

TypeScript is a peer dependency — ts-fake uses whatever compiler (4.7+) your project already has. On every change, CI validates that the published type declarations resolve correctly across module systems (are-the-types-wrong). It also type-checks consumer fixtures that import the package by name under node16 resolution at the 4.7 floor — exercising the exports map exactly as a consumer would — and compiles the examples under bundler resolution against the top of the range.

Contributing

Contributions are welcome! See CONTRIBUTING.md for guidelines.

License

MIT Jason Duffett

Keywords