Right, so you’ve written your beautiful library, your types are a work of art, and you’ve run tsc and tsd and everything passes. You’re feeling pretty good, right? Don’t. You’ve only tested your types in situ, from the source. The real world is a much, much darker place. Your meticulously crafted .d.ts files get bundled, transformed, and ultimately consumed by a user’s project in ways that can make a complete mockery of your original intentions. This is where @arethetypeswrong/cli (or attw for short) comes in—it’s the final, brutal honesty your library needs before it faces the public.

Think of attw as a smoke test for your published package. It doesn’t just check if the types are syntactically correct; it installs your package fresh from the registry (or a tarball) and then performs a series of checks to see if the types actually work for the consumer. It answers the critical question: “If I npm install your-package, will I get usable types, or a silent, soul-crushing any?”

Why Your Source Types Lie to You

You might be thinking, “But my build process outputs declaration files! What’s the problem?” The problem is the gap between your source code and the final published artifact. Here are the usual suspects:

  • Bundlers: If you use tsup, rollup, or webpack with a TypeScript plugin, they’re generating your final .d.ts files, not the TypeScript compiler directly. They can and do make different choices.
  • Incorrect package.json Entries: You absolutely must point to your types correctly. attw will mercilessly call you out if your "types" field points to a non-existent file or, heaven forbid, to your source .ts files (which aren’t included in the publish).
  • Re-exporting Nightmares: Using export * from './internal/module' is convenient until it breaks type resolution in the consumer’s project. attw identifies these “unresolved” imports.

Installing and Running the Truth-Teller

Enough theory. Let’s get it running. You’ll want to install it globally or use npx for a one-off check.

# The easy way
npx @arethetypeswrong/cli@latest your-package-name

# Or install it for frequent use
npm install -g @arethetypeswrong/cli
attw your-package-name

But here’s the pro-tip: don’t just test the published version on npm. Test the exact tarball you’re about to publish. Build your package, create the tarball, and point attw at it.

# 1. Build your package and pack it
npm run build
npm pack

# 2. This creates a .tgz file. Feed it to attw.
attw ./your-package-name-1.0.0.tgz

This is your last line of defense. If it passes here, you can publish with confidence.

Deciphering the Output: It’s Not Just Pass/Fail

Running attw will give you a beautifully color-coded table. Green good, red bad. But it’s the specific errors that matter. Let’s break down the most common ones you’ll see.

The Dreaded “Bundled” Warning

This is arguably the most important check. It tells you if your type declarations are “bundled,” meaning all your type definitions are mashed into a few large .d.ts files, or if they mirror your actual source module structure.

$ attw my-bundled-library.tgz
# ... look for this ...
✗ Detected file: lib/index.d.ts
‼️ Bundled types: This library's types appear to be bundled.

Why is this bad? Because it breaks type resolution for your users. If they try to use a helper type from one of your sub-modules, TypeScript might not be able to find it. It also utterly demolishes your library’s ability to be treeshaken. The fix? Configure your bundler to preserve the module structure. In tsup, for example, it’s the dts entry point option:

// tsup.config.js
export default {
  entry: ['src/index.ts'],
  // This tells tsup to generate a .d.ts file for each entry point
  dts: {
    entry: ['src/index.ts'],
  },
  // ... other options
};

“False ESM” and Other package.json Sins

This is where attw acts as a package.json linter. It will check if your "types" field is correct and, crucially, if you’re correctly signaling whether your package is ESM or CommonJS.

A classic mistake is publishing a dual-mode package (with "type": "module") but forgetting to specify the ESM entry point. attw will call this “False CJS” or “False ESM.”

// Your package.json MUST be explicit. For a dual-mode package:
{
  "name": "my-library",
  "type": "module",
  "main": "./dist/index.cjs",
  "types": "./dist/index.d.cts", // Types for CommonJS entry
  "exports": {
    ".": {
      "import": {
        "types": "./dist/index.d.ts", // Types for ESM entry
        "default": "./dist/index.js"
      },
      "require": {
        "types": "./dist/index.d.cts",
        "default": "./dist/index.cjs"
      }
    }
  }
}

If attw finds a mismatch here, it will tell you exactly what’s wrong. Heed its warning. This is the number one cause of “I installed the types but nothing is working!” issues.

Making It Part of Your CI Pipeline

You’re not going to remember to run this manually every time, are you? Of course not. Automate it. The easiest way is to use the CLI in your CI script. A failing attw check should break the build.

# .github/workflows/test-publish.yml
- name: Build package
  run: npm run build

- name: Create tarball
  run: npm pack

- name: Check types with attw
  run: npx @arethetypeswrong/cli@latest ./your-package-*.tgz

Consider this non-negotiable. It’s the difference between a library that feels professional and one that leaks type errors all over your users’ consoles. It’s the final, crucial step in shipping types you can actually be proud of.