Error: Debug Failure when importing `AssertionError` from `node:assert/strict`
🔎 Search Terms
Error: Debug Failure
🕗 Version & Regression Information
Happened when using tsc v5.4.5 with @types/node. Wasn’t an issue before (older tsc and older @types/node v20.12.12).
⏯ Playground Link
https://github.com/rauschma/tsc-debug-failure
💻 Code
import { AssertionError } from 'node:assert/strict';
Some of my compilerOptions in tsconfig:
"module": "NodeNext",
"moduleResolution": "NodeNext",
"isolatedModules": true,
"verbatimModuleSyntax": true,
"resolveJsonModule": true,
🙁 Actual behavior
Error: Debug Failure.
at getTypeOfVariableOrParameterOrPropertyWorker (/usr/local/lib/node_modules/typescript/lib/tsc.js:53005:11)
at getTypeOfVariableOrParameterOrProperty (/usr/local/lib/node_modules/typescript/lib/tsc.js:52974:20)
at getTypeOfSymbol (/usr/local/lib/node_modules/typescript/lib/tsc.js:53306:14)
at getTypeOfAlias (/usr/local/lib/node_modules/typescript/lib/tsc.js:53226:358)
at getTypeOfSymbol (/usr/local/lib/node_modules/typescript/lib/tsc.js:53318:14)
at getNarrowedTypeOfSymbol (/usr/local/lib/node_modules/typescript/lib/tsc.js:67681:18)
at checkIdentifier (/usr/local/lib/node_modules/typescript/lib/tsc.js:67808:16)
at checkExpressionWorker (/usr/local/lib/node_modules/typescript/lib/tsc.js:76261:16)
at checkExpression (/usr/local/lib/node_modules/typescript/lib/tsc.js:76216:32)
at checkNonNullExpression (/usr/local/lib/node_modules/typescript/lib/tsc.js:70497:29)
🙂 Expected behavior
No exception (normal operation)
Additional information about the issue
Possibly helpful:
- I initially couldn’t tell what the problem was (in my rather large repo) but Nathan Shively-Sanders suggested I change line
tsc.js:53005as follows. That told me that this problem was aboutAssertionError.
- Debug.assertIsDefined(symbol.valueDeclaration);
+ Debug.assertIsDefined(symbol.valueDeclaration, Debug.formatSymbol(symbol));
It repros in the playground too: TS playground. To repro you need to hover over AssertionError. An interesting fact is that the first hover after a source edit triggers the error but the second one doesn't and it renders a good tooltip content.
Failing test case:
// @strict: true
// @moduleResolution: node
// @noEmit: true
// @filename: node_modules/@types/node/index.d.ts
/// <reference path="assert.d.ts" />
/// <reference path="assert/strict.d.ts" />
// @filename: node_modules/@types/node/assert/strict.d.ts
declare module "node:assert/strict" {
import { strict } from "node:assert";
export = strict;
}
// @filename: node_modules/@types/node/assert.d.ts
declare module "assert" {
function assert(value: unknown, message?: string | Error): asserts value;
namespace assert {
class AssertionError extends Error {
actual: unknown;
expected: unknown;
code: "ERR_ASSERTION";
constructor(options?: {
message?: string | undefined;
actual?: unknown | undefined;
expected?: unknown | undefined;
});
}
namespace strict {
type AssertionError = assert.AssertionError;
}
const strict: Omit<typeof assert, "strict">;
}
export = assert;
}
declare module "node:assert" {
import assert = require("assert");
export = assert;
}
// @filename: index.ts
import { AssertionError } from 'node:assert/strict';
throw new AssertionError({
message: 'Hello',
actual: 3,
expected: 3,
});
The above test crashes even in TS 4.5 (and back even in 4.0 though with a slightly different error). So this most definitely is not a recent regression at all.
This is an interesting one. So the symbol that leads to a crash is a result of combineValueAndTypeSymbols. That combines a type symbol with a value symbol that is a mapped symbol. Mapped symbols don't have .valueDeclaration so there is nothing to attach onto the resulting combined symbol here. This leads to a crash.
We could propagate checkFlags here so getTypeOfSymbol could call getTypeOfMappedSymbol instead of getTypeOfVariableOrParameterOrProperty but that doesn't solve anything on its own. The combined symbol isn't truly a mapped symbol so it lacks properties that getTypeOfMappedSymbol expects.
I need to think more about it but it seems like more stuff should get propagated onto that combined symbol from the mapped symbol. getTypeOfSymbol here is interested in the type of the value and the value is typed using a mapped type... so - if I'm not mistaken - the only way to get the correct type is to resolve that mapped type symbol (we just can't do it right now since we lost the information required to do that along the way)
Hello there,
Here is my temporary fix, it does seem to work at first glance.
Before:
import assert, { AssertionError } from 'node:assert/strict';
import { AssertionError } from 'node:assert';
import assert from 'node:assert/strict';
It looks like assert.fail (from /strict) is emitting the same AssertionError as the one from the regular assert.
After that, no problem to build with tsc, no Debug Failure anymore.
Only minor issue I have now is that ESLint will complain:
'node:assert' import is restricted from being used. Use node:assert/strict instead.eslint[no-restricted-imports](https://eslint.org/docs/latest/rules/no-restricted-imports)
Cheers!
I can reproduce it with latest ts 5.5.3
for me it isn't working with @types/node: 20.14.9 but it does work with 18.19.9
- package.json
{
"name": "ts-issue",
"scripts": {
"typecheck": "tsc --noEmit -p tsconfig.json"
},
"license": "ISC",
"devDependencies": {
"@types/node": "20.14.9",
"typescript": "5.5.3",
"tslib": "2.6.3"
}
}
-
tsconfig.json
{
"compileOnSave": false,
"include": ["./src/issue.ts"],
"compilerOptions": {
"baseUrl": ".",
"rootDir": ".",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"importHelpers": true,
"target": "es2021",
"module": "CommonJS",
"moduleResolution": "Node",
"incremental": true,
"sourceMap": true,
"declaration": false,
"esModuleInterop": true,
"useDefineForClassFields": false,
"allowJs": true,
"resolveJsonModule": true,
"moduleDetection": "force",
"isolatedModules": false,
"lib": ["es2021", "es2022.Error", "ESNext.Disposable"],
"strict": true,
"noUncheckedIndexedAccess": false,
"noImplicitOverride": false,
"skipLibCheck": true,
"skipDefaultLibCheck": true,
"pretty": true,
"paths": {}
}
}
-
issue.ts
import { AssertionError } from "node:assert/strict";
throw new AssertionError({
message: "unreachable assertion",
operator: "unreachable",
});
- Output:
..../node_modules/typescript/lib/tsc.js:120127
throw e;
^
Error: Debug Failure.
at getTypeOfVariableOrParameterOrPropertyWorker (..../node_modules/typescript/lib/tsc.js:54702:11)
at getTypeOfVariableOrParameterOrProperty (..../node_modules/typescript/lib/tsc.js:54674:20)
at getTypeOfSymbol (..../node_modules/typescript/lib/tsc.js:55003:14)
at getTypeOfAlias (..../node_modules/typescript/lib/tsc.js:54923:373)
at getTypeOfSymbol (..../node_modules/typescript/lib/tsc.js:55015:14)
at getNarrowedTypeOfSymbol (..../node_modules/typescript/lib/tsc.js:69744:18)
at checkIdentifier (..../node_modules/typescript/lib/tsc.js:69883:16)
at checkExpressionWorker (..../node_modules/typescript/lib/tsc.js:78402:16)
at checkExpression (..../node_modules/typescript/lib/tsc.js:78356:32)
at checkNonNullExpression (..../node_modules/typescript/lib/tsc.js:72591:29)
Node.js v20.11.0
@JulianCataldo another workaround is to use assert.AssertionError instead:
import assert from 'node:assert/strict';
const { AssertionError } = assert;
new AssertionError();
// Or...
new assert.AssertionError();
I ran into this issue while porting a project to ESM and Jest tests to native node tests. Tests were passing when running them with Webstorm (using tsx, no type checking), and no type error reported on the IDE. However, running tests with borp failed with a tsc Debug Failure error.
Here's a more minimal reproduction:
// @filename: index.ts
import { AssertionError } from "./assert";
new AssertionError("assert");
// @filename: assert.d.ts
declare namespace assert {
class AssertionError extends Error {}
const strict: Omit<typeof assert, never>;
}
export = assert.strict;
You can do it in one file if you want.
I've filed a fix at https://github.com/DefinitelyTyped/DefinitelyTyped/pull/71889.