@react-dnd/invariant
`@react-dnd/invariant` is the one-function assertion helper used inside React DnD. Call `invariant(condition, message, ...values)` and it returns when the condition is truthy; otherwise it throws an `Error` named `Invariant Violation`, replacing each `%s` in the message with the next value. It has no runtime dependencies and is unrelated to drag-and-drop behavior. Its millions of downloads are mainly a consequence of React DnD depending on it, not evidence that application developers need to install it directly.
Do not add this package merely because its download count looks reassuring; it is a React DnD implementation detail with stale published packaging and weak TypeScript semantics. Keep it only where exact React DnD compatibility is the requirement.
Use it if
- You contribute to React DnD or maintain code already standardized on its exact Invariant Violation error behavior
- You need a dependency-free runtime assertion with only `%s` substitution and do not need TypeScript control-flow narrowing
- You are preserving compatibility with existing tests or error handling that checks the React DnD helper's error name
- You are choosing a new general-purpose assertion helper: this package has one function, no package README, and its npm description is only `invariantx`
- You rely on TypeScript narrowing after an assertion: the published declaration returns void instead of using an `asserts condition` signature
- You need CommonJS or a modern exports map: npm 4.0.2 is ESM-only with `type: module`, while the repository's later dual ESM/CommonJS package layout was never published
- You want printf-style formatting beyond `%s`: the implementation only replaces `%s`, consumes arguments in order, and leaves other tokens untouched
- You expect production error messages to be stripped as the source comment claims: the published code still constructs the formatted message, while its production-only branch instead throws if the message argument is omitted
Setup reality
Installation has no native build, peer dependency, configuration file, or runtime dependency. The sharp edges are all about choosing an internal utility as a direct dependency. Version 4.0.2 is an ESM package whose `main` points to `dist/index.js`; older `require()` test runners and CommonJS applications cannot consume it normally. The React DnD repository later changed this workspace package to emit CommonJS and ESM with an exports map, but npm still serves the April 2022 package, so copying configuration from the current repository describes artifacts users do not receive. There is no package-specific README or documentation site. The message is mandatory in the TypeScript declaration, and it should always be passed at runtime too: in production, `invariant(true, undefined)` throws before it even checks the true condition, while development does not. Only `%s` is interpolated, extra arguments are ignored, and missing arguments become the string `undefined`. Most importantly, the declaration is `(condition: any, format: string, ...args: any[]) => void`, not an assertion signature, so TypeScript will not narrow nullable values after the call. Wrap it in your own typed assertion if compatibility forces you to use it. For new code, `tiny-invariant` or a small local `asserts` function is a cleaner contract and avoids depending on React DnD's private utility release cadence.
Patterns
Assert an internal conditionassert-condition
import { invariant } from '@react-dnd/invariant';
invariant(items.length > 0, 'Expected at least one item');
process(items[0]);Use this for programmer assumptions, not user-input validation. A false condition throws synchronously with the name Invariant Violation.
Insert a value into the error messageformat-error-value
invariant(
handlers.has(handlerId),
'Expected handler %s to be registered',
handlerId,
);Only `%s` is supported. Tokens such as `%d`, `%j`, or named placeholders are not interpreted.
Insert multiple values in orderformat-multiple-values
invariant(
sourceType === targetType,
'Cannot connect source %s to target %s',
sourceType,
targetType,
);Each `%s` consumes the next argument. Too few values put `undefined` into the message; extra values are ignored.
Add the TypeScript narrowing the package lacksassert-defined-with-types
import { invariant } from '@react-dnd/invariant';
function assertDefined<T>(
value: T,
label: string,
): asserts value is NonNullable<T> {
invariant(value != null, '%s must be defined', label);
}
assertDefined(currentUser, 'currentUser');
console.log(currentUser.id);The wrapper's `asserts` return type performs narrowing. Calling invariant directly does not narrow because version 4.0.2 declares a void return.
Reject an out-of-range lookupassert-array-member
const item = items[index];
invariant(item !== undefined, 'No item at index %s', index);
// TypeScript may still consider item undefined here.
handle(item!);The non-null assertion is still needed with strict indexed access. Prefer a local typed assertion wrapper if this pattern occurs often.
Recognize the thrown errorinspect-invariant-error
try {
invariant(config.ready, 'Configuration is not ready');
} catch (error) {
if (error instanceof Error && error.name === 'Invariant Violation') {
reportInternalBug(error);
} else {
throw error;
}
}The package exports no custom Error class, so checks are string-based. Avoid treating invariant failures as recoverable validation errors.
Test the failure messagetest-invariant-failure
import { expect, it } from 'vitest';
it('rejects an unknown type', () => {
expect(() => invariant(false, 'Unknown type %s', 'ghost'))
.toThrow('Unknown type ghost');
});The error is thrown synchronously. Also assert the name separately if callers depend on Invariant Violation.
Keep a message on every assertionrequire-message-always
// Safe in development and production:
invariant(connection !== null, 'Expected an active connection');Never bypass the types to omit the message. In production the implementation throws about the missing message even when the condition is true.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| tiny-invariant | npm | Choose it for a deliberately public small invariant helper with clearer direct-use documentation |
| invariant | npm | Choose it when compatibility with the long-established Facebook-style invariant package matters |
| ts-invariant | npm | Choose it for an invariant API maintained as a standalone TypeScript-oriented package |
| zod | npm | Choose it when the real job is validating unknown external data and returning structured, typed errors |