For a given Scala.js project (and all its transitive dependencies) with @JSExport*exports to JavaScript, emit .d.ts files that describe the exported API for consumption by TypeScript. We will use tasty-query to analyze the API of the project and its dependencies.
Description
In hybrid projects with both Scala.js and TypeScript code, we want to effectively communicate between the two codebases. Since both languages are statically typed, ideally we want to do that using shared type definitions. Using TypeScript code from Scala.js can be done using ScalablyTyped: a tool that generates .scalafacade types for Scala.js from TypeScript files. For the other direction, we currently have no good automated tool to produce TypeScript types out of a Scala.js codebase. We have to write them by hand, or call Scala.js as if it were a dynamically typed JavaScript library.
In order to address this issue, we want to automatically generate .d.ts TypeScript type definition files for the "public" API of a Scala.js codebase. By public, we mean what is visible from JavaScript code (not from other Scala.js code depending on it). This includes @JSExportTopLevel entities, as well as their transitive dependencies.
For example, given the following Scala.js code:
class Foo extends js.Object {
def foo(): Int = 1
}
@JSExportTopLevel("Bar")
class Bar extends js.Object {
def bar(): String = "hello"
def newFoo(): Foo = new Foo()
}
class Other extends js.Object
we want to generate the following TypeScript type definitions:
interface Foo {
foo(): number
}
export class Bar {
bar(): string
newFoo(): Foo
}
Note that Foo became an interface in TypeScript, because it is not itself exported. That means that TypeScript cannot instantiate it. However, it can see Bar, instantiate it, and call newFoo() to get a Foo. Therefore we need to characterize the API of an already-created Foo instance. Other, on the other hand, completely disappears, as it does not appear in the public API of something that is (transitively) exported.
Existing approaches to this problem, notably Scala-TS, use a compiler plugin to get semantic access to the Scala.js types. This approach faces some challenges: Scala.js generates a set of JavaScript modules for an entire classpath at a time, whereas compiler plugins only see individual compilation units (source files). This makes the transitive aspect of exports challenging to handle, as well as the production of .d.ts files matching the eventual JavaScript modules.
An approach working on the entire classpath of the Scala.js project would be better able to tackle those challenges. However, until recently, it was difficult to semantically load and examine a full Scala classpath.
This is where tasty-query comes in: it is specifically designed to give access to all the Scala type system information about an entire classpath.
Supervisor
Sébastien Doeraene (sebastien.doeraene@epfl.ch): Principal Engineer at the Scala Center
Expected outcome
A core library that takes a tasty-query Classpath and emits a set of .d.ts files describing the exported Scala.js API
The core library should at least handle the following constructs:
@JSExportTopLevel JavaScript classes (extending js.Any), objects, defs and vals
def, val, var and lazy val members of the above
classes and traits transitively mentioned in the types of the above
@JSExportStatic members of companions of exported classes
all shapes of types that have a direct, straightforward translation to TypeScript (for example, union types should be handled; type lambdas should not)
Stretch goals are:
@JSExportTopLevel Scala classes (not extending js.Any) and objects
@JSExport members of Scala classes
Inner JavaScript classes and objects
An sbt plugin to invoke the core library for a Scala.js project as a dedicated task
Summary
For a given Scala.js project (and all its transitive dependencies) with
@JSExport*
exports to JavaScript, emit.d.ts
files that describe the exported API for consumption by TypeScript. We will use tasty-query to analyze the API of the project and its dependencies.Description
In hybrid projects with both Scala.js and TypeScript code, we want to effectively communicate between the two codebases. Since both languages are statically typed, ideally we want to do that using shared type definitions. Using TypeScript code from Scala.js can be done using ScalablyTyped: a tool that generates
.scala
facade types for Scala.js from TypeScript files. For the other direction, we currently have no good automated tool to produce TypeScript types out of a Scala.js codebase. We have to write them by hand, or call Scala.js as if it were a dynamically typed JavaScript library.In order to address this issue, we want to automatically generate
.d.ts
TypeScript type definition files for the "public" API of a Scala.js codebase. By public, we mean what is visible from JavaScript code (not from other Scala.js code depending on it). This includes@JSExportTopLevel
entities, as well as their transitive dependencies.For example, given the following Scala.js code:
we want to generate the following TypeScript type definitions:
Note that
Foo
became aninterface
in TypeScript, because it is not itself exported. That means that TypeScript cannot instantiate it. However, it can seeBar
, instantiate it, and callnewFoo()
to get aFoo
. Therefore we need to characterize the API of an already-createdFoo
instance.Other
, on the other hand, completely disappears, as it does not appear in the public API of something that is (transitively) exported.Existing approaches to this problem, notably Scala-TS, use a compiler plugin to get semantic access to the Scala.js types. This approach faces some challenges: Scala.js generates a set of JavaScript modules for an entire classpath at a time, whereas compiler plugins only see individual compilation units (source files). This makes the transitive aspect of exports challenging to handle, as well as the production of
.d.ts
files matching the eventual JavaScript modules.An approach working on the entire classpath of the Scala.js project would be better able to tackle those challenges. However, until recently, it was difficult to semantically load and examine a full Scala classpath.
This is where tasty-query comes in: it is specifically designed to give access to all the Scala type system information about an entire classpath.
Supervisor
Sébastien Doeraene (sebastien.doeraene@epfl.ch): Principal Engineer at the Scala Center
Expected outcome
Classpath
and emits a set of.d.ts
files describing the exported Scala.js API@JSExportTopLevel
JavaScript classes (extendingjs.Any
), objects, defs and valsdef
,val
,var
andlazy val
members of the above@JSExportStatic
members of companions of exported classes@JSExportTopLevel
Scala classes (not extendingjs.Any
) and objects@JSExport
members of Scala classesclass
es andobject
s