PONY λ M2 Modula-2
for TypeScript programmers

You already know TypeScript.Now explore other languages.

Side-by-side, interactive cheatsheets for TypeScript programmers
comparing TypeScript to other languages. Every example runs live in your browser — no setup, no installation.

▶ Start with Go Browse comparisons ↓

Choose your own path by reordering languages

Go Pre-Alpha

Static types you already love, plus true parallelism and one deployable binary. A TypeScript developer moving to Go keeps compile-time type safety and inference, but trades the event loop and structural typing for goroutines, nominal types with zero values, and errors as ordinary return values.

  • Goroutines and channels run on every CPU core — Go offers real parallelism where async/await is concurrency on a single thread
  • Errors are returned values checked with if err != nil — there is no throw/catch for ordinary failures, and the signature shows what can fail
  • Every type has a zero value, so an uninitialized variable is 0 or "" — never undefined; no strictNullChecks needed
  • Interfaces are satisfied implicitly, like structural typing, but checked by the compiler — and there is no any escape hatch
  • Compiles to one static binary with no node_modules and no transpile step — go build is the whole toolchain
JavaScript Alpha ⚡ Works Offline ⚡ Offline

You already know JavaScript — this page is about what is actually real at runtime. What the compiler erases, what it quietly emits as running code, where the types stop protecting you, and how to keep type safety in plain JavaScript with JSDoc and no build step.

  • What disappears: annotations, interface/type, generics, as, satisfies, and ! are all erased — and private is only advisory
  • What emits code: enum builds a runtime object, namespace an IIFE, and parameter properties generate this.x = x
  • Why types cannot be trusted at runtime — JSON.parse/fetch return any, indexing lies, and you cannot instanceof an interface
  • What is real ECMAScript, not TypeScript: #private fields, ?., and ?? survive compilation and run in plain JS
  • Keeping type safety without a build step — JSDoc @typedef/@param checked by tsc, plus runtime validation (zod) at the boundary
Python Beta ⚡ Works Offline ⚡ Offline

The dynamic language a TypeScript developer reaches for in data, scripting, and ML. Python keeps annotations optional and unenforced, trading compile-time guarantees and structural typing for whitespace blocks, comprehensions, and a runtime that just runs — no build step at all.

  • Type hints use the same name: type / | None syntax, but they are advisory — mypy checks them, the interpreter ignores them
  • List, dict, and set comprehensions fold filter/map into one expression, replacing chained array methods
  • Real keyword and default arguments instead of an options object; match does structural patterns like a discriminated union
  • Indentation defines blocks — no braces, no semicolons — and empty collections are falsy, so if not items tests for empty
  • @dataclass gives value equality and a constructor for free, the closest analog to a TypeScript record-shaped interface
Kotlin Pre-Alpha

Kotlin gives TypeScript's null-safety promise real teeth. The | undefined/?./?? discipline you already know is erased the moment your code runs; Kotlin's String?/?./?: is enforced by the compiler down to a runtime guarantee. Add data classes with free structural equality, exhaustive when on sealed classes for your discriminated unions, and real extension functions — all on the JVM.

  • Null safety that survives to runtime — TypeScript's strict-null-checks vanishes when the code runs; Kotlin's String vs String? is a real, compiler-and-runtime-enforced distinction, not just a build-time discipline
  • Data classes generate real equality — data class Person(val name: String, val age: Int) gets working equals/hashCode/copy() at runtime; TypeScript types are erased, so equivalent behavior needs hand-written code or a library
  • Sealed classes and exhaustive when — a strong, direct parallel to a TypeScript discriminated union narrowed by an exhaustive switch, but the exhaustiveness check is automatic instead of an opt-in never trick
  • Extension functions — a real language feature for adding methods to existing types; TypeScript's closest analog, global module augmentation, patches a shared prototype instead of scoping cleanly
  • Coroutines and structured concurrency — suspend fun reads like async/await, but a coroutineScope guarantees every child coroutine finishes or is cancelled together, a guarantee Promise chains do not give you
Rust Pre-Alpha

The type discipline taken all the way down. A TypeScript developer learning Rust keeps generics, closures, and discriminated unions, but the checks now run at compile time over real memory: ownership replaces the garbage collector, and Option/Result replace null and throw.

  • Option<T> replaces T | undefined and Result<T, E> replaces throw — absence and failure are values the compiler forces you to handle
  • Ownership and borrowing replace the garbage collector — deterministic cleanup and data races caught at compile time, with no runtime
  • Enums are true sum types with exhaustive match — discriminated unions made first-class, and the compiler rejects an unhandled case
  • Traits with bounded generics are like interfaces, but implemented explicitly and monomorphised — no structural any to fall back on
  • Iterator adapters (map/filter/collect) are lazy, zero-cost pipelines that read like the array methods you already use
Swift Pre-Alpha

Structural typing meets nominal typing, at compile time and at runtime. Swift keeps TypeScript's appetite for static types but enforces them in the compiled binary, replaces erased unions with real enums, and gives Optional<T> the teeth TypeScript's | undefined never had.

  • interface/type are structural — any matching shape qualifies; Swift's protocol is nominal — a type must explicitly declare conformance
  • TypeScript's optional ?/| undefined is erased at runtime and easily bypassed with !; Swift's Optional<T> is a real type enforced by if let/guard let/??, and its own force-unwrap ! crashes loudly instead of failing silently
  • Discriminated unions with a hand-maintained tag field become Swift enum cases with associated values — a close conceptual parallel, but compiler-verified rather than convention-based
  • TypeScript classes are always reference types; Swift defaults to value-type structs and reserves classes for genuine reference semantics — a distinction JS/TS developers, who only know objects-by-reference, have to learn from scratch
  • async/await syntax is nearly identical — a genuine bridge point — but Swift's structured concurrency (Task, actors) enforces data-race safety that TypeScript's single-threaded event loop never had to think about
  • TypeScript's any/unknown escape hatches have no real Swift equivalent — Swift's type system cannot be turned off, even for Any
C# Pre-Alpha

The language TypeScript was modeled on — same lead designer, Anders Hejlsberg. C# gives a TypeScript developer the type system they already love, but reified at runtime and compiled to native-speed IL: nominal interfaces, real generics, value types, and records, with no erasure and no any.

  • Generics are reified — typeof(T) works at runtime, unlike TypeScript's erased type parameters
  • Nullable reference types (string?, ?., ??) are exactly strictNullChecks — the operators TypeScript borrowed from C#
  • record types bring value equality and a with expression; switch expressions add exhaustive, type-narrowing pattern matching
  • LINQ is filter/map/reduce as lazy Where/Select/Aggregate over any sequence
  • Interfaces are nominal and checked — a type must declare : IFoo, where TypeScript accepts any matching shape
Roc Pre-Alpha

The type system you wish TypeScript had, compiled to native code. Roc is structurally typed with whole-program inference — just like TypeScript — but its types are sound and never erased: no any, no as casts, no null, no undefined, and no runtime surprises the compiler signed off on.

  • Tag unions are discriminated unions perfected — no kind: field to maintain, exhaustiveness checked by match instead of a never-typed default-case trick
  • No null and no undefinedTry(ok, err) and ad-hoc tag unions replace T | undefined, and Roc's ?? operator works just like the one you know
  • Structural typing throughout — records work like TypeScript objects, so your instincts about shapes and type aliases transfer directly
  • Function coloring done right — effectful functions carry a ! suffix the way async marks asynchrony, except the compiler enforces it for every side effect
  • No garbage collector — compile-time reference counting frees memory deterministically, with no GC pauses and no tuning
  • Errors are typed values, not exceptions — a function's failure modes appear in its signature instead of vanishing into catch (problem: unknown)
Ruby ⚡ Works Offline ⚡ Offline

What a TypeScript developer reaches for when the type system stops earning its keep. Ruby drops annotations, the compile step, and structural typing for duck typing, blocks, and an object model so uniform that even integers and nil are real objects with methods — the language behind Rails.

  • Blocks instead of arrow-function callbacks — numbers.map { |number| number * 2 }, with the &:upcase shorthand for one-method calls
  • Everything is an object with a class, including 42 and nil — no primitive/wrapper split, no undefined; only nil and false are falsy
  • Real keyword arguments and "endless" one-line methods (def square(x) = x * x) replace options objects and arrow functions
  • Structural pattern matching with case/in — destructure hashes and arrays the way you would with a discriminated union, plus guards
  • Open classes and method_missing give runtime metaprogramming that a Proxy only approximates
Drag cards to reorder · your order is saved locally