I'd like to write a project in Typescript. The project builds on top of the Typescript compiler, so I'm using typescript as a library as well, which (AFAIK) is a CommonJS library. While the project is intended to be ran on Node (not in the browser), I'd like to structure this project to use ES Modules as much as possible. In other words, I set "type": "module"
in the package.json
.
Example project
I have created a minimally reproducible example here: https://github.com/evertheylen/test-typescript-import. You can run a file with tsc && node build/test-typescript-compile-[...].js
. There's two approaches:
1. Standard Typescript import
See the file test-typescript-compile-with-import.ts
:
import * as ts from "typescript";
// This will print: `The ts object has keys: ['default']`
console.log("The ts object has keys:", Object.keys(ts));
// This will of course fail
console.log(ts.createProgram(['foobar.ts'], {noEmitOnError: true}).emit());
Here, the Typescript static compilation checks work (i.e. ts.createProgram
is recognized with the proper types). However, at runtime the ts
object only has a single default
key (i.e. in order to actually run createProgram
I'd have to write ts.default.createProgram
which Typescript does not allow).
2. Use require
See the file test-typescript-compile-with-require.ts
(also see the createRequire
docs):
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const ts = require("typescript");
// This will print: `The ts object has keys: <lots of them>`
console.log("The ts object has keys:", Object.keys(ts));
// This will succeed!
console.log(ts.createProgram(['foobar.ts'], {noEmitOnError: true}).emit());
// The problem is that the 'ts' object is of type any, so (for example)
// the following typo won't be caught at compile time:
console.log(ts.createProogram(['foobar.ts'], {noEmitOnError: true}).emit());
This works, but Typescript does not recognize it and the ts
object is of type any
, which is not what I want (as is clear from the example).
Possible solutions
- Typescript is not going to become available as ES modules for a while, see this issue.
- Use approach (1) but set
ts = ts.default
in some way (maybe with a@ts-ignore
). I couldn't get this to work, Node will complain about this. It also feels very hacky to import a CommonJS library as if it were an ES module. - Use approach (2) but somehow convince Typescript that this
ts
object follows the same type as if I importedtypescript
. I read into various usages ofdeclare
and.d.ts
files but couldn't come up with anything.