• Stars
    star
    592
  • Rank 75,570 (Top 2 %)
  • Language
    Go
  • License
    MIT License
  • Created over 2 years ago
  • Updated 4 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Generate Typescript types from Golang source code

🎑 tygo

Tygo is a tool for generating Typescript typings from Golang source files that just works.

It preserves comments, understands constants and also supports non-struct type expressions. It's perfect for generating equivalent types for a Golang REST API to be used in your front-end codebase.

🚀 Now supports Golang 1.18 generic types, struct inheritance

Installation

# Go >= 1.17
go install github.com/gzuidhof/tygo@latest
# Go < 1.17:
go install github.com/gzuidhof/tygo

Example

Golang input file

// Comments are kept :)
type ComplexType map[string]map[uint16]*uint32

type UserRole = string
const (
	UserRoleDefault UserRole = "viewer"
	UserRoleEditor  UserRole = "editor" // Line comments are also kept
)

type UserEntry struct {
	// Instead of specifying `tstype` we could also declare the typing
	// for uuid.NullUUID in the config file.
	ID uuid.NullUUID `json:"id" tstype:"string | null"`

	Preferences map[string]struct {
		Foo uint32 `json:"foo"`
		// An unknown type without a `tstype` tag or mapping in the config file
		// becomes `any`
		Bar uuid.UUID `json:"bar"`
	} `json:"prefs"`

	MaybeFieldWithStar *string  `json:"address"`
	Nickname           string   `json:"nickname,omitempty"`
	Role               UserRole `json:"role"`

	Complex    ComplexType `json:"complex"`
	unexported bool        // Unexported fields are omitted
	Ignored    bool        `tstype:"-"` // Fields with - are omitted too
}

type ListUsersResponse struct {
	Users []UserEntry `json:"users"`
}

Typescript output

/**
 * Comments are kept :)
 */
export type ComplexType = {
  [key: string]: {
    [key: number /* uint16 */]: number /* uint32 */ | undefined;
  };
};
export type UserRole = string;
export const UserRoleDefault: UserRole = "viewer";
export const UserRoleEditor: UserRole = "editor"; // Line comments are also kept
export interface UserEntry {
  /**
   * Instead of specifying `tstype` we could also declare the typing
   * for uuid.NullUUID in the config file.
   */
  id: string | null;
  prefs: {
    [key: string]: {
      foo: number /* uint32 */;
      /**
       * An unknown type without a `tstype` tag or mapping in the config file
       * becomes `any`
       */
      bar: any /* uuid.UUID */;
    };
  };
  address?: string;
  nickname?: string;
  role: UserRole;
  complex: ComplexType;
}
export interface ListUsersResponse {
  users: UserEntry[];
}

For a real baptism by fire example, here is a Gist with output for the Go built-in net/http and time package.

Usage

Option A: CLI (recommended)

Create a file tygo.yaml in which you specify which packages are to be converted and any special type mappings you want to add.

packages:
  - path: "github.com/gzuidhof/tygo/examples/bookstore"
    type_mappings:
      time.Time: "string /* RFC3339 */"
      null.String: "null | string"
      null.Bool: "null | boolean"
      uuid.UUID: "string /* uuid */"
      uuid.NullUUID: "null | string /* uuid */"

Then run

tygo generate

The output Typescript file will be next to the Go source files.

Option B: Library-mode

config := &tygo.Config{
  Packages: []*tygo.PackageConfig{
      &tygo.PackageConfig{
          Path: "github.com/gzuidhof/tygo/examples/bookstore",
      },
  },
}
gen := tygo.New(config)
err := gen.Generate()

Config

# You can specify more than one package
packages:
  # The package path just like you would import it in Go
  - path: "github.com/my/package"

    # Where this output should be written to.
    # If you specify a folder it will be written to a file `index.ts` within that folder. By default it is written into the Golang package folder.
    output_path: "webapp/api/types.ts"

    # Customize the indentation (use \t if you want tabs)
    indent: "    "

    # Specify your own custom type translations, useful for custom types, `time.Time` and `null.String`.
    # Be default unrecognized types will be `any`.
    type_mappings:
      time.Time: "string"
      my.Type: "SomeType"

    # This content will be put at the top of the output Typescript file, useful for importing custom types.
    frontmatter: |
      "import {SomeType} from "../lib/sometype.ts"

    # Filenames of Go source files that should not be included
    # in the output.
    exclude_files:
      - "private_stuff.go"

See also the source file tygo/config.go.

Type hints through tagging

You can tag struct fields with tstype to specify their output Typescript type.

Custom type mapping

// Golang input

type Book struct {
	Title    string    `json:"title"`
	Genre    string    `json:"genre" tstype:"'novel' | 'crime' | 'fantasy'"`
}
// Typescript output

export interface Book {
  title: string;
  genre: "novel" | "crime" | "fantasy";
}

Alternative

You could use the frontmatter field in the config to inject export type Genre = "novel" | "crime" | "fantasy" at the top of the file, and use tstype:"Genre". I personally prefer that as we may use the Genre type more than once.

Required fields

Pointer type fields usually become optional in the Typescript output, but sometimes you may want to require it regardless.

You can add ,required to the tstype tag to mark a pointer type as required.

// Golang input
type Nicknames struct {
	Alice   *string `json:"alice"`
	Bob     *string `json:"bob" tstype:"BobCustomType,required"`
	Charlie *string `json:"charlie" tstype:",required"`
}
// Typescript output
export interface Nicknames {
  alice?: string;
  bob: BobCustomType;
  charlie: string;
}

Readonly fields

Sometimes a field should be immutable, you can add ,readonly to the tstype tag to mark a field as readonly.

// Golang input
type Cat struct {
	Name    string `json:"name,readonly"`
	Owner   string `json:"owner"`
}
// Typescript output
export interface Cat {
  readonly name: string;
  owner: string;
}

Inheritance

Tygo supports interface inheritance. To extend an inlined struct, use the tag tstype:",extends" on struct fields you wish to extend. Only struct types can be extended.

Struct pointers are optionally extended using Partial<MyType>. To mark these structs as required, use the tag tstype:",extends,required".

Named struct fields can also be extended.

Example usage here

// Golang input
import "example.com/external"

type Base struct {
	Name string `json:"name"`
}

type Base2[T string | int] struct {
	ID T `json:"id"`
}

type OptionalPtr struct {
	Field string `json:"field"`
}

type Other[T int] struct {
	*Base                  `       tstype:",extends,required"`
	Base2[T]               `       tstype:",extends"`
	*OptionalPtr           `       tstype:",extends"`
	external.AnotherStruct `       tstype:",extends"`
	OtherValue             string `                  json:"other_value"`
}
// Typescript output
export interface Base {
  name: string;
}

export interface Base2<T extends string | number /* int */> {
  id: T;
}

export interface OptionalPtr {
  field: string;
}

export interface Other<T extends number /* int */>
  extends Base,
    Base2<T>,
    Partial<OptionalPtr>,
    external.AnotherStruct {
  other_value: string;
}

Generics

Tygo supports generic types (Go version >= 1.18) out of the box.

// Golang input
type UnionType interface {
	uint64 | string
}

type ABCD[A, B string, C UnionType, D int64 | bool] struct {
	A A `json:"a"`
	B B `json:"b"`
	C C `json:"c"`
	D D `json:"d"`
}
// Typescript output
export type UnionType = number /* uint64 */ | string;

export interface ABCD<
  A extends string,
  B extends string,
  C extends UnionType,
  D extends number /* int64 */ | boolean
> {
  a: A;
  b: B;
  c: C;
  d: D;
}

YAML support

Tygo supports generating typings for YAML-serializable objects that can be understood by Go apps. By default, Tygo will respect yaml Go struct tags, in addition to json, but it will not apply any transformations to untagged fields. However, the default behavior of the popular gopkg.in/yaml.v2 package for Go structs without tags is to downcase the struct field names. To emulate this behavior, one can use the flavor configuration option:

packages:
  - path: "github.com/my/package"
    output_path: "webapp/api/types.ts"
    flavor: "yaml"
// Golang input
type Foo struct {
	TaggedField string `yaml:"custom_field_name_in_yaml"`
    UntaggedField string
}
// Typescript output
export interface Foo {
  custom_field_name_in_yaml: string;
  untaggedfield: string;
}

Related projects

  • typescriptify-golang-structs: Probably the most popular choice. The downside of this package is that it relies on reflection rather than parsing, which means that certain things can't be kept such as comments without adding a bunch of tags to your structs. The CLI generates a Go file which is then executed and reflected on, and its library requires you to manually specify all types that should be converted.
  • go2ts: A transpiler with a web interface, this project was based off this project. It's perfect for quick one-off transpilations. There is no CLI, no support for const and there are no ways to customize the output.

If this repository was useful for your project, consider leaving a star.

License

MIT

More Repositories

1

starboard-notebook

In-browser literate notebooks
TypeScript
1,217
star
2

coi-serviceworker

Cross-origin isolation (COOP and COEP) through a service worker for situations in which you can't control the headers (e.g. GH pages)
JavaScript
369
star
3

nn-transfer

Convert trained PyTorch models to Keras, and the other way around
Python
226
star
4

luna16

LUNA16 Lung Nodule Analysis - NWI-IMC037 Final Project
Python
183
star
5

zarr.js

Javascript implementation of Zarr
TypeScript
132
star
6

GConsole

Developer Console for Unity3D
C#
101
star
7

starboard-cli

Starboard CLI for local (offline) editing of notebooks
CSS
20
star
8

LD49

TypeScript
20
star
9

starboard-observable

Observable cells in Starboard Notebook. Why not?
TypeScript
20
star
10

jpeg-artifact-reduction

Jupyter Notebook
16
star
11

starboard-jupyter

Plugin to run Starboard cells in an external Jupyter kernel
TypeScript
15
star
12

starboard-wrap

🌯A small library that wraps a Starboard Notebook iframe in the parent webpage
TypeScript
14
star
13

wasmwrap

Wrap WASM into JS files as base64
TypeScript
13
star
14

hx-ash

Entity Component Framework
Haxe
12
star
15

jupystar

Convert Jupyter notebooks (ipynb) to Starboard notebooks
Jupyter Notebook
8
star
16

starboard-python

Plugin that adds Python cells to Starboard
8
star
17

no-free-plan

HTML
8
star
18

rollup-plugin-base64

A rollup plugin that allows you to import files as a base64 string
JavaScript
7
star
19

flipper

🐬 Flipper is a monitoring service that automatically re-points floating IPs at healhy targets.
Go
7
star
20

li-hackathon

48 hour hackathon project: From RU with Love
Vue
6
star
21

nasync-js

What if JS didn't have await and async, because everything is async and awaited?
TypeScript
5
star
22

starlit

Go
4
star
23

WebMetaverse

Supposedly the 3D world wide web.
JavaScript
3
star
24

whole-slide-breast-cancer-classification

Resulting publication http://spie.org/Publications/Journal/10.1117/1.JMI.4.4.044504
Jupyter Notebook
3
star
25

HRI

Human Robot Interaction course project (robot cooking assistant with NLP)
Python
3
star
26

starcloud

CDN origin for Starboard assets with the correct headers set
Go
2
star
27

advent-of-code-2020

Advent of Code 2020 Solutions
2
star
28

LD30

"Connected Worlds"
C#
2
star
29

GConsoleNGUI

NGUI Frontend for GConsole
C#
2
star
30

ai-at-the-web-scale

Final project for "AI at the web scale" course (profit optimization for webshop -> contextual bandit problem)
Python
1
star
31

magiclogin-docs

Docs for https://magiclogin.net
1
star
32

excess

Performant WebRTC signalling server (written in Elixir)
Elixir
1
star
33

gamejam

GAMEJAM games
ASP
1
star
34

Mash

Mash, 2D Game Engine with Ash Entity System Framework
Haxe
1
star