A collection of useful Node.js tricks
To use TypeScript in Node.js >=v22.6.0 in a bin executable, use a shebang with the --experimental-strip-types flag:
bin/index.ts
#!/usr/bin/env -S node --experimental-strip-types
const a: number = 1;
console.log(a);However, this will fail if the file is contained within node_modules, confirmed with Node.js <=v22.18.0. This node_modules ban change in the future.
For running .ts files in node_modules, consider using a JavaScript bin executable to strip types in node_modules on the fly using Node.js built-in type stripping support via registerHooks and stripTypeScriptTypes() from node:module:
bin/index.js
#!/usr/bin/env node
import { registerHooks, stripTypeScriptTypes } from 'node:module';
const tsRegex = /^file:.*(?<!\.d)\.m?ts$/;
// Intercept .ts / .mts files (skipping .d.ts files) and
// transpile to JS, returning ES module
registerHooks({
load(url, context, nextLoad) {
if (tsRegex.test(url)) {
return {
format: 'module',
source: stripTypeScriptTypes(
/** @type {import('node:module').ModuleSource} */ (
// eslint-disable-next-line @typescript-eslint/no-base-to-string -- ModuleSource returns useful information from .toString()
nextLoad(url).source
).toString(),
{
mode: 'transform',
sourceUrl: url,
},
),
};
}
return nextLoad(url, context);
},
});
// Path to entry point
await import('../src/index.ts');Another option is tsx:
- Add
tsxto your project dependendencies - Use
pnpm exec tsxto execute your file usingtsx#!/usr/bin/env -S pnpm exec tsx
Or alternatively, consider running the executable in Bun.
To write zero-dependency tests in TypeScript in Node.js, create a test file using node:assert/strict and node:test, eg:
__tests__/index.test.ts
import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import { test } from 'node:test';
await test('shows process.env error messages', () => {
const {
stdout,
stderr,
status: exitCode,
} = spawnSync('node', ['./build/upleveled-drone.mjs'], {
encoding: 'utf-8',
});
assert.equal(exitCode, 1);
assert.equal(stdout, '');
assert.equal(
stderr
// GITHUB_REPOSITORY is set on GitHub Actions but not locally
.replace('process.env.GITHUB_REPOSITORY is undefined\n', ''),
`process.env.DRONE_TOKEN is undefined
process.env.ISSUE_NUMBER is undefined
`,
);
});And then run it using type stripping in Node.js v22.18.0 or later:
$ node __tests__/index.test.tsTo run the tests in watch mode, use the --watch flag:
$ node --watch __tests__/index.test.ts