diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..312925e
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,23 @@
+name: Test Tygo
+on:
+ pull_request:
+ push:
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ go-version:
+ - 1.22.x
+ steps:
+ - name: checkout
+ uses: actions/checkout@v4
+ - name: install
+ uses: actions/setup-go@v5
+ with:
+ go-version: '${{ matrix.go-version }}'
+ - name: vet
+ run: go vet ./...
+ - name: test
+ run: go test -v -race ./...
\ No newline at end of file
diff --git a/config/config.go b/config/config.go
index d2cad77..2eadd0a 100644
--- a/config/config.go
+++ b/config/config.go
@@ -8,9 +8,6 @@ import (
"gopkg.in/yaml.v2"
)
-const defaultFallbackType = "any"
-const defaultPreserveComments = "default"
-
func ReadFromFilepath(cfgFilepath string) tygo.Config {
b, err := ioutil.ReadFile(cfgFilepath)
if err != nil {
@@ -22,16 +19,5 @@ func ReadFromFilepath(cfgFilepath string) tygo.Config {
log.Fatalf("Could not parse config file from: %v", err)
}
- // apply defaults
- for _, packageConf := range conf.Packages {
- if packageConf.FallbackType == "" {
- packageConf.FallbackType = defaultFallbackType
- }
-
- if packageConf.PreserveComments == "" {
- packageConf.PreserveComments = defaultPreserveComments
- }
- }
-
return conf
}
diff --git a/go.mod b/go.mod
index 4c2d47b..704bb6e 100644
--- a/go.mod
+++ b/go.mod
@@ -8,9 +8,12 @@ require (
)
require (
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/mod v0.5.1 // indirect
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
)
require (
@@ -18,6 +21,7 @@ require (
github.com/google/uuid v1.3.0
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
+ github.com/stretchr/testify v1.9.0
golang.org/x/tools v0.1.9
gopkg.in/guregu/null.v4 v4.0.0
)
diff --git a/go.sum b/go.sum
index e3f22f3..b622dc8 100644
--- a/go.sum
+++ b/go.sum
@@ -89,6 +89,7 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@@ -281,6 +282,7 @@ github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
@@ -322,6 +324,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -762,6 +766,8 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/tygo/config.go b/tygo/config.go
index 0505450..105b86a 100644
--- a/tygo/config.go
+++ b/tygo/config.go
@@ -8,6 +8,8 @@ import (
)
const defaultOutputFilename = "index.ts"
+const defaultFallbackType = "any"
+const defaultPreserveComments = "default"
type PackageConfig struct {
// The package path just like you would import it in Go
@@ -77,27 +79,12 @@ func (c Config) PackageNames() []string {
func (c Config) PackageConfig(packagePath string) *PackageConfig {
for _, pc := range c.Packages {
if pc.Path == packagePath {
- if pc.Indent == "" {
- pc.Indent = " "
- }
-
- var err error
- pc.Flavor, err = normalizeFlavor(pc.Flavor)
- if err != nil {
- log.Fatalf("Invalid config for package %s: %s", packagePath, err)
- }
-
- pc.PreserveComments, err = normalizePreserveComments(pc.PreserveComments)
+ pcNormalized, err := pc.Normalize()
if err != nil {
- log.Fatalf("Invalid config for package %s: %s", packagePath, err)
+ log.Fatalf("Error in config for package %s: %s", packagePath, err)
}
- pc.OptionalType, err = normalizeOptionalType(pc.OptionalType)
- if err != nil {
- log.Fatalf("Invalid config for package %s: %s", packagePath, err)
- }
-
- return pc
+ return &pcNormalized
}
}
log.Fatalf("Config not found for package %s", packagePath)
@@ -168,3 +155,36 @@ func (c PackageConfig) ResolvedOutputPath(packageDir string) string {
}
return c.OutputPath
}
+
+// Normalize returns a new PackageConfig with default values set.
+func (pc PackageConfig) Normalize() (PackageConfig, error) {
+ if pc.Indent == "" {
+ pc.Indent = " "
+ }
+
+ if pc.FallbackType == "" {
+ pc.FallbackType = defaultFallbackType
+ }
+
+ if pc.PreserveComments == "" {
+ pc.PreserveComments = defaultPreserveComments
+ }
+
+ var err error
+ pc.Flavor, err = normalizeFlavor(pc.Flavor)
+ if err != nil {
+ return pc, fmt.Errorf("invalid flavor config for package %s: %s", pc.Path, err)
+ }
+
+ pc.PreserveComments, err = normalizePreserveComments(pc.PreserveComments)
+ if err != nil {
+ return pc, fmt.Errorf("invalid preserve_comments config for package %s: %s", pc.Path, err)
+ }
+
+ pc.OptionalType, err = normalizeOptionalType(pc.OptionalType)
+ if err != nil {
+ return pc, fmt.Errorf("invalid optional_type config for package %s: %s", pc.Path, err)
+ }
+
+ return pc, nil
+}
diff --git a/tygo/convert_string.go b/tygo/convert_string.go
new file mode 100644
index 0000000..60ea801
--- /dev/null
+++ b/tygo/convert_string.go
@@ -0,0 +1,41 @@
+package tygo
+
+import (
+ "fmt"
+ "go/parser"
+ "go/token"
+ "strings"
+)
+
+// ConvertGoToTypescript converts Go code string to Typescript.
+//
+// This is mostly useful for testing purposes inside tygo itself.
+func ConvertGoToTypescript(goCode string, pkgConfig PackageConfig) (string, error) {
+ src := fmt.Sprintf(`package tygoconvert
+
+%s`, goCode)
+
+ fset := token.NewFileSet()
+
+ f, err := parser.ParseFile(fset, "", src, parser.AllErrors|parser.ParseComments)
+ if err != nil {
+ return "", fmt.Errorf("failed to parse source: %w", err)
+ }
+
+ pkgConfig, err = pkgConfig.Normalize()
+ if err != nil {
+ return "", fmt.Errorf("failed to normalize package config: %w", err)
+ }
+
+ pkgGen := &PackageGenerator{
+ conf: &pkgConfig,
+ pkg: nil,
+ }
+
+ s := new(strings.Builder)
+
+ pkgGen.generateFile(s, f, "")
+ code := s.String()
+
+ return code, nil
+}
diff --git a/tygo/fixtures_test.go b/tygo/fixtures_test.go
new file mode 100644
index 0000000..117856f
--- /dev/null
+++ b/tygo/fixtures_test.go
@@ -0,0 +1,113 @@
+package tygo
+
+import (
+ "embed"
+ "fmt"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "gopkg.in/yaml.v3"
+)
+
+// Embed markdown test fixtures
+//
+//go:embed testdata/fixtures/*.md
+var mdfs embed.FS
+
+type MarkdownFixture struct {
+ PackageConfig PackageConfig
+ GoCode string
+ TsCode string
+}
+
+func TestConvertGoToTypescriptSmoketest(t *testing.T) {
+ t.Parallel()
+
+ goCode := "type MyType uint8"
+ tsCode, err := ConvertGoToTypescript(goCode, PackageConfig{})
+ require.NoError(t, err)
+
+ expected := `export type MyType = number /* uint8 */;
+`
+ assert.Equal(t, expected, tsCode)
+}
+
+func parseMarkdownFixtures(fileContents []byte) ([]MarkdownFixture, error) {
+ fixtures := make([]MarkdownFixture, 0)
+ currentFixture := MarkdownFixture{}
+
+ currentBlockContents := ""
+ currentBlockLanguage := ""
+ inCodeBlock := false
+ for _, line := range strings.Split(string(fileContents), "\n") {
+ if strings.HasPrefix(line, "```") {
+ if inCodeBlock {
+ // End of code block
+ if currentBlockLanguage == "ts" || currentBlockLanguage == "typescript" {
+ // Every fixture ends with a typescript block
+ currentFixture.TsCode = currentBlockContents
+ fixtures = append(fixtures, currentFixture)
+ currentFixture = MarkdownFixture{}
+ } else if currentBlockLanguage == "go" {
+ currentFixture.GoCode = currentBlockContents
+ } else if currentBlockLanguage == "yml" || currentBlockLanguage == "yaml" {
+ // Parse package config
+ pc := PackageConfig{}
+ err := yaml.Unmarshal([]byte(currentBlockContents), &pc)
+ if err != nil {
+ return nil, fmt.Errorf("failed to unmarshal package config: %w", err)
+ }
+ currentFixture.PackageConfig = pc
+ }
+ currentBlockContents = ""
+ currentBlockLanguage = ""
+ } else { // Start of code block
+ language := strings.TrimPrefix(line, "```")
+ language = strings.TrimSpace(language)
+ currentBlockLanguage = language
+ }
+ inCodeBlock = !inCodeBlock
+ continue
+ }
+
+ if inCodeBlock {
+ currentBlockContents += line + "\n"
+ }
+ }
+
+ return fixtures, nil
+
+}
+
+// Tests all markdown files in `testdata/fixtures/` directory.
+func TestMarkdownFixtures(t *testing.T) {
+ t.Parallel()
+
+ fixtures, err := mdfs.ReadDir("testdata/fixtures")
+ require.NoError(t, err)
+
+ for _, fixture := range fixtures {
+ fixture := fixture
+
+ // Read markdown file
+ md, err := mdfs.ReadFile("testdata/fixtures/" + fixture.Name())
+ require.NoError(t, err)
+
+ testCases, err := parseMarkdownFixtures(md)
+ require.NoError(t, err)
+
+ for _, tc := range testCases {
+ tc := tc
+ t.Run(fixture.Name(), func(t *testing.T) {
+ t.Parallel()
+
+ tsCode, err := ConvertGoToTypescript(tc.GoCode, tc.PackageConfig)
+ require.NoError(t, err)
+
+ assert.Equal(t, tc.TsCode, tsCode)
+ })
+ }
+ }
+}
diff --git a/tygo/generator.go b/tygo/generator.go
index c4137f7..788453d 100644
--- a/tygo/generator.go
+++ b/tygo/generator.go
@@ -2,7 +2,6 @@ package tygo
import (
"fmt"
- "io/ioutil"
"os"
"path/filepath"
@@ -73,7 +72,7 @@ func (g *Tygo) Generate() error {
return nil
}
- err = ioutil.WriteFile(outPath, []byte(code), 0664)
+ err = os.WriteFile(outPath, []byte(code), 0664)
if err != nil {
return nil
}
diff --git a/tygo/package_generator.go b/tygo/package_generator.go
index 615f985..9af6a8a 100644
--- a/tygo/package_generator.go
+++ b/tygo/package_generator.go
@@ -6,6 +6,43 @@ import (
"strings"
)
+// generateFile writes the generated code for a single file to the given strings.Builder.
+func (g *PackageGenerator) generateFile(s *strings.Builder, file *ast.File, filepath string) {
+ first := true
+
+ ast.Inspect(file, func(n ast.Node) bool {
+ switch x := n.(type) {
+
+ // GenDecl can be an import, type, var, or const expression
+ case *ast.GenDecl:
+ if x.Tok == token.IMPORT {
+ return false
+ }
+ isEmit := false
+ if x.Tok == token.VAR {
+ isEmit = g.isEmitVar(x)
+ if !isEmit {
+ return false
+ }
+ }
+
+ if first {
+ if filepath != "" {
+ g.writeFileSourceHeader(s, filepath, file)
+ }
+ first = false
+ }
+ if isEmit {
+ g.emitVar(s, x)
+ return false
+ }
+ g.writeGroupDecl(s, x)
+ return false
+ }
+ return true
+ })
+}
+
func (g *PackageGenerator) Generate() (string, error) {
s := new(strings.Builder)
@@ -19,39 +56,7 @@ func (g *PackageGenerator) Generate() (string, error) {
continue
}
- first := true
-
- ast.Inspect(file, func(n ast.Node) bool {
- switch x := n.(type) {
-
- // GenDecl can be an import, type, var, or const expression
- case *ast.GenDecl:
- if x.Tok == token.IMPORT {
- return false
- }
- isEmit := false
- if x.Tok == token.VAR {
- isEmit = g.isEmitVar(x)
- if !isEmit {
- return false
- }
- }
-
- if first {
- g.writeFileSourceHeader(s, filepaths[i], file)
- first = false
- }
- if isEmit {
- g.emitVar(s, x)
- return false
- }
- g.writeGroupDecl(s, x)
- return false
- }
- return true
-
- })
-
+ g.generateFile(s, file, filepaths[i])
}
return s.String(), nil
diff --git a/tygo/testdata/fixtures/directive.md b/tygo/testdata/fixtures/directive.md
new file mode 100644
index 0000000..9fc3b25
--- /dev/null
+++ b/tygo/testdata/fixtures/directive.md
@@ -0,0 +1,57 @@
+This fixture is here to reproduce Github issue #26.
+
+```go
+// Comment above a directive
+//
+//go:foo
+//go:bar
+const SomeValue = 3 //comment:test
+
+// Empty Comment
+const AnotherValue = 4 //
+
+//go:something
+const DirectiveOnly = 5
+
+// RepoIndexerType specifies the repository indexer type
+type RepoIndexerType int //revive:disable-line:exported
+
+const (
+ // RepoIndexerTypeCode code indexer
+ RepoIndexerTypeCode RepoIndexerType = iota // 0
+ // RepoIndexerTypeStats repository stats indexer
+ RepoIndexerTypeStats // 1
+)
+
+const A = "a"
+const B = "a"
+const C = "c"
+```
+
+```ts
+/**
+ * Comment above a directive
+ */
+export const SomeValue = 3;
+/**
+ * Empty Comment
+ */
+export const AnotherValue = 4;
+
+export const DirectiveOnly = 5;
+/**
+ * RepoIndexerType specifies the repository indexer type
+ */
+export type RepoIndexerType = number /* int */;
+/**
+ * RepoIndexerTypeCode code indexer
+ */
+export const RepoIndexerTypeCode: RepoIndexerType = 0; // 0
+/**
+ * RepoIndexerTypeStats repository stats indexer
+ */
+export const RepoIndexerTypeStats: RepoIndexerType = 1; // 1
+export const A = "a";
+export const B = "a";
+export const C = "c";
+```
\ No newline at end of file
diff --git a/tygo/testdata/fixtures/emit.md b/tygo/testdata/fixtures/emit.md
new file mode 100644
index 0000000..da4ad27
--- /dev/null
+++ b/tygo/testdata/fixtures/emit.md
@@ -0,0 +1,58 @@
+```go
+// emit directive on a string literal emits that value.
+//
+//tygo:emit
+var _ = `export type OtherStructAsTuple=[
+ a:number,
+ b:number,
+ c:string,
+]
+`
+
+//tygo:emit This has no effect, only strings.
+var _ = 12
+
+// a non-string var is ignored. A var with no comment is ignored.
+
+var foo = " "
+
+// CustomMarshalled illustrates getting tygo to emit literal text
+// This solves the problem of a struct field being marshalled into a tuple.
+//
+// emit directive on a struct emits the remainder of the directive line
+//
+//tygo:emit export type StructAsTuple=[a:number, b:number, c:string]
+type CustomMarshalled struct {
+ Content []StructAsTuple `json:"content"`
+}
+
+//tygo:emit export type Genre = "novel" | "crime" | "fantasy"
+type Book struct {
+ Title string `json:"title"`
+ Genre string `json:"genre" tstype:"Genre"`
+}
+```
+
+```ts
+export type OtherStructAsTuple=[
+ a:number,
+ b:number,
+ c:string,
+]
+
+export type StructAsTuple=[a:number, b:number, c:string]
+/**
+ * CustomMarshalled illustrates getting tygo to emit literal text
+ * This solves the problem of a struct field being marshalled into a tuple.
+ * emit directive on a struct emits the remainder of the directive line
+ */
+export interface CustomMarshalled {
+ content: StructAsTuple[];
+}
+export type Genre = "novel" | "crime" | "fantasy"
+
+export interface Book {
+ title: string;
+ genre: Genre;
+}
+```
\ No newline at end of file
diff --git a/tygo/testdata/fixtures/generic.md b/tygo/testdata/fixtures/generic.md
new file mode 100644
index 0000000..a39c76d
--- /dev/null
+++ b/tygo/testdata/fixtures/generic.md
@@ -0,0 +1,150 @@
+# Union types and empty interfaces and types
+```yaml
+fallback_type: "unknown"
+```
+
+```go
+// Comment for UnionType
+type UnionType interface {
+ // Comment for fields are possible
+ uint64 | string | *bool // comment after
+
+ // Comment for a method
+ SomeMethod() string
+}
+
+type Derived interface {
+ ~int | string // Line comment
+}
+
+type Any interface {
+ string | any
+}
+
+type Empty interface{}
+
+type Something any
+
+type EmptyStruct struct{}
+```
+
+```ts
+/**
+ * Comment for UnionType
+ */
+export type UnionType =
+ /**
+ * Comment for fields are possible
+ */
+ number /* uint64 */ | string | boolean | undefined // comment after
+;
+export type Derived =
+ number /* int */ | string // Line comment
+;
+export type Any =
+ string | unknown;
+export type Empty = unknown;
+export type Something = any;
+export interface EmptyStruct {
+}
+```
+
+# Values and pointers
+
+```yaml
+fallback_type: "unknown"
+```
+
+```go
+
+type ValAndPtr[V any, PT *V, Unused ~uint64] struct {
+ Val V
+ // Comment for ptr field
+ Ptr PT // ptr line comment
+}
+
+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"`
+}
+
+type Foo[A string | uint64, B *A] struct {
+ Bar A
+ Boo B
+}
+
+type WithFooGenericTypeArg[A Foo[string, *string]] struct {
+ SomeField A `json:"some_field"`
+}
+
+// Should not be output as it's a function
+func (f Foo[int, Derived]) DoSomething() {
+ panic("something")
+}
+```
+
+```ts
+export interface ValAndPtr {
+ Val: V;
+ /**
+ * Comment for ptr field
+ */
+ Ptr: PT; // ptr line comment
+}
+export interface ABCD {
+ a: A;
+ b: B;
+ c: C;
+ d: D;
+}
+export interface Foo {
+ Bar: A;
+ Boo: B;
+}
+export interface WithFooGenericTypeArg> {
+ some_field: A;
+}
+```
+
+# Single
+
+```go
+type Single[S string | uint] struct {
+ Field S
+}
+
+type SingleSpecific = Single[string]
+```
+
+```ts
+export interface Single {
+ Field: S;
+}
+export type SingleSpecific = Single;
+```
+
+# Any field
+
+Example for https://github.com/gzuidhof/tygo/issues/65.
+```go
+type AnyStructField[T any] struct {
+ Value T
+ SomeField string
+}
+```
+```ts
+export interface AnyStructField {
+ Value: T;
+ SomeField: string;
+}
+```
+
+```go
+type JsonArray[T any] []T
+```
+
+```ts
+export type JsonArray = T[];
+```
\ No newline at end of file
diff --git a/tygo/testdata/fixtures/inheritance.md b/tygo/testdata/fixtures/inheritance.md
new file mode 100644
index 0000000..17ac305
--- /dev/null
+++ b/tygo/testdata/fixtures/inheritance.md
@@ -0,0 +1,45 @@
+```go
+type Base struct {
+ Name string `json:"name"`
+}
+
+type Base2[T string | int] struct {
+ ID T `json:"id"`
+}
+
+type Base3[T string, X int] struct {
+ Class T `json:"class"`
+ Level X `json:"level"`
+}
+
+type Other[T int, X string] struct {
+ *Base `tstype:",extends,required"`
+ Base2[T] `tstype:",extends"`
+ *Base3[X, T] `tstype:",extends"`
+ OtherWithBase Base ` json:"otherWithBase"`
+ OtherWithBase2 Base2[X] ` json:"otherWithBase2"`
+ OtherValue string ` json:"otherValue"`
+ Author bookapp.AuthorWithInheritance[T] `tstype:"bookapp.AuthorWithInheritance" json:"author"`
+ bookapp.Book `tstype:",extends"`
+ TextBook *bookapp.TextBook[T] `tstype:",extends,required"`
+}
+```
+
+```ts
+export interface Base {
+ name: string;
+}
+export interface Base2 {
+ id: T;
+}
+export interface Base3 {
+ class: T;
+ level: X;
+}
+export interface Other extends Base, Base2, Partial>, bookapp.Book, bookapp.TextBook {
+ otherWithBase: Base;
+ otherWithBase2: Base2;
+ otherValue: string;
+ author: bookapp.AuthorWithInheritance;
+}
+```
\ No newline at end of file
diff --git a/tygo/testdata/fixtures/simple.md b/tygo/testdata/fixtures/simple.md
new file mode 100644
index 0000000..e35833c
--- /dev/null
+++ b/tygo/testdata/fixtures/simple.md
@@ -0,0 +1,76 @@
+```go
+type MyUint8 uint8
+type MyInt int
+type MyString string
+type MyAny any
+
+// Should be a number in TypeScript.
+type MyRune rune
+```
+
+```ts
+export type MyUint8 = number /* uint8 */;
+export type MyInt = number /* int */;
+export type MyString = string;
+export type MyAny = any;
+/**
+ * Should be a number in TypeScript.
+ */
+export type MyRune = number /* rune */;
+```
+
+
+Struct with some comments
+```go
+// Comment for a struct
+type MyStruct struct {
+ SomeField any `json:"some_field"`
+ // Comment for a field
+ OtherField bool // Comment after line
+ FieldWithImportedType some.Type
+}
+```
+
+```ts
+/**
+ * Comment for a struct
+ */
+export interface MyStruct {
+ some_field: any;
+ /**
+ * Comment for a field
+ */
+ OtherField: boolean; // Comment after line
+ FieldWithImportedType: any /* some.Type */;
+}
+```
+
+No preserve comments
+```yaml
+preserve_comments: "none"
+```
+
+```go
+// Foo
+type MyValue int // Bar
+```
+
+```ts
+export type MyValue = number /* int */;
+```
+
+Empty file
+```go
+```
+
+```ts
+```
+
+Unexported
+
+```go
+const myValue = 3
+```
+
+```ts
+```
\ No newline at end of file