Skip to content

Coming from TypeScript ​

Kinmokusei deliberately feels familiar to TypeScript readers, but familiarity stops at the source surface. The runtime, package graph, type boundaries, and generated artifacts are Go.

Quick comparison ​

TopicFamiliar shapeKinmokusei contract
Annotationsname: stringStatic types checked for predictable Go generation
Functions and arrowsfunction and =>Explicit result types at public boundaries; Go-compatible callbacks
Classes and interfacesclass, interface, visibilityReference classes lower to Go structs/methods/interfaces; no prototype model
Objects{ name: string }Structural value objects generate anonymous Go structs with JSON tags
Numbersnumber spelling existsnumber is float64; int and fixed-width integers remain distinct
StringsDouble-quoted literals and +Verified UTF-8 string, raw bstring; byte length/indexing, checked text slicing, code-point range
NullT | nullOnly nil-backed reference types; flow proof required before access
ErrorsPromise rejection / thrown valuesExplicit Result<T>, raw Go error, or typed exceptions
AsyncPromise<T> / awaitNon-escaping, exactly-once Task<T>; not a Promise runtime
Modulesnpm/ES modulesRelative or locked external .km modules, source exports and explicit Go imports
Propertiesget / set, staticChecked accessor calls; shared static fields/constants independent of generic instantiations
Decorators@Name / @Factory(...)Typed package-initialization registration with checked construction/invocation adapters

There is no JavaScript runtime ​

Kinmokusei does not provide JavaScript objects, prototypes, the DOM, Node.js globals, npm packages, dynamic property lookup, or TypeScript erasure semantics. A source construct is accepted only when the compiler can give it a defined Kinmokusei meaning and predictable Go representation.

This is valid because strings is a real Go namespace:

ts
import go strings from "strings";

function normalize(value: string): Result<string> {
  return string(strings.ToUpper(strings.TrimSpace(value)));
}

Choose data by runtime behavior ​

TypeScript developers often use one object/class model for many jobs. Kinmokusei separates three shapes:

  • A structural object is an anonymous data value with deterministic JSON tags.
  • A native struct is a nominal value copied with Go struct rules.
  • A class is a reference with identity, visibility, interfaces, and optional explicit inheritance.

Slices and maps carry shared backing storage even when held inside a copied struct, so outer value copying is shallow.

number is not the universal numeric type ​

number, float, and float64 are identical. int, uint, fixed-width signed and unsigned integers, byte, and float32 are separate types. Numeric values do not silently widen or cross signedness; use an explicit conversion when Go conversion rules permit it.

Rejected implicit behavior ​

Text handling also differs: len("湯a") is 4 bytes, not two UTF-16 code units or two characters. text[0] is a byte; range yields decoded int32 code points. No .length, template interpolation or automatic Unicode normalization is provided. See the Unicode string recipe. Runtime strings from Go are bstring: use checked string(raw) decoding before treating them as UTF-8 text. A string slice cannot split a code point.

Familiar surface syntax never enables JavaScript coercion. A string condition is rejected rather than converted by truthiness:

ts
function invalid(label: string): void {
  if (label) {
    return;
  }
}

Likewise, a runtime int32 does not widen merely because the result expects int64:

ts
function invalid(value: int32): int64 {
  return value;
}

Write return int64(value); when that conversion is intended. Fixed arrays and slices are also distinct storage contracts rather than interchangeable array-like values:

ts
function invalid(values: [2]int): int[] {
  return values;
}

Use copyArray or viewArray for the supported slice-to-fixed-array direction. Choose a slice in the function contract when variable length is intended.

Result<T> is not a tagged object ​

Result<T> can appear only as a function or method return effect. It lowers to Go (T, error), while Result<void> lowers to error.

ts
function parse(text: string): Result<int> {
  const value = strconv.Atoi(text)?;
  return ok(value);
}

It cannot be stored in a variable, field, collection, or nested result. This keeps the public Go API direct.

Task<T> is not Promise<T> ​

A task is a local single-consumption capability. It cannot be copied, reassigned, captured, stored, or returned. Every continuing path must consume it exactly once with await or detach.

The callee and arguments execute synchronously once, before the worker goroutine starts. A worker panic is transported and re-panicked by await. Context propagation and cancellation remain explicit through ordinary Go context.Context values.

Null facts can expire ​

A non-null check narrows a stable local or class-field path. Later writes, aliases, mutable captures, addresses, unknown calls, and loop joins can invalidate that proof. The compiler points to the invalidating boundary; a const snapshot creates stable local identity when needed.

What to learn next ​

Start with Types and data, then read Errors and nullability and Concurrency. Those three pages cover the largest semantic differences from TypeScript.

Kinmokusei is a pre-1.0 project. Documentation describes implemented behavior.