ts-node icon indicating copy to clipboard operation
ts-node copied to clipboard

Compile time errors being suppressed when using esm

Open Stono opened this issue 1 year ago • 4 comments

I really struggled to think of the correct title for this, so please edit it as required. I went down a rabbithole today whilst moving one of our apps to esm, as it was failing to start (with ts-node) due to the following node error:

❯ node --experimental-specifier-resolution=node --loader ts-node/esm lib/index.ts
node:internal/modules/run_main:115
    triggerUncaughtException(
    ^
[Object: null prototype] {
  [Symbol(nodejs.util.inspect.custom)]: [Function: [nodejs.util.inspect.custom]]
}

Node.js v22.5.1

Doesn't really give away much. It turned out however that the root caused we missed a return type on a promise eg Promise needed to be Promise<void>. This error was visible if you did a tsc, but ts-node was giving this output.

After gradually chipping away at the code to make a minimal example; i managed to reproduce it with a really simple bit of code that doesn't even technically have an error in it.

Assuming your code is in ./lib, create ./lib/index.ts:

import { SomeServer } from './server.js'

const webServer = new SomeServer()

and ./lib/server.ts:

export class SomeServer {}

If you try and start this; you'll get the error. The only thing "wrong" with this code is the fact that webServer is an unused variable. If i add console.log(webServer), eg:

import { SomeServer } from './server.js'

const webServer = new SomeServer()
console.log(webServer)

Then all is well:

❯ node --experimental-specifier-resolution=node --loader ts-node/esm lib/index.ts

SomeServer {}

Specifications

  • versions:
❯ ./node_modules/.bin/ts-node -vv
ts-node v10.9.2
node v22.5.1
compiler v5.3.3
  • tsconfig.json:
{
  "compilerOptions": {
    "lib": ["ES2022", "DOM"],
    "module": "ES2022",
    "target": "ES2022",
    "rootDir": "./",
    "outDir": "./built",
    "baseUrl": "./",
    "allowJs": false,
    "checkJs": false,
    "alwaysStrict": true,
    "noImplicitAny": true,
    "noImplicitThis": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "noEmitOnError": false,
    "sourceMap": false,
    "declaration": false,
    "strictBindCallApply": true,
    "noImplicitOverride": true,
    "noUncheckedIndexedAccess": true,
    "incremental": false,
    "skipDefaultLibCheck": true,
    "experimentalDecorators": true,
    "noUnusedLocals": true,
    "esModuleInterop": true,
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "allowSyntheticDefaultImports": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true
  },
  "include": ["lib/**/*.ts", "test/**/*.ts"],
  "exclude": ["node_modules/**/*"]
}
  • Operating system and version: Mac

Stono avatar Jul 29 '24 09:07 Stono

Just a heads up that my error was due to the server.ts file having a variable in an "if" check which I hadn't initialized yet, e.g.

app.get("/hut/:locationId", (req: Request, res: Response) => {
  const data = req.params.locationId;
  if (!location_id) { // <---- location_id was not not instantiated anywhere
    res.status(400).send("Bad Request: Missing locationId");
  } else {
    res.render('hut_template', data);
  }
});

The reason my tsc compilation wasn't catching was due to the same variable name "location_id" being instantiated in another .ts file so I was going mad to search for the cause...

DHE1999 avatar Feb 06 '25 10:02 DHE1999

I encountered this error when there was no use of a defined variable or function, it happens when I execute the script using ts-node, let's say node --loader ts-node/esm scripts/myscript.ts. And then I did some research and tried a simple trick, and the error went away.

// the linter will help you with this
const someVar: any // intended to use later, I want to check it out first
// 
const _falseCall = (_any: any) => { }; _falseCall(someVar);

// what about this
// You must use this function or it will trigger errors 
const myFutureFunc = ()=> {
   // ...
   // lines of codes
   // ...
}
//  
_falseCall(myFutureFunc); // I tried this and the error went away

// ...
// tons of lines
// ...

here is my .eslintrc.cjs:


module.exports = {
  root: true,
  env: { browser: true, es2020: true },
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:react-hooks/recommended',
  ],
  ignorePatterns: ['dist', '.eslintrc.cjs'],
  parser: '@typescript-eslint/parser',
  plugins: ['react-refresh'],
  rules: {
    'react-refresh/only-export-components': [
      'warn',
      { allowConstantExport: true },
    ],
  },
}

To conclude, if I know which variables or functions are not used, I try to comment out on them.
However, there is a suggestion to enable type-aware lint rules, check it out here. I don't know if it helps.

inoshadi avatar Apr 15 '25 05:04 inoshadi

This seems to be a general issue of how any UncaughtException is shown when ts-node/esm processes a file. It makes logs checking impossible as it always ends up showing the same cryptic result:

node:internal/modules/run_main:123
    triggerUncaughtException(
    ^
[Object: null prototype] {
  [Symbol(nodejs.util.inspect.custom)]: [Function: [nodejs.util.inspect.custom]]
}

The workaround to fix this is to make ts-node log TS errors to stderr instead of throwing exceptions (credits to this comment):

/* tsconfig.json */
{
  /* ... */
  "ts-node": {
    "logError": true,
    "pretty": true /* <= technically not required */
  }
}

snowbytes avatar Sep 28 '25 21:09 snowbytes

@snowbytes this solution is working for me, thanks 👍

rubs019 avatar Sep 30 '25 23:09 rubs019