Simple tool to transpile Scala datamodel
Scala-TS handles various cases and can be configured in many ways.
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;
}
Case class Station
with Option
al 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.
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;
}
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 asstring
.
In Scala 3, Opaque Types are supported in a similar way.
package scala3ts.examples
object Event {
opaque type EventType = String
}
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
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
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
andDeclarationMapper.UnionAsSimpleUnion
for settingscalatsDeclarationMappers
, andImportResolver.UnionWithLiteralSingleton
andscalatsImportResolvers
setting; Details below in configuration documentation.
See on GitHub (example with Enumeratum)
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
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
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
If valueClassAsTagged
(see Example 4a) is applied, then LowerGrade
is generated as below.
public LowerGrade: Grade /* number */ = Grade(0);
See on GitHub
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
inlist
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
}
}