tree: 952fe14cd569621ade5825cddcdbb2b88d4ce4aa
  1. bin/
  2. lib/
  3. test/
  4. tool/
  5. analysis_options.yaml
  6. CHANGELOG.md
  7. LICENSE
  8. pubspec.yaml
  9. README.md
js_interop_gen/README.md

Tools to generate Dart interfaces from TypeScript declaration files and Web IDL definitions.

Using this

The tools to generate bindings are written in Dart, compiled to JavaScript, and run on Node.

There is one entrypoint present in this package:

  • js_interop_gen.dart: This entrypoint is for generating Dart interfaces from TS Declaration code, given the path to a .d.ts file

Requirements

Node.js

The js_interop_gen requires Node.js v22 or newer. Older Node versions are not supported and may result in runtime failures.

TS Declarations

To generate Dart interfaces for a given .d.ts file, run the following at the root of this package:

dart run js_interop_gen <input.d.ts>
dart run js_interop_gen -o <output> <input>

If multiple files are passed, the output option is regarded as an output directory instead.

For more information on the command-line options you can pass alongside, you can check the help information

dart run js_interop_gen --help

Configuration

The generator also has support for configuring the output of the generator, allowing support for configuring features like: variadic argument count, preamble (if any), TS Compiler Options, etc.

These configuration options can either be passed from the command line, or via a YAML configuration file. To pass a configuration file, pass the --config option.

Given a sample configuration file:

input: a.d.ts
output: b.d.ts
ts-config-file: tsconfig.json

The following are equivalent

dart run js_interop_gen -o b.d.ts --ts-config tsconfig.json a.d.ts
dart run js_interop_gen --config config.yaml

Note that not all configuration options are direct mappings between the CLI and the configuration file.

Configuration File Reference

OptionDescriptionExample
nameThe name of the bindingsname: MyBindings
descriptionA description of the bindings (optional)description: My awesome bindings
preamblePreamble text to insert before the bindings (optional)preamble: |
// Generated. Do not edit.
inputA file (single string) or set of files (array of strings) passed into the generatorinput: bindings.d.ts
or
input:
- bindings.d.ts
outputThe output file or directory to write the bindings tooutput: lib/src/js
includeDeclarations to include in the generated output (as a list). Can either be passed as a raw string to match the full name, or as a regular expression. By default, the generator outputs all exported declarationsinclude:
- myNumber
language_versionThe Dart Language Version to use, usually for formatting (optional)language_version: 3.6.0
ts_configAn object consisting of TS Configurations following the tsconfig.json file schema used for configuring the TypeScript Program/Compiler (optional)ts_config:
compilerOptions:
target: es2020
ts_config_fileThe TS Configuration file (tsconfig.json) if any (optional)ts_config_file: tsconfig.json
generate_allInclude generating declarations for code that isn't exported. Defaults to falsegenerate_all: true
ignore_errorsIgnore source code warnings and errors (they will still be printed). Defaults to falseignore_errors: true
functions.varargsThe number of arguments that variable-argument functions should take. Defaults to 4functions:
varargs: 6

Conventions

The generator scripts use a number of conventions to consistently handle TS definitions:

Top Level Declarations

  • Top level declarations are handled as top level dart external declarations annotated with @JS.

Enums

  • Enums are represented as extension types with static members.
  • In most cases, values are known beforehand, so the representation types of the resulting extension types are Dart primitive types with non-external static members.
  • If the value for an enum is not given, then the representation type is an interop type, and any members without a value are marked as external

Interfaces and Classes

  • Interfaces and Classes are emitted as extension types that wrap and implement JSObject
  • Interface and Class inheritance is maintained using implements between extension types
  • Classes have default constructors if none are provided
  • Interface and Classes support members such as: properties, functions, operators, call declarations, construct signatures, getters and setters.
  • Readonly properties are represented as getters.
  • Overriding signatures are annotated with @redeclare.
  • Multiple instances of interfaces in a given scope are merged together into a single interface. Also supports merging with var declarations.

Namespaces

  • Namespaces in TS are converted to extension types on JSObject with static members.
  • Typed declarations (such as enums, classes, interfaces and namespaces) are prefixed and generated in the general module space.
  • Constructor calls for classes are generated for every class in a namespace as a static redirect method.
  • Namespaces nested inside the namespace are as well generated as static getters on the namespace, which are references to the namespaces themselves.
  • Supports overloading and declaration merging with classes, interfaces, enums, var declarations, and functions.

Types

  • Supports mapping basic JS types to Dart js_interop types.
  • never type returned from a const declaration, function, readonly property or method has the declaration annotated with @doNotStore.
  • Supports automatically mapping web types to package:web types
  • Anonymous Unions/Intersections: Anonymous unions and intersections are represented as extension types with cast members to cast the value as a type in the union/intersection. If the union is homogenous and contains strings/numbers, then it is generated more like an enum, with specific static values. An intersection with null or undefined equals never
  • Anonymous Objects: Anonymous objects are represented similar to interface declarations, with an object literal constructor.
  • Anonymous Closures and Construct Signatures: Anonymous closures and construct signatures are represented as extension types on JSFunction overriding the call method. This allows for a stronger type signature than just JSFunction. While closures leave their calls as external, construct signatures just redirect to the class constructor.
  • Generic types are represented as normal Dart generics, and they are constrained by JSAny by default.
  • typeof declarations are references to the type of the declaration, which are:
    • Variable: The variable's type
    • Enum: An object representation of the enum (in order to support keyof correctly).
    • Function: JSFunction
    • Otherwise, the extension type representation of the type that it refers to is used.
  • keyof declarations are represented as unions of the keys for the operand type.

Other

  • Supports importing/exporting declarations. If the files that are being imported are included in input, then such references are treated as imports in Dart, else the declarations are generated in-place.
  • Supports documentation (JSDoc), and maps some annotations to Dart annotations such as @deprecated and @experimental