Scala-TS

Logo

Simple tool to transpile Scala datamodel

View the Project on GitHub scala-ts/scala-ts

Examples

Scala-TS handles various cases and can be configured in many ways.

Example 1

A simple case class Incident.

package scalats.examples

case class Incident(id: String, message: String)

Generated TypeScript: Interface Incident

export interface Incident {
  id: string;
  message: string;
}

Example 2

Case class Station with Optional field lastIncident.

package scalats.examples

case class Station(
    id: String,
    name: String,
    lastIncident: Option[Incident])

Generated TypeScript:

export interface Station {
  id: string;
  name: string;
  lastIncident?: Incident;
}

See optionToNullable in configuration documentation.

Example 3

Generic case class Tagged[T].

package scalats.examples

case class Tagged[T](tag: String, value: T)

Generated TypeScript:

export interface Tagged<T> {
  tag: string;
  value: T;
}

Example 4

Related case classes Event and Message, also using the previous generic type Tagged and Value class EventType.

package scalats.examples

import java.util.Locale
import java.time.OffsetDateTime

final class EventType(val name: String) extends AnyVal

case class Event(
    id: String,
    changed: OffsetDateTime,
    `type`: EventType,
    messages: Tagged[Seq[TextMessage]])

case class TextMessage(
    format: String,
    language: Locale,
    text: String)

Generated TypeScript: Note that the Value class EventType is exported as the inner type (there string for val name: String).

export interface Event {
  id: string;
  changed: Date;
  type: string; // <---- HERE
  messages: Tagged<ReadonlyArray<Message>>;
}

export interface Message {
  format: string;
  language: string;
  text: string;
}

Locale type is provided a transpiler as string.

In Scala 3, Opaque Types are supported in a similar way.

package scala3ts.examples

object Event {
  opaque type EventType = String
}

Example 4a

The declaration mapped valueClassAsTagged in configuration as below.

scalatsDeclarationMappers += valueClassAsTagged

Then the Value class EventType is generated as a tagged/branded type.

export type EventType = string & { __tag: 'EventType' };

// Constructor
export function EventType(value: string): EventType {
  return value as EventType
}

export function isEventType(v: any): v is EventType {
  return (typeof v) === 'string';
}

Then such value can be initialized as bellow.

const done: EventType = EventType('Done')

isEventType(done) // true

Example 5

Sealed trait/family Transport; By default, sealed trait is generated using inheritance.

package scalats.examples

sealed trait Transport {
  def name: String
}

case class TrainLine(
    name: String,
    startStationId: String,
    endStationId: String)
    extends Transport

case class BusLine(
    id: Int,
    name: String,
    stopIds: Seq[String])
    extends Transport

Generated TypeScript:

export interface TrainLine extends Transport {
  name: string;
  startStationId: string;
  endStationId: string;
}

export interface BusLine extends Transport {
  id: number;
  name: string;
  stopIds: ReadonlyArray<string>;
}

export interface Transport {
  name: string;
}

See on GitHub

In Scala 3, Union Types can be used.

object Transport:
  type Line = TrainLine | BusLine

Example 6

Sealed trait/family as TypeScript union type.

Using scalatsUnionWithLiteral settings (which setup appropriate declaration mapper and import resolvers), a Scala sealed family representing a union type can be generated as TypeScript union type.

package scalats.examples

sealed trait Greeting

object Greeting {
  case object Hello extends Greeting
  case object GoodBye extends Greeting
  case object Hi extends Greeting
  case object Bye extends Greeting

  case class Whatever(word: String) extends Greeting
}

Generated TypeScript:

export const ByeInhabitant = 'Bye';

export type Bye = typeof ByeInhabitant;

export const GoodByeInhabitant = 'GoodBye';

export type GoodBye = typeof GoodByeInhabitant;

export const HelloInhabitant = 'Hello';

export type Hello = typeof HelloInhabitant;

export const HiInhabitant = 'Hi';

export type Hi = typeof HiInhabitant;

export interface Whatever {
  word: string;
}

export type Greeting = Bye | GoodBye | Hello | Hi | Whatever;

See scalatsUnionWithLiteral SBT settings, DeclarationMapper.SingletonAsLiteral and DeclarationMapper.UnionAsSimpleUnion for setting scalatsDeclarationMappers, and ImportResolver.UnionWithLiteralSingleton and scalatsImportResolvers setting; Details below in configuration documentation.

See on GitHub (example with Enumeratum)

Example 7

Scala Enumeration.

package scalats.examples

object WeekDay extends Enumeration {
  type WeekDay = Value
  val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
}

Generated TypeScript: union WeekDay

export type WeekDay = 'Mon' | 'Tue' | 'Wed' | 'Thu' | 'Fri' | 'Sat' | 'Sun'

export const WeekDayValues = [ 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun' ]
// Useful to iterate the values

See on GitHub

In Scala 3, Enum Types are supported.

package scalats.examples

enum WeekDay:
  case Mon
  case Tue
  case Wed
  case Thu
  case Fri
  case Sat
  case Sun

Example 8

Scala Singleton objects as TypeScript Literal types.

package scalats.examples

sealed abstract class State(val entryName: String)

case object Alabama extends State("AL")
case object Alaska extends State("AK")

Generated TypeScript: entryName is transpiled as literal.

export const AlabamaInhabitant = "AL";

export type Alabama = typeof AlabamaInhabitant;

export const AlaskaInhabitant = "AK";

export type Alaska = typeof AlaskaInhabitant;

See on GitHub

Example 9

Stable literal members (val or nullary def) in single objects as type invariants.

package scalats.examples

final class Grade(val value: Int) extends AnyVal

object Constants {
  def code = 1
  val name = "foo"
  val LowerGrade = new Grade(0)
}

Generated TypeScript: code, name and LowerGrade are generated

import { Grade, isGrade } from './Grade';

export class Constants {
  public LowerGrade: Grade /* number */ = 0;
  public name: string = "foo";
  public code: number = 1;

  private static instance: Constants;

  private constructor() {}

  public static getInstance() {
    if (!Constants.instance) {
      Constants.instance = new Constants();
    }

    return Constants.instance;
  }
}

export function isConstants(v: any): v is Constants {
  return (v instanceof Constants) && (v === Constants.getInstance());
}

See on GitHub

Example 9a

If valueClassAsTagged (see Example 4a) is applied, then LowerGrade is generated as below.

public LowerGrade: Grade /* number */ = Grade(0);

See on GitHub

Example 9b

Data structures like Seq, Set and Map are supported, when values are supported.

package scalats.examples

object ConstantsWithDataStructures {
  def code = 1
  val name = "foo"
  val LowerGrade = new Grade(0)

  val list = List(LowerGrade)
  def set = Set("lorem", "ipsum")

  val dict = Map(
    "A" -> "value #1",
    "B" -> name)
}

Generated TypeScript: list, set and dict are generated as ReadonlyArray, ReadonlySet and object.

Note that the non-literal stable terms as LowerGrade in list are supported.

import { Grade, isGrade } from './Grade';

export class Constants {
  public code: number = 1;

  public name: string = "foo";

  public LowerGrade: Grade = 0;

  public list: ReadonlyArray<Grade> = [ this.LowerGrade ];

  public set: ReadonlySet<string> = new Set("lorem", "ipsum");

  public readonly dict = {
    'A': "value #1",
    'B': this.name
  }
}