Skip to content

1. Types

Selene edited this page Mar 30, 2023 · 1 revision

Interfaces

CUE TypeScript @cuetsy(kind)
Struct Interface interface

TypeScript interfaces are expressed as regular structs in CUE.

Caveats

CUE TypeScript
MyInterface: {
    Num:   number
    Text:  string
    List:  [...number]
    Map:   [string]: string
    Truth: bool
} @cuetsy(kind="interface")
export interface MyInterface {
  List:  Array<number>;
  Map:   Record<string, string>;
  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:

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

ByUnifying: AInterface & {
    BField: int
} @cuetsy(kind="interface")

ByEmbedding: {
    AInterface
    CField: bool
} @cuetsy(kind="interface")
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
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")
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,
}

Using Enums with interfaces.

CUE TypeScript
MyEnum: "foo" | "bar" | "baz" @cuetsy(kind="enum")

MyInterface: {
  enum:          MyEnum
  enumWithValue: MyEnum & "foo"
  unionEnums:    (MyEnum & "foo") | (MyEnum & "bar")
} @cuetsy(kind="interface")
export enum MyEnum {
  Bar = 'bar',
  Baz = 'baz',
  Foo = 'foo',
}

export interface MyInterface {
  enum: MyEnum;
  enumWithValue: MyEnum.Foo;
  union: (MyEnum.Foo | MyEnum.Bar);
}

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:

Using enums with defaults

CUE TypeScript
MyEnum: "foo" | *"bar" | "baz" @cuetsy(kind="enum")
export enum MyEnum {
  Bar = 'bar',
  Baz = 'baz',
  Foo = 'foo',
}

export const defaultMyEnum: MyEnum = MyEnum.Bar;

Using enum defaults in interfaces

The pattern to set a default enum value in an interface is MyEnum & (*"foo" | _). It couldn't intuitive but its the way to differentiate it from an union of elements. Example:

CUE TypeScript
MyEnum: "foo" | "bar" | "baz" @cuetsy(kind="enum")

MyInterface: {
  enumWithDefault: MyEnum & (*"foo" | _)
  union:           MyEnum | *"foo"
} @cuetsy(kind="interface")
export enum MyEnum {
  Bar = 'bar',
  Baz = 'baz',
  Foo = 'foo',
}

export interface MyInterface {
  enumWithDefault: MyEnum;
  union: (MyEnum | 'foo');
}

export const defaultMyInterface: Partial<MyInterface> = {
  enumWithDefault: MyEnum.Foo,
  union: 'foo',
}

Unions

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):

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

Using unions in interfaces

CUE TypeScript
MyUnion: 1 | 2 | 3 | 4 | 5 | 6 @cuetsy(kind="type")

MyInterface: {
  typeUnion: MyUnion
  typeUnionWithDefault: MyUnion & (*1 | _)
  typeUnionValue: MyUnion & 1
} @cuetsy(kind="interface")
export type MyUnion = 1 | 2 | 3 | 4 | 5 | 6;

export interface MyInterface {
  typeUnion: MyUnion;
  typeUnionValue: 1;
  typeUnionWithDefault: MyUnion;
}

export const defaultMyInterface = {
  typeUnionWithDefault: 1,
}

Defaults in union types are using the same pattern than enums.