Files
oxc-project__oxc/apps/oxlint/src-js/utils/asserts.ts
overlookmotel cfaa754ded refactor(linter/plugins): debug assert that values passed via NAPI are not undefined (#20070)
Follow-on after #19675.

The type for `lint` function that NAPI-RS produces is incorrect. `Option::None` on Rust side is translated to `null`, never `undefined`. Check this with a debug assertion, instead of handling `undefined` at runtime (which is pointless, because it never is `undefined`).
2026-03-06 17:39:15 +00:00

133 lines
4.4 KiB
TypeScript

/**
* Assert a value is of a certain type.
*
* Has no runtime effect - only for guiding the type-checker.
* Minification removes this function and all calls to it, so it has zero runtime cost.
*
* @param value - Value
*/
// oxlint-disable-next-line no-unused-vars
export function typeAssertIs<T>(value: unknown): asserts value is T {}
/**
* Assert a value is not `null` or `undefined`.
*
* In release builds, is a no-op. Only does runtime checks in debug builds.
* Minification removes this function and all calls to it in release builds, so it has zero runtime cost.
*
* Use this for testing conditions which would indicate a bug in the code.
* Do NOT use this for validating user input.
*
* If creating the error message is expensive, or potentially creating the message itself can result in an error
* when the assertion passes, pass a function which returns the message.
*
* ```ts
* debugAssertIsNonNull(thing, () => `Should not be null: ${getErrorMessage()}`);
* ```
*
* @param value - Value
* @param message - Message to include in error if `value == null`, or a function which returns the message
* to include in error (optional).
*/
export function debugAssertIsNonNull<T>(
value: T | null | undefined,
message?: string | (() => string),
): asserts value is T {
if (!DEBUG) return;
if (value === null || value === undefined) {
if (typeof message === "function") {
message = message();
} else if (message === undefined) {
// oxlint-disable-next-line typescript/restrict-template-expressions
message = `Expected non-null value, got ${value}`;
}
throw new Error(message);
}
}
/**
* Assert a value is not `undefined`.
*
* In release builds, is a no-op. Only does runtime checks in debug builds.
* Minification removes this function and all calls to it in release builds, so it has zero runtime cost.
*
* Use this for testing conditions which would indicate a bug in the code.
* Do NOT use this for validating user input.
*
* If creating the error message is expensive, or potentially creating the message itself can result in an error
* when the assertion passes, pass a function which returns the message.
*
* ```ts
* debugAssertIsNotUndefined(thing, () => `Should not be undefined: ${getErrorMessage()}`);
* ```
*
* @param value - Value
* @param message - Message to include in error if `value` is `undefined`, or a function which returns the message
* to include in error (optional).
*/
export function debugAssertIsNotUndefined<T>(
value: T | undefined,
message?: string | (() => string),
): asserts value is T {
if (!DEBUG) return;
if (value === undefined) {
if (typeof message === "function") {
message = message();
} else if (message === undefined) {
message = "Expected value not to be undefined";
}
throw new Error(message);
}
}
/**
* Debug assert that `fn` is a function.
*
* In release builds, is a no-op. Only does runtime checks in debug builds.
* Minification removes this function and all calls to it in release builds, so it has zero runtime cost.
*
* @param fn - Function
* @throws {TypeError} If `fn` is not a function in debug build
*/
export function debugAssertIsFunction(fn: unknown): asserts fn is Function {
if (!DEBUG) return;
if (typeof fn !== "function") throw new TypeError("Expected to be a function");
}
/**
* Assert a condition.
*
* In release builds, is a no-op. Only does runtime checks in debug builds.
* Minification removes this function and all calls to it in release builds, so it has zero runtime cost.
*
* Use this for testing conditions which would indicate a bug in the code.
* Do NOT use this for validating user input.
*
* If creating the error message is expensive, or potentially creating the message itself can result in an error
* when the assertion passes, pass a function which returns the message.
*
* ```ts
* debugAssert(condition, () => `Condition failed: ${getErrorMessage()}`);
* ```
*
* @param condition - Condition which is expected to be `true`
* @param message - Message to include in error if condition is `false`,
* or a function which returns the message to include in error if condition is `false` (optional).
*/
export function debugAssert(
condition: boolean,
message?: string | (() => string),
): asserts condition {
if (!DEBUG) return;
if (!condition) {
if (typeof message === "function") message = message();
throw new Error(message ?? "Assertion failed");
}
}