Skip to content

grafana/cuetsy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

66 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Cuetsy Logo

Installation · Usage

cuetsy

Converting CUE objects to their TypeScript equivalent (highly experimental!)

  • CUE makes defining and validating canonical data specification easy
  • TypeScript is dominant in the frontend, but cannot natively benefit from this
  • CUE types have direct TypeScript equivalents, so cuetsy can bridge this gap

Example

CUETypeScript
DiceFaces: 1 | 2 | 3 | 4 | 5 | 6 @cuetsy(kind="type")

Animal: {
    Name: string
    Sound: string
} @cuetsy(kind="interface")

LeggedAnimal: Animal & {
    Legs: int
} @cuetsy(kind="interface")

Pets: "Cat" | "Dog" | "Horse" @cuetsy(kind="enum")
export type DiceFaces = 1 | 2 | 3 | 4 | 5 | 6;
export interface Animal {
  Name: string;
  Sound: string;
}
export interface LeggedAnimal extends Animal {
  Legs: number;
}
export enum Pets {
  Cat = "Cat",
  Dog = "Dog",
  Horse = "Horse",
}

Status

Cuetsy is in its early development, so it does not support all TypeScript features. However, the following are supported:

Installation

Cuetsy can be installed using Go 1.16+

$ go install github.com/grafana/cuetsy/cmd/cuetsy

Usage

cuetsy must be invoked on files as follows:

$ cuetsy [file.cue]

This will create a logically equivalent [file].ts

Union Types

CUE TypeScript @cuetsy(kind)
Disjunction Union Type type

Union types are expressed in CUE and TypeScript nearly the same way, namely a series of disjunctions (a | b | c):

CUETypeScript
MyUnion: 1 | 2 | 3 | 4 | 5 | 6 @cuetsy(kind="type")
export type MyUnion = 1 | 2 | 3 | 4 | 5 | 6;

Interfaces

CUE TypeScript @cuetsy(kind)
Struct Interface interface

TypeScript interfaces are expressed as regular structs in CUE.

Caveats:

  • Nested structs are not supported
CUETypeScript
MyInterface: {
    Num: number
    Text: string
    List: [...number]
    Truth: bool
} @cuetsy(kind="interface")
export interface MyInterface {
  List: number[];
  Num: number;
  Text: string;
  Truth: boolean;
}

Inheritance

Interfaces can optionally inherit from another interface. This is expressed using the union operator &:

CUETypeScript
AInterface: {
    AField: string
} @cuetsy(kind="interface")

BInterface: AInterface & {
    BField: int
} @cuetsy(kind="interface")
export interface AInterface {
  AField: string;
}
export interface BInterface extends AInterface {
  BField: number;
}

Enums

CUE TypeScript @cuetsy(kind)
Disjunction, Struct Enum enum

TypeScript's enums are union types, and are a mostly-exact mapping of what can be expressed with CUE's disjunctions. Disjunctions may contain only string or numeric values.

The member names (keys) of the TypeScript enum are automatically inferred as the titled camel-case variant of their string value, but may be explicitly specified using the memberNames attribute. If the disjunction contains any numeric values, memberNames must be specified.

CUE TypeScript
// Enum-level comment
// Foo: member-level comment
// Bar: member-level comment
AutoCamel: "foo" | "bar" @cuetsy(kind="enum")
// Enum-level comment
// Foo: member-level comment
// Bar: member-level comment
ManualCamel: "foo" | "bar" @cuetsy(kind="enum",memberNames="Foo|Bar")
Arbitrary: "foo" | "bar" @cuetsy(kind="enum",memberNames="Zip|Zap")
Numeric: 0 | 1 | 2 @cuetsy(kind="enum",memberNames="Zero|One|Two")
ErrMismatchLen: "a" | "b" | "c" @cuetsy(kind="enum",memberNames="a|b")
ErrNamelessNumerics: 0 | 1 | 2 @cuetsy(kind="enum")
/**
 * Enum-level comment
 **/
enum AutoCamel {
  // member-level comment
  Foo = "foo",
  // member-level comment
  Bar = "bar",
}
/**
 * Enum-level comment
 **/
enum ManualCamel {
  // member-level comment
  Foo = "foo",
  // member-level comment
  Bar = "bar",
}
enum Arbitrary {
  Zip = "foo",
  Zap = "bar",
}
enum Numeric {
  Zero = 0,
  One = 1,
  Two = 2,
}

Defaults

CUE TypeScript
Defaults const

Cuetsy can optionally generate a const for each type that holds default values. For that, attach CUE Default Values to your type definitions:

CUETypeScript
MyUnion: 1 | 2 | *3 @cuetsy(kind="type")

MyDisjEnum: "foo" | *"bar" @cuetsy(kind="enum")
MyStructEnum: {
    A: "Foo"
    B: "Bar" @cuetsy(enumDefault)
} @cuetsy(kind="enum")

MyInterface: {
    num: int | *6
    txt: string | *"CUE"
    enm: MyDisjEnum
} @cuetsy(kind="interface")
export type MyUnion = 1 | 2 | 3;
export const myUnionDefault: MyUnion = 3;

export enum MyDisjEnum {
  Bar = "bar",
  Foo = "foo",
}
export const myDisjEnumDefault: MyDisjEnum = MyDisjEnum.Bar;

export enum MyStructEnum {
  A = "Foo",
  B = "Bar",
}
export const myStructEnumDefault: MyStructEnum = MyStructEnum.B;

export interface MyInterface {
  enm: MyDisjEnum;
  num: number;
  txt: string;
}
export const myInterfaceDefault: MyInterface = {
  enm: myDisjEnumDefault,
  num: 6,
  txt: "CUE",
};