How to Exclude Specific Go Fields from TypeScript Generation in tsgo

Add a json:"-" or tsgo:"-" struct tag to any exported Go field to prevent it from appearing in the generated TypeScript declarations.

The tsgo command-line tool in the microsoft/typescript-go repository walks your Go AST and converts exported struct fields into TypeScript interface members. When you need to hide specific fields from the resulting .d.ts output—such as sensitive data or internal implementation details—struct tags provide the control mechanism without modifying field visibility in Go.

How Field Visibility Maps to TypeScript Output

The generator follows the same visibility rules as the Go JSON encoder when deciding which fields appear in the output. In internal/transformers/tstransforms/typeeraser.go, the AST walker checks each struct field against these criteria:

  • Exported fields (starting with uppercase letter): Included by default
  • Unexported fields (starting with lowercase letter): Automatically excluded
  • Exported fields with omission tags: Excluded when tagged with json:"-" or tsgo:"-"

This means any exported field you mark with a dash (-) as the first option in its tag will be skipped during TypeScript generation.

Methods to Exclude Go Fields from TypeScript Output

You have two primary tag-based approaches to suppress field emission, depending on whether you want to affect JSON marshaling simultaneously.

Using the Standard json:"-" Tag

The most common method reuses the standard Go JSON omit marker. When tsgo encounters json:"-", it interprets this as a universal "do not emit this field" signal across all external representations.

package model

type User struct {
    ID       int    `json:"id"`
    Name     string `json:"name"`
    Password string `json:"-"`  // Excluded from .d.ts output
}

Using the Custom tsgo:"-" Tag

For cases where you want to hide a field only from TypeScript declarations while keeping it in JSON output, use the tsgo tag. The generator specifically checks for a tag named tsgo and treats - as the omit marker.

type Config struct {
    Host      string `json:"host"`
    DebugMode bool   `tsgo:"-"`  // Hidden from TypeScript, visible in Go
}

Practical Code Examples

Here is a complete example showing multiple exclusion patterns in a single struct:

package model

import "time"

// User represents a system user with selective TypeScript exposure.
type User struct {
    ID          int       `json:"id"`                // → id: number;
    Name        string    `json:"name"`              // → name: string;
    Email       string    `json:"email"`             // → email: string;
    Password    string    `json:"-"`                 // **Excluded** from .d.ts
    InternalKey string    `tsgo:"-"`                 // **Excluded** from .d.ts only
    CreatedAt   time.Time `json:"created_at"`        // → created_at: Date;
}

Running tsgo (or go run ./cmd/tsgo) on this package produces:

export interface User {
    id: number;
    name: string;
    email: string;
    created_at: Date;
}

Notice that both Password and InternalKey are absent from the generated interface.

Advanced Tag Combinations

You can combine tags to fine-tune behavior across JSON serialization and TypeScript generation.

Keeping JSON but Hiding TypeScript

Use both tags when you want a field included in JSON payloads but omitted from type declarations:

type Session struct {
    Token     string `json:"token" tsgo:"-"`  // In JSON, hidden from .d.ts
    ExpiresAt int64  `json:"expires_at"`
}

Renaming While Preserving Other Fields

If you need to rename a field for TypeScript while excluding others, provide the desired name in the primary tag:

type Event struct {
    RawData     []byte `tsgo:"-"`                // Completely hidden
    Timestamp   int64  `json:"eventTime"`        // → eventTime: number;
}

Implementation Details in the Source Code

The exclusion logic resides in internal/transformers/tstransforms/typeeraser.go, which walks the Go AST and determines which members to preserve for the declaration file. The code checks struct tags for the omission marker - and filters accordingly.

Additional relevant files in the generation pipeline include:

Summary

  • Unexported fields (lowercase) are automatically excluded from TypeScript generation
  • Add json:"-" to exclude a field from both JSON and TypeScript output
  • Add tsgo:"-" to exclude a field only from TypeScript declarations while preserving JSON behavior
  • Combine tags (json:"name" tsgo:"-") to include data in JSON payloads but hide types from consumers
  • The logic is implemented in internal/transformers/tstransforms/typeeraser.go following Go's JSON encoding conventions

Frequently Asked Questions

Can I exclude a field from TypeScript but include it in JSON output?

Yes. Use the tsgo:"-" tag without a json:"-" tag. This hides the field from the generated .d.ts file while allowing standard JSON marshaling to include the field in API responses.

Why does json:"-" affect TypeScript generation?

The tsgo generator reuses Go's JSON tagging logic to determine external field names. When the JSON tag is exactly -, the field is considered ignored for any external representation, so tsgo follows that convention for its own output as implemented in internal/transformers/tstransforms/typeeraser.go.

Do I need to use both json:"-" and tsgo:"-" tags?

No. Either tag will exclude the field from TypeScript declarations. Use json:"-" when you want to suppress both JSON serialization and TypeScript generation. Use tsgo:"-" when you need the field in JSON but hidden from TypeScript consumers.

How do I exclude multiple fields from the same struct?

Apply the exclusion tag to each field individually. There is no struct-level exclusion mechanism; you must tag each field you want to hide with either json:"-" or tsgo:"-".

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →