Command-line tool to convert TypeScript type definitions to haxe externs
Thanks to the Haxe Foundation for supporting this project!
Install dts2hx in your local project as a development dependency:
npm install dts2hx --save-dev
Install a module with types, for example npm install three
. If your module of choice doesn't include type definitions, try installing externally maintained ones with npm install @types/{module-name}
Run dts2hx on the node module
npx dts2hx three --modular
This will generate externs into .haxelib/three, to use the externs, add --library three
to your build.hxml file.
We add --modular
because we intend to use the library via require()
rather than via a global-scope THREE
object. If you want to use the THREE
object, add --global
Alternatively, generate externs for all local package.json dependencies with
npx dts2hx --all
For bonus points, add dts2hx as a postinstall script in your package.json so that externs are generated automatically after npm install
"scripts": {
"postinstall": "dts2hx --all"
}
See examples/ for example projects using popular js libraries
The generated externs use haxe 4+ syntax. See dts2hx --help
for a complete list of options
There are no TypeScript definitions for my module
Many popular js modules have external type definitions maintained in places like DefinitelyTyped – try installing external definitions with: npm install @types/{module-name}
, then use dts2hx {module-name}
as normal
My types use TypeScript 4.0+ syntax and are not supported
While you're waiting for dts2hx to support newer syntax natively, you can use downlevel-dts to translate your types to TS 3.4 syntax which dts2hx will support
How do you convert a local TypeScript definition file, like index.d.ts?
dts2hx uses the same module resolution as TypeScript, so in TypeScript you import types from this file with import {...} from './index'
, for dts2hx you would do dts2hx ./index
Why is there a global
package?
TypeScript definitions often define two parallel sets of types, one for use with <script src="https://github.com/haxiomic/dts2hx/raw/master/">
(global) imports and the other for use with es6-style module imports. Unfortunately, these two sets of types are often not exactly the same and can differ in subtle ways
If you don’t want the global directory you can use dts2hx pixi.js --modular
, or if you only want externs without the global directory you can do dts2hx pixi.js --global
You can customize the name of the global directory with --globalPackageName
Difference between @:jsRequire()
and @:native()
TypeScript type definitions specify whether or not the symbols are accessible globally (@:native()
) or via module resolution (@:jsRequire()
). Many type definitions include both globally available and modular symbols. If a library has global symbols, they will be emitted in a package called global
. all types in the global
package use @:native()
metadata, whereas types elsewhere will use @:jsRequire()
.
If your types only use @:jsRequire
and you want to run in a browser (like the three.js type definitions), then you can use a bundler. I recommend esbuild over webpack and others because it has by far the best performance (~100 milliseconds bundling time).
For example, to call a bundler after haxe generates your js file, first install esbuild:
npm install esbuild
--cmd
that calls esbuild to your hxml, for example:
--js example.js
--cmd npx esbuild example.js --bundle --outfile=bundle.js
Should I publish generated types to haxelib?
Ideally dts2hx replaces the need to install externs from haxelib, however if the generated externs are not perfect and require manual fixups you may want to publish a curated version to haxelib. Before you do that please consider opening an issue here noting the fixup required instead – it would be better if dts2hx converted more modules perfectly
What makes this different from previous approaches?
The idea of generating Haxe externs from .d.ts
files is not new, ts2hx for instance was started 5 years ago already. However, this turned out to not be viable because it implemented a TypeScript parser in Haxe. The maintenance effort required turned out to be too great since TypeScript is evolving quickly.
This project takes the opposite approach and hooks into the TypeScript compiler API, which simplifies future maintenance a lot.
haxe build.hxml
Our test setup is fairly bare bones but effective – dts2hx is executed on a range of tricky libraries and edge cases and the result is committed to this repo. After making a change to dts2hx, the git diff can be reviewed for expected and unexpected changes. While there's basic CI, reviewing the diff is a manual process
dts2hx is currently in alpha release, everything should work but please report any issues!
#if global @:native(...) # else @:jsRequire(...) #end
postinstall: dts2hx --all
is faster)
--install
option
install @types/{name}
if no types found in main moduleOr$N<T0 ... T$N>
and ConstOr$N<T0 ... T$N>
to enable better type union behavior (and enable enum subsets)
type ModifierSyntaxKindEnum = Modifiers['kind']
;@:overloads
are supported and .call()
isn't requirediterator
symbol)@:genericBuild
abstract
Constructible
type@:jsRequire()
so a separate bundler isn't required. Maybe a we could use a macro for this
@:native()
fields to externs, @:native
information is lost