# @snap/ts-inject

> 100% typesafe dependency injection framework for TypeScript projects

Latest version **1.1.0** (published 2026-09-03) · MIT license · 0 weekly downloads

## Install

```sh
npm install @snap/ts-inject
pnpm add @snap/ts-inject
yarn add @snap/ts-inject
bun add @snap/ts-inject
```

## Health

**Score 75/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; has provenance; recently updated; high maintenance score; high quality score.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 1.1.0 |
| Published | 2026-09-03 |
| First published | 2024-08-14 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 0 |
| Unpacked size | 288.5 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Provenance | attested (GitHub Actions) |
| GitHub stars | 22 |
| Author | Snap Inc. |
| Maintainers | svc-npmjs, msilivonik-sc, kburov, carsonholgate |
| Keywords | TypeScript, typesafe, Dependency Injection, DI, Inversion of Control, IoC, Snap, Snapchat |

## Links

- npm: https://www.npmjs.com/package/@snap/ts-inject
- Repository: https://github.com/Snapchat/ts-inject
- Homepage: https://snapchat.github.io/ts-inject/
- Issues: https://github.com/Snapchat/ts-inject/issues
- npm.io page: https://npm.io/package/@snap/ts-inject

## Alternatives

- [memory-cache](https://npm.io/package/memory-cache.md) — 795.0K weekly downloads
- [@httptoolkit/proxy-agent](https://npm.io/package/@httptoolkit/proxy-agent.md) — 11.2K weekly downloads
- [express-cache-controller](https://npm.io/package/express-cache-controller.md) — 5.3K weekly downloads
- [http-cache-middleware](https://npm.io/package/http-cache-middleware.md) — 4.5K weekly downloads
- [cache2](https://npm.io/package/cache2.md) — 1.5K weekly downloads

## Recent versions

- 1.1.0 (latest) — 2026-09-03
- 1.0.0-alpha.1 (alpha) — 2026-05-20
- 0.0.1-dev-0 (internal-compat) — 2024-11-30
- 1.0.1 — 2026-07-03
- 1.0.0 — 2026-05-20
- 0.4.0 — 2026-03-11
- 0.3.2 — 2024-11-28
- 0.3.1 — 2024-11-20
- 0.3.0 — 2024-11-04
- 0.2.0 — 2024-09-19
- 0.1.2 — 2024-09-09
- 0.1.2-alpha.1 — 2024-09-09
- 0.2.0-alpha.1 — 2024-09-08
- 0.1.1 — 2024-09-06
- 0.1.0 — 2024-08-14

## README

# ts-inject

`ts-inject` is a 100% typesafe dependency injection framework for TypeScript projects, designed to enhance code sharing and modularity by ensuring compile-time dependency resolution. This framework leverages the dependency injection design pattern to decouple dependency usage from creation, allowing components to rely on interfaces rather than implementations.

## Features and Alternatives

`ts-inject` brings typesafety to dependency injection, setting it apart from a vast majority of frameworks, like [InversifyJS](https://github.com/inversify/InversifyJS), which operate at runtime and therefore lack this level of typesafety.

While [typed-inject](https://github.com/nicojs/typed-inject) also prioritizes typesafety, it lacks several key features that `ts-inject` offers:

- **Overcomes TypeScript Nested Type Limitations**: Unlike some frameworks, `ts-inject` navigates around [TypeScript's limits on nested types](https://github.com/nicojs/typed-inject/issues/22), making it more robust for complex applications.
- **Composable Containers**: `ts-inject` enables merging multiple containers, facilitating greater modularity and code reuse.
- **PartialContainer**: It allows service registration without pre-defined dependencies, offering more flexibility compared to regular containers.

## Getting Started

### Installation

```bash
npm install @snap/ts-inject
```

### Sample Usage

This quick start guide demonstrates how to define services, register them in a container, and then retrieve them for use.

#### Defining Services

Define a couple of services. For simplicity, we'll use a `Logger` service and a `Database` service, where `Database` depends on `Logger` for logging purposes.

```ts
class Logger {
  log(message: string) {
    console.log(`Log: ${message}`);
  }
}

class Database {
  static dependencies = ["Logger"] as const;
  constructor(private logger: Logger) {}

  save(record: string) {
    this.logger.log(`Saving record: ${record}`);
  }
}
```

#### Setting Up the Container

With `ts-inject`, you can set up a container to manage these services using `providesValue` and `providesClass`:

```ts
import { Container } from "@snap/ts-inject";

const container = Container.providesValue("Logger", new Logger()).providesClass("Database", Database);

const db = container.get("Database");
db.save("user1"); // Log: Saving record: user1
```

#### Lazy Class Lookup

`providesClass` accepts both lazy and eager class registration:

```ts
container.providesClass("Database", () => Database); // class lookup occurs on first resolution
container.providesClass("Database", Database); // dependency metadata is read during registration
```

The thunk form defers class lookup through `Container` and `PartialContainer` registration and composition until the
service is first resolved. `appendClass("plugins", () => Plugin)` supports the same form.

This is especially useful with cache-on-first-read module namespaces, where reading an export can trigger module
evaluation. Ordinary static ESM imports are different: native ESM evaluates imported modules before the importing
module runs, so wrapping an already imported binding in `() => Database` cannot defer native ESM module evaluation by
itself. It only defers the class lookup performed by `ts-inject`.

#### Inline Factory Functions

When a service needs custom creation logic, pass a factory function directly to `provides`:

```ts
import { Container } from "@snap/ts-inject";

// Zero-dependency lazy factory
const container = Container.provides("Logger", () => new Logger());

// Factory with dependencies — tokens are resolved from the container
const appContainer = container
  .providesValue("apiUrl", "https://api.example.com")
  .provides("httpClient", ["apiUrl"] as const, (url: string) => createHttpClient(url));
```

For most services, use `providesValue` (eager values), `providesClass` (classes with `static dependencies`), or the
inline `provides` form above. The `Injectable()` helper is only needed when you need a reusable factory object — for
example, to pass to `run()` for eager initialization.

#### Composable Containers

`ts-inject` supports composable containers, allowing you to modularize service registration:

```ts
const baseContainer = Container.providesValue("Logger", new Logger());
const appContainer = baseContainer.providesClass("Database", Database);

const db = appContainer.get("Database");
db.save("user2"); // Log: Saving record: user2
```

> **Note:** Each registration method (`provides`, `providesValue`, `providesClass`, etc.)
> returns a **new child container** — the original container is never modified.
> Always use the returned value; calls whose return value is discarded have no effect.

You can also bootstrap a container from a plain object with `fromObject`:

```ts
const configContainer = Container.fromObject({ apiUrl: "https://api.example.com", timeout: 5000 });
```

#### Multi-Binding

Containers support appending to array-typed services, useful for plugin systems and extensible pipelines:

```ts
const container = Container.providesValue("plugins", [] as Plugin[])
  .appendClass("plugins", AuthPlugin)
  .appendClass("plugins", LoggingPlugin)
  .appendValue("plugins", { name: "inline", run: () => {} });

container.get("plugins"); // [AuthPlugin, LoggingPlugin, { name: "inline", ... }]
```

### Key Concepts

- **Container**: A registry for all services, handling their creation and retrieval.
- **PartialContainer**: Similar to a Container but allows services to be registered without defining all dependencies upfront. Unlike a regular Container, it does not support retrieving services directly.
- **Service**: Any value or instance provided by the Container.
- **Token**: A unique identifier for each service, used for registration and retrieval within the Container.
- **InjectableClass**: Classes that can be instantiated by the Container. Dependencies are specified in a static `dependencies` field to enable automatic injection via `providesClass`.
- **InjectableFunction**: A reusable factory object created by `Injectable()`. Rarely needed directly — prefer the inline `provides('token', factory)` form. Use `Injectable()` when you need to store or pass a factory to `run()`.

### API Reference

For comprehensive documentation of all ts-inject features and APIs, please refer to the [API Reference](https://snapchat.github.io/ts-inject/).

## Contributing

[Contributing guide](CONTRIBUTING.md).

## License

`ts-inject` is published under [MIT license](LICENSE.md).

## Project Origins

`ts-inject` originated as an internal project at [Snap Inc.](https://snap.com/), developed by [Weston Fribley](https://github.com/wfribley). Inspired by the principles of [typed-inject](https://github.com/nicojs/typed-inject), it was designed to address the limitations of existing dependency injection frameworks and improve typesafe dependency resolution in TypeScript. Initially aimed at enhancing [CameraKit](https://www.npmjs.com/package/@snap/camera-kit)'s codebase, its success led to its adoption across various teams at [Snap Inc.](https://snap.com/), and now it has evolved into an open-source project to benefit the wider TypeScript community.

---
_Source: https://npm.io/package/@snap/ts-inject · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
