Skip to content

Instantly share code, notes, and snippets.

@PatrickJS
Created June 4, 2026 04:47
Show Gist options
  • Select an option

  • Save PatrickJS/05271a2b0978532c1ba72b6d84bce533 to your computer and use it in GitHub Desktop.

Select an option

Save PatrickJS/05271a2b0978532c1ba72b6d84bce533 to your computer and use it in GitHub Desktop.

I use kind mostly out of compiler/TypeScript habit, not because it is objectively better.

In TypeScript, both of these are normal discriminants:

type Route =
  | { type: 'mount'; path: string }
  | { type: 'app'; name: string };
type Route =
  | { kind: 'mount'; path: string }
  | { kind: 'app'; name: string };

They work the same for narrowing. The choice is mostly API taste, ecosystem convention, and collision avoidance.

Why People Use kind kind is common in compiler-ish code, ASTs, IRs, and internal state machines.

Reasons:

  1. Avoids overloading type type can already mean many things:

    {
      type: 'json',
      contentType: 'application/json',
      runtimeType: 'browser',
      schemaType: 'object'
    }

    If a domain already has a meaningful “type,” kind can be a cleaner discriminant.

  2. Reads like “variant kind” In a discriminated union, the field is not always a real-world type. It is often “which shape of object is this?”

    { kind: 'text-node', value: 'hello' }
    { kind: 'element-node', tag: 'div' }
  3. Internal compiler convention A lot of internal code uses concepts like:

    node.kind
    token.kind
    event.kind
    instruction.kind

    It feels natural when building parsers, compilers, runtime graphs, state machines, or intermediate representations.

  4. Less likely to clash with external schemas If user data has a type property, internal wrappers can still use kind:

    {
      kind: 'resource',
      resource: {
        type: 'user'
      }
    }
  5. Signals “internal representation” kind can make an object feel less like public JSON and more like a typed internal object.

Why People Use type type is more common in public JavaScript/JSON APIs.

Examples:

{ type: 'click' }
{ type: 'module' }
{ type: 'button' }
{ type: 'object' }
{ type: 'user.created' }

Reasons:

  1. More familiar to JS users Public config often uses type because it is obvious. Users do not stop and ask why the field is called kind.

  2. Matches event/action conventions Redux, DOM-ish events, message buses, JSON descriptors, and config objects often use type.

    { type: 'user.created', payload: user }
  3. Better for serialized JSON If something is meant to be inspected, emitted, stored, sent across process boundaries, or documented as JSON, type tends to feel natural.

  4. Schema tooling expects it JSON Schema, OpenAPI-ish thinking, and many config validators already use type language.

  5. Less compiler-y kind can feel like implementation jargon. type feels like ordinary app config.

When kind Is Better Use kind when:

  • The object is mostly internal.
  • You are building an AST, compiler IR, parser, runtime graph, or state machine.
  • The domain already needs a separate type field.
  • You want to distinguish “variant discriminant” from “domain type.”
  • Users rarely write this object by hand.

Example:

type InternalNode =
  | { kind: 'route'; routeId: string }
  | { kind: 'cache'; cacheId: string }
  | { kind: 'origin'; originUrl: string };

When type Is Better Use type when:

  • Users write the object directly.
  • The object is public API/config.
  • The value is serialized, logged, inspected, or sent over a wire.
  • You want to align with normal JS/JSON conventions.
  • There is no competing domain meaning for type.

Example:

{
  type: 'redirect',
  from: '/old',
  to: '/new'
}

When Neither Is Best Sometimes a domain-specific word is clearer than both.

Use runtime instead of type:

{
  runtime: 'browser'
}

Use mode:

{
  mode: 'development'
}

Use role:

{
  role: 'frontend'
}

Use target:

{
  target: 'cloudflare'
}

Use format:

{
  format: 'json'
}

This is usually the best rule: if a more specific noun exists, use it. Use type or kind only when the field is genuinely a generic discriminant.

Practical Rule For public JS/TS config:

Prefer domain-specific names first.
If generic discriminant is needed, prefer type.
Use kind for internal/compiler-ish structures or when type would collide.

So my earlier kind instinct came from internal TypeScript/IR habits. For a public config API, type is usually the better default unless there is a concrete collision.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment