grafana / cuetsy

Experimental CUE->TypeScript exporter
Apache License 2.0
106 stars 8 forks source link

Cuetsy Logo

Installation · Usage

cuetsy

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

Example

CUETypeScript
```cue 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") ``` ```typescript 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 experimental. 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

Alternatively, cuetsy can be used as a library for more customized code generation.

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
```cue MyUnion: 1 | 2 | 3 | 4 | 5 | 6 @cuetsy(kind="type") ``` ```typescript 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:

CUETypeScript
```cue MyInterface: { Num: number Text: string List: [...number] Truth: bool } @cuetsy(kind="interface") ``` ```typescript export interface MyInterface { List: number[]; Num: number; Text: string; Truth: boolean; } ```

Inheritance

Interfaces can optionally extend another interface. If a type marked for export as a kind="interface" is unified (whether by & or embedding) with another type marked for export as an interface, it will produce extend in output:

CUETypeScript
```cue AInterface: { AField: string } @cuetsy(kind="interface") ByUnifying: AInterface & { BField: int } @cuetsy(kind="interface") ByEmbedding: { AInterface CField: bool } @cuetsy(kind="interface") ``` ```typescript export interface AInterface { AField: string; } export interface ByUnifying extends AInterface { BField: number; } export interface ByEmbedding extends AInterface { CField: boolean; } ```

Enums

CUE TypeScript @cuetsy(kind)
Disjunction String enums, Numeric enums 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.

For string enums, 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. For a numeric enum, memberNames must be specified.

CUE TypeScript
```cue AutoCamel: "foo" | "bar" @cuetsy(kind="enum") 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") ``` ```ts export enum AutoCamel { Bar = 'bar', Foo = 'foo', } export enum ManualCamel { Bar = 'bar', Foo = 'foo', } export enum Arbitrary { Zap = 'bar', Zip = 'foo', } export enum Numeric { One = 1, Two = 2, Zero = 0, } ```

Defaults

CUE TypeScript
Defaults const

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

CUETypeScript
```cue MyUnion: 1 | 2 | *3 @cuetsy(kind="type") MyEnum: "foo" | *"bar" @cuetsy(kind="enum") MyInterface: { num: int | *6 txt: string | *"CUE" enm: MyDisjEnum } @cuetsy(kind="interface") ``` ```typescript export type MyUnion = 1 | 2 | 3; export const defaultMyUnion: MyUnion = 3; export enum MyEnum { Bar = 'bar', Foo = 'foo', } export const defaultMyEnum: MyEnum = MyEnum.Bar; export interface MyInterface { enm: MyDisjEnum; num: number; txt: string; } export const defaultMyInterface: Partial = { enm: MyDisjEnum.Bar, num: 6, txt: 'CUE', }; ```