Side-by-side, interactive cheatsheets for TypeScript programmers
comparing TypeScript to other languages. Every example runs live in your browser — no
setup, no installation.
Choose your own path by reordering languages
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.
async/await is concurrency on a single threadif err != nil — there is no throw/catch for ordinary failures, and the signature shows what can fail0 or "" — never undefined; no strictNullChecks neededany escape hatchnode_modules and no transpile step — go build is the whole toolchainYou 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.
interface/type, generics, as, satisfies, and ! are all erased — and private is only advisoryenum builds a runtime object, namespace an IIFE, and parameter properties generate this.x = xJSON.parse/fetch return any, indexing lies, and you cannot instanceof an interface#private fields, ?., and ?? survive compilation and run in plain JS@typedef/@param checked by tsc, plus runtime validation (zod) at the boundaryThe 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.
name: type / | None syntax, but they are advisory — mypy checks them, the interpreter ignores themfilter/map into one expression, replacing chained array methodsmatch does structural patterns like a discriminated unionif not items tests for empty@dataclass gives value equality and a constructor for free, the closest analog to a TypeScript record-shaped interfaceKotlin 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.
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 disciplinedata 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 librarywhen — 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 tricksuspend fun reads like async/await, but a coroutineScope guarantees every child coroutine finishes or is cancelled together, a guarantee Promise chains do not give youThe 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 handlematch — discriminated unions made first-class, and the compiler rejects an unhandled caseany to fall back onmap/filter/collect) are lazy, zero-cost pipelines that read like the array methods you already useStructural 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?/| 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 silentlyenum cases with associated values — a close conceptual parallel, but compiler-verified rather than convention-basedTask, actors) enforces data-race safety that TypeScript's single-threaded event loop never had to think aboutany/unknown escape hatches have no real Swift equivalent — Swift's type system cannot be turned off, even for AnyThe 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.
typeof(T) works at runtime, unlike TypeScript's erased type parametersstring?, ?., ??) 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 matchingfilter/map/reduce as lazy Where/Select/Aggregate over any sequence: IFoo, where TypeScript accepts any matching shapeThe 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.
kind: field to maintain, exhaustiveness checked by match instead of a never-typed default-case tricknull and no undefined — Try(ok, err) and ad-hoc tag unions replace T | undefined, and Roc's ?? operator works just like the one you know! suffix the way async marks asynchrony, except the compiler enforces it for every side effectcatch (problem: unknown)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.
numbers.map { |number| number * 2 }, with the &:upcase shorthand for one-method calls42 and nil — no primitive/wrapper split, no undefined; only nil and false are falsydef square(x) = x * x) replace options objects and arrow functionscase/in — destructure hashes and arrays the way you would with a discriminated union, plus guardsmethod_missing give runtime metaprogramming that a Proxy only approximates