Skip to content

Nim for TypeScript Programmers

Juan Carlos edited this page Feb 1, 2020 · 29 revisions

Table Of Contents


Unofficial, work in progress! This is still a stub. Please help extending it. There may be inaccuracies in this guide. The guide assumes some intermediate knowledge.

The general tutorials can be found here:

The manual provides an overview of the language:


Feature TypeScript Nim
Execution model JavaScript code (Compiler) JavaScript code (Compiler)
Written using TypeScript Nim
License Apache MIT
Version (Mayor) 3.x 1.x
Typing Static, "provably correct" types Static, Strong, Inferred
Meta-programming :negative_squared_cross_mark: #issue-13252, Decorators are limited :heavy_check_mark: template, macro
int8/16/32/64 types :negative_squared_cross_mark: :heavy_check_mark:
float32/float64 types :negative_squared_cross_mark: :heavy_check_mark:
Char types :negative_squared_cross_mark: :heavy_check_mark:
Subrange types :negative_squared_cross_mark: #issue-225324972 :heavy_check_mark:
JSON types :negative_squared_cross_mark: #issue-56296923 :heavy_check_mark:
Regex types :negative_squared_cross_mark: #issue-128264906 :heavy_check_mark:
Option types :negative_squared_cross_mark: :heavy_check_mark:
Dependent types :negative_squared_cross_mark: :heavy_check_mark:
Operator Overloading :negative_squared_cross_mark: :heavy_check_mark:
Custom Operators :negative_squared_cross_mark: :heavy_check_mark:
Run-time Checks :negative_squared_cross_mark: :heavy_check_mark:
Side Effects Tracking :negative_squared_cross_mark: :heavy_check_mark:
Enum types :heavy_check_mark: :heavy_check_mark:
Immutability Limited, readonly keyword :heavy_check_mark:
Function Arguments Immutability Mutable Immutable
Full DOM API :heavy_check_mark: :heavy_check_mark:
NodeJS integration :heavy_check_mark: :heavy_check_mark:
Generics :heavy_check_mark: :heavy_check_mark:
Type inference :heavy_check_mark: :heavy_check_mark:
Closures :heavy_check_mark: :heavy_check_mark:
Object-Oriented :heavy_check_mark: :heavy_check_mark:
Methods :heavy_check_mark: :heavy_check_mark:
Exceptions :heavy_check_mark: :heavy_check_mark:
Anonymous Functions :heavy_check_mark: :heavy_check_mark:
Arrow Functions :heavy_check_mark: :heavy_check_mark:
Array Comprehensions :heavy_check_mark: :heavy_check_mark:
Formatted String Literals :heavy_check_mark: :heavy_check_mark:
FFI :heavy_check_mark: JS only :heavy_check_mark: C/C++/JS
Async :heavy_check_mark: :heavy_check_mark:
Regex :heavy_check_mark: :heavy_check_mark:
Self-Documentation comments :heavy_check_mark: :heavy_check_mark:
Package Publishing :heavy_check_mark: :heavy_check_mark:
Package Manager :heavy_check_mark: :heavy_check_mark:
Code AutoFormatter :heavy_check_mark: via NPM :heavy_check_mark: Nimpretty
File extensions .ts, .tsx .nim, .nims


Creating a new variable uses var or let or const. Nim has immutability and compile-time function execution. You can assign functions to variables. const is different from TypeScript by being truly immutable.

Declaration Compile-Time Run-Time Immutable Requires Assignment
var :negative_squared_cross_mark: :heavy_check_mark: :negative_squared_cross_mark: :negative_squared_cross_mark:
let :negative_squared_cross_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark:
const :heavy_check_mark: :negative_squared_cross_mark: :heavy_check_mark: :heavy_check_mark:

If you are just starting from scratch, you can use var while learning, it will not produce an error for doing so, until you learn more.

Ternary operators

conditional ? "result0" : "result1"

:arrow_up: TypeScript :arrow_up:          :arrow_down: Nim :arrow_down:

if conditional: "result0" else: "result1"

You probably notice that the Ternary Operator is just an if..else inline.

Anonymous Functions

var variable = (() => {
    return var1 + var2

:arrow_up: TypeScript :arrow_up:          :arrow_down: Nim :arrow_down:

var variable = (proc (var1, var2: int): int = var1 + var2)

Anonymous Functions on Nim are basically a function without a name and surrounded by parens.


this on Nim does not have a fixed or hardcoded naming, so you may see some code using self, Nim only cares about it being the first argument. this or self is immutable by default.

type Kitten = object
proc purr(this: Kitten) = echo "Purr Purr" # this
proc meow(self: Kitten) = echo "Meow Meow" # self

Arrow Functions

Nim has Arrow Functions, they are syntax sugar for normal functions, lets convert the Kitten example to use Arrow Functions.

type Kitten = object
let purr = (this: Kitten) => echo "Purr Purr" # this
let meow = (self: Kitten) => echo "Meow Meow" # self

If you do not need to pass any argument, just do not pass anything.

let purr = () => echo "Purr Purr" # No arguments
  • Using Arrow Functions requires import sugar, on Nim syntax sugar is on the sugar module.

Server-Side Render

Nim Source Code Filters NimF does server-side rendering of templates, a powerful templating engine on standard library.

Nim Source Code Filters use a header like a SheBang #?stdtmpl and the file extension *.nimf, then everything inside is just normal Nim code with the prefix #, they are usually functions that return a string, functions can use normal string operations and formatting, they get compiled into normal Nim code, providing excellent performance.

#func generateXML(name, age: string): string =
  <name> $name </name>
  <age>  $age  </age>

Client-Side Render

Karax does Client-Side Render on Nim, it can do Server-Side render too, among other really cool things.

Karax Clien Side Render

Compile-Time Function Execution

Nim has Compile-Time Function Execution that allows you to run Backend-like code at compile-time and use it on Frontend at run-time. Compile-time FFI is also possible. Most code that works at compile-time and NimScript also tends to work for Frontend.

Example: Compile-time recursive file system walk module.


Nim has some similarities with Svelte for Frontend, but at the time of writing Svelte does not fully supports TypeScript. You can think of Nim like a Svelte but with TypeScript like static typing and a whole Backend ecosystem. Svelte can not do Backend (is not designed to).


Theres a Nim module for ParcelJS support:


Theres a Nim module for CommonJS support:


Cross-platform GUI framework with WebGL target, compiles your GUI to WebGL:


You can run Nim inside Electron.

You can also use cross-platform tiny (1 .h file) and fast (C code) WebView:

WIISH also provides a similar cross-platform WebView, with other targets too:

If it is not mandatory to be a JavaScript-only GUI, and you just need a GUI that works on Windows/Linux/Mac, then you have even more alternatives to try:


ReactJS for Nim:


For compiling your project to WebAssembly see:

Build Modes

When your code is ready for production you should use a Release build, you can add to the compile command -d:release -d:danger.

Feature Release Build Debug Build
Speed Fast Slow
File Size Small Big
Optimized :heavy_check_mark: :negative_squared_cross_mark:
Tracebacks :negative_squared_cross_mark: :heavy_check_mark:
Run-time checks :heavy_check_mark: :heavy_check_mark:
Compile-time checks :heavy_check_mark: :heavy_check_mark:
assert :negative_squared_cross_mark: :heavy_check_mark:
doAssert :heavy_check_mark: :heavy_check_mark:


Nim does not Minify the compiled JavaScript by default.

Nim typically compiles to very small file sizes when builds for release, thanks to Dead Code Elimination only the used symbols of each Nim module gets compiled and is present on the output file, if you import a module and is unused it will not exist on the output file (and a Hint shows on the terminal about unused import).

Nim uses spaces as indentation. Basically you are good to go with the JavaScript file that Nim compiles.

Alternatively you can use any other minifier software to do that kind of post-processing of JavaScript.


Nim does not obfuscate the compiled JavaScript by default.

If you want to obfuscate the compiled JavaScript, you can control the name mangling, among other more useful features using {.exportc.} pragma, to use the mangling as obfuscator.

var variable {.exportc: "lkfnjmgkw3du4905r3q2ep8n4urfp34w2efltgvepotik132qm0".} = false
proc funct() {.exportc: "kl34jgo9liw35e4atr8i30q2rk1fipkpfrsdofir93o2qujfoks".} = echo 42

Compiles to:

var lkfnjmgkw3du4905r3q2ep8n4urfp34w2efltgvepotik132qm0 = false;

function kl34jgo9liw35e4atr8i30q2rk1fipkpfrsdofir93o2qujfoks() {

You use the human friendly names variable and funct, while the code compiles to the obfuscated names.

Nim will not lose track of the names, you can generate the obfuscated names with any random algo of your own.

Alternatively you can use any other obfuscator software to do that kind of post-processing of JavaScript.

:arrow_up: :arrow_up: :arrow_up: :arrow_up:

Clone this wiki locally
You can’t perform that action at this time.