Generate typescript interfaces from your scala classes



2.12 2.13 2.12

Scala TSI can automatically generate Typescript Interfaces from your Scala classes.


To use the project add the SBT plugin dependency in project/plugins.sbt:

// See badge above for latest version number
addSbtPlugin("com.scalatsi" % "sbt-scala-tsi" % "0.8.2")

And configure the plugin in your project:

// Replace with your project definition
lazy val root = (project in file("."))
      // The classes that you want to generate typescript interfaces for
      typescriptExports := Seq("MyClass"),
      // The output file which will contain the typescript interfaces
      typescriptOutputFile := baseDirectory.value / "model.ts",
      // Include the package(s) of the classes here
      // Optionally import your own TSType implicits to override default default generated
      typescriptGenerationImports := Seq("mymodel._", "MyTypescript._")

Now sbt generateTypescript will transform a file like

case class MyClass(foo: String, bar: Int)

Into a typescript interface like

export interface IMyClass {
  foo: string
  bar: number

See #Example or the example project for more a more examples

Without sbt plugin

The sbt plugin will add the required dependency to your project. It can be added manually if you don't use the plugin:

libraryDependencies += "com.scalatsi" %% "scala-tsi" % "<version>"


Key Type Default Description
typescriptExports Seq[String] Seq() A list of all your (top-level) classes that you want to generate interfaces for
typescriptGenerationImports Seq[String] Seq() A list of all imports. This should import all classes you defined above, as well as custom TSType implicits
typescriptOutputFile File target/scala-tsi.ts The output file with generated typescript interfaces
typescriptStyleSemicolons Boolean false Whether to add semicolons to the exported model
typescriptHeader Option[String] Some("...") A header for the output file. Contains a notice about the file being generated by default
typescriptTaggedUnionDiscriminator Option[String] Some("type") The discriminator field for tagged unions, or None to disable tagged unions


You can check out the example project for a complete set-up and more examples.

Say we have the following JSON:

   "name": "person name",
   "email": "",
   "age": 25,
   "job": {
      "tasks": ["Be in the office", "Drink coffee"],
      "boss": "Johnson"

Generated from this Scala domain model:

package myproject

case class Person(
  name: String,
  email: Email,
  age: Option[Int],
  // for privacy reasons, we do not put this social security number in the JSON
  ssn: Option[Int],
  job: Job
// This type will get erased when serializing to JSON, only the string remains
case class Email(address: String)

case class Job(tasks: Seq[String], boss: String)

With Typescript, your frontend can know what data is available in what format. However, keeping the Typescript definitions in sync with your scala classes is a pain and error-prone. scala-tsi solves that.

First we define the mapping as follows

package myproject

import com.scalatsi.*
import com.scalatsi.dsl.*

// A TSType[T] is what tells scala-tsi how to convert your type T into typescript
// MyModelTSTypes contains all TSType[?]'s for your model
// You can also spread these throughout your codebase, for example in the same place where your JSON (de)serializers
object MyModelTSTypes {
  // Tell scala-tsi to use the typescript type of string whenever we have an Email type
  // Alternatively, TSType.alias[Email, String] will create a `type Email = string` entry in the typescript file
  implicit val tsEmail: TSType[Email] = TSType.sameAs[Email, String]
  // TSType.fromCaseClass will convert your case class to a typescript definition
  // `- ssn` indicated the ssn field should be removed
  implicit val tsPerson: TSType[Person] = TSType.fromCaseClass[Person] - "ssn"

And in your build.sbt configure the sbt plugin to output your class:

lazy val root = (project in file("."))
    typescriptExports           := Seq("Person"),
    typescriptGenerationImports := Seq("myproject._", "MyModelTSTypes._"),
    typescriptOutputFile        := baseDirectory.value / "model.ts"

this will generate in your project root a model.ts:

export interface IPerson {
  name : string
  email : string
  age ?: number
  job: IJob

export interface IJob {
  tasks: string[]
  boss: string


This document contains more detailed explanation of the library and usage

Circular references

Currently, scala-tsi cannot always handle circular references. You will get an error along the following lines:

[error] Circular reference encountered while searching for TSType[B]
[error] Please break the cycle by locally defining an implicit TSType like so:
[error] implicit val tsType...: TSType[...] = {
[error]   implicit val tsA: TSType[B] = TSType.external("IB") // name of your "B" typescript type here
[error]   TSType.getOrGenerate[...]
[error] }
[error] for more help see

To help scala-tsi and break the cycle you will need to define an explicit manual reference. For example, if you have the following classes

case class A(b: B)
case class B(a: A)

You will get a warning on Scala 2, and an error on Scala 3. The Scala 2 output might also not always be as desired. To fix this, you can explicitly define the right values.

Scala 2
object B {
  // This explicit definition is to help scala-tsi with the recursive definition of A and B
  private implicit val aReference: TSType[A] = TSType.external[A]("IA")
  implicit val bTS: TSType[B] = TSType.fromCaseClass[B]
Scala 3
object B {
// This explicit definition is to help scala-tsi with the recursive definition of A and B
  private given TSType[A] = TSType.external[A]("IA") 
  prviate val generatedTSType = TSType.getOrGenerate[B] 
  given TSType[B] = generatedTSType