-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Nim for TypeScript Programmers
DISCLAIMER!
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:
http://nim-lang.org/docs/tut1.html
http://nim-lang.org/docs/tut2.html
The manual provides an overview of the language: http://nim-lang.org/docs/manual.html
Feature | TypeScript | Nim |
---|---|---|
Execution model | JavaScript code (Compiler) | JavaScript code (Compiler) |
Written using | TypeScript | Nim |
License | Apache | MIT |
Version (Major) | 3.x |
1.x |
Typing | Static, "provably correct" types | Static, Strong, Inferred |
Meta-programming | ❎ #issue-13252, Decorators are limited | ✔️ template, macro |
int8/16/32/64 types | ❎ | ✔️ |
float32/float64 types | ❎ | ✔️ |
Char types | ❎ | ✔️ |
Subrange types | ❎ #issue-225324972 | ✔️ |
JSON types | ❎ #issue-56296923 | ✔️ |
Regex types | ❎ #issue-128264906 | ✔️ |
Option types | ❎ | ✔️ |
Dependent types | ❎ | ✔️ |
Operator Overloading | ❎ | ✔️ |
Custom Operators | ❎ | ✔️ |
Run-time Checks | ❎ | ✔️ |
Side Effects Tracking | ❎ | ✔️ |
Enum types | ✔️ | ✔️ |
Immutability | Limited, readonly keyword |
✔️ |
Function Arguments Immutability | Mutable | Immutable |
Full DOM API | ✔️ | ✔️ |
console.log() anywhere |
❓ Compiler complains | ✔️ |
console.assert() anywhere |
❓ Compiler complains | ✔️ |
NodeJS integration | ✔️ | ✔️ |
Generics | ✔️ | ✔️ |
Type inference | ✔️ | ✔️ |
Closures | ✔️ | ✔️ |
Object-Oriented | ✔️ | ✔️ |
Methods | ✔️ | ✔️ |
Exceptions | ✔️ | ✔️ |
Anonymous Functions | ✔️ | ✔️ |
Arrow Functions | ✔️ | ✔️ |
Array Comprehensions | ✔️ | ✔️ |
Formatted String Literals | ✔️ | ✔️ |
FFI | ✔️ JS only | ✔️ C/C++/JS |
Async | ✔️ | ✔️ |
Regex | ✔️ | ✔️ |
Self-Documentation comments | ✔️ | ✔️ |
Package Publishing | ✔️ | ✔️ |
Package Manager | ✔️ | ✔️ |
Code AutoFormatter | ✔️ via NPM | ✔️ 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 | AutoInitialized |
---|---|---|---|---|---|
var |
❎ | ✔️ | ❎ | ❎ | ✔️ |
let |
❎ | ✔️ | ✔️ | ✔️ | ✔️ |
const |
✔️ | ❎ | ✔️ | ✔️ | ✔️ |
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.
conditional ? "result0" : "result1"
⬆️ TypeScript ⬆️ ⬇️ Nim ⬇️
if conditional: "result0" else: "result1"
You probably notice that the Ternary Operator is just an if..else
inline.
var variable = ((var1: number, var2: number) => {
return var1 + var2
})();
⬆️ TypeScript ⬆️ ⬇️ Nim ⬇️
var variable = (proc (var1, var2: int): int = var1 + var2)
Anonymous Functions in Nim are basically a function without a name and surrounded by parens.
this
in 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
Nim has Arrow Functions, they are syntax sugar for normal functions. Using Arrow Functions requires import sugar
.
Lets convert the Kitten example to use Arrow Functions.
import sugar
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.
import sugar
let purr = () => echo "Purr Purr" # No arguments
You can use function names with your Arrow Functions:
functionName(argument0, argument1: int) => argument0 + argument1
another_name(argument0, argument1: int) => argument0 + argument1
You can use Pragmas on your Arrow Functions:
let functionName = () {.inline.} => 42
let another_name = () {.inline.} => 42
You can use function names and Pragmas on your Arrow Functions:
function_name(argument0, argument1: int) {.noSideEffect.} => argument0 + argument1
let variab = (argument0, argument1: int) {.noSideEffect.} => argument0 + argument1
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.
#?stdtmpl
#func generateXML(name, age: string): string =
<xml>
<name> $name </name>
<age> $age </age>
</xml>
Karax does Client-Side Render in Nim, it can do Server-Side render too, includes local dev server, among other really cool things.
Hello World, Client-Side
include karax/prelude
setRenderer (func(): auto = buildHtml(tdiv): text"Hello World")
Hello World, Server-Side:
import karax/[karaxdsl,vdom]
writeFile "app.html", $(block: buildHtml(tdiv):
h1: text"Hello World"
p: text"Compile: nim r file.nim ")
Hello World, Server-Side with Bulma CSS:
include prelude
import karax / [karaxdsl, vdom]
writeFile "example.html", $(block: buildHtml(tdiv):
link(href="https://cdn.jsdelivr.net/npm/[email protected]/css/bulma.min.css", rel="stylesheet")
section(class="section"):
tdiv(class="container"):
h1(class="title"):
text"Welcome"
button(class="button is-success"): text"Example"
ol(class="ol"):
li: text"Item 0"
li: text"Item 1"
code(class="code"): text"""echo "Hello World" """
)
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).
- Svelte inspired Nim Frontend https://mildred.github.io/nclearseam
Note: Svelte added TypeScript
Theres a Nim module for ParcelJS support:
https://github.com/nepeckman/parcel-plugin-nim#parcel-plugin-nim
Theres a Nim module for CommonJS support:
https://github.com/nepeckman/jsExport.nim#jsexportnim
Cross-platform GUI framework with WebGL target, compiles your GUI to WebGL:
https://github.com/yglukhov/nimx#nimx--
You can run Nim inside Electron.
You can also use cross-platform tiny (1 .h
file) and fast (C code) WebView:
- https://github.com/juancarlospaco/webgui#buit-in-dark-mode
- https://github.com/oskca/webview#webview-for-nim
WIISH also provides a similar cross-platform WebView, with other targets too: https://github.com/iffy/wiish#features
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:
- https://github.com/yglukhov/nimx#nimx--
- https://github.com/filcuc/nimqml#nimqml
- https://github.com/ba0f3/uibuilder.nim#uibuildernim
- https://github.com/khchen/wNim#wnim
- https://github.com/trustable-code/NiGui#nigui
- https://github.com/johnnovak/illwill#main-features
- https://github.com/stefansalewski/gintro
- https://github.com/nim-lang/ui#ui
- https://github.com/zacharycarter/nimgui#nimgui
ReactJS for Nim:
https://github.com/andreaferretti/react.nim#reactnim
You can compile your Nim code to WebAssembly using:
- Clang WASM target OR Emscripten OR NLVM.
For compiling your project to WebAssembly see:
- https://github.com/def-/nimes#nimes-nes-emulator-in-nim-
- https://github.com/arnetheduck/nlvm#introduction
- https://github.com/2vg/nim-wasm-example
- https://github.com/yglukhov/nimwasmrt#wasmrt-
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 | ✔️ | ❎ |
Tracebacks | ❎ | ✔️ |
Run-time checks | ✔️ | ✔️ |
Compile-time checks | ✔️ | ✔️ |
assert |
❎ | ✔️ |
doAssert |
✔️ | ✔️ |
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() {
rawEcho("42");
}
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.
Nim can directly interoperate with JavaScript, here is how you can create your own libs.
There is no additional performance cost, the compiler will just emit the code you want.
As minimal as possible example, not too useful but explains as simple as possible:
func functionName(argument: SomeNumber) {.importjs: """ console.log(#) """.}
-
#
is replaced by arguments by index, starting at index 0, if any. -
@
is replaced by all remaining arguments, if any, separated by comma. -
$$
to escape a single$
on string or Regex. -
$1
is replaced by the current function name. - Argument types and return types are Nim types, from std lib or your own custom.
-
importcpp
is sometimes used asimportjs
. - You can use
openArray
as return type on functions, it will returnarray
instead ofseq
. - Print to console using vanilla JavaScript
console.log()
and similar. -
Ensure that DOM is ready using vanilla JavaScript
(function(){ ... })();
. -
Enforce Strict Mode using vanilla JavaScript
"use strict";
. - https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-importjs-pragma
- If the function has no arguments, then
@
generates nothing (empty). - If the function has no arguments, then
#
produces an error. - Note that with
@
commas are automatically inserted, with#
you must add the commas. - You can use multi-line strings for the patterns.
- You can use
strformat
for the patterns, but you must importstrformat
.
Lets compile this tiny example to JavaScript target:
func functionName(argument0, argument1: SomeNumber) {.importjs: """ console.log(#, #) """.}
functionName(42, 9)
⬆️ Nim ⬆️ ⬇️ JavaScript ⬇️
console.log(42, 9);
If you need to use named arguments you can use {.emit.}
pragma:
func functionName(namedArgument: int) = {.emit: """ console.log( `namedArgument` ); """.}
-
`namedArgument`
is replaced by arguments by name. - Argument types and return types are Nim types, from std lib or your own custom.
- Print to console using
console.log()
and similar. - You can use
openArray
as return type on functions, it will returnarray
instead ofseq
. -
Ensure that DOM is ready using vanilla JavaScript
(function(){ ... })();
. - You can use
"use strict"
if you want, but it may not have effect. - The rest is similar to
importjs
, as explained above. - https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-emit-pragma
Lets compile this tiny example to JavaScript target:
func functionName(namedArgument, otherArgument: int) = {.emit: """
console.log( `namedArgument`, `otherArgument` );
""".}
functionName(42, 99)
⬆️ Nim ⬆️ ⬇️ JavaScript ⬇️
function function_name(named_argument, other_argument) {
console.log( named_argument, other_argument );
}
function_name(42, 99);
More Examples:
func example0(argument0, argument1, argument2: SomeNumber | string) {.importjs: """
(function () {
"use strict";
console.log('Argument0 = ' + #, 'Argument1 = ' + #, 'Argument2 = ' + #);
})(); """.}
func example1(foo, bar, baz: SomeNumber) = {.emit: """
console.log('foo = ' + `foo`, 'bar = ' + `bar`, 'baz = ' + `baz`);
""".}
{.importjs.}
import JavaScript.{.compiletime.}
Force code to run at compile-time.{.exportc.}
Escape Name Mangling (for Debug, Obfuscation, etc).{.emit.}
Emit code directly with passed arguments.{.varargs.}
Force a function to accept several arguments.{.hint.}
Human-friendly colored messages at compile-time that generates no code.{.warning.}
Human-friendly colored messages at compile-time that generates no code.{.codegendecl.}
Emit code directly on code generator.{.noinit.}
Escape implicit variable auto-initialization.{.discardable.}
Allow to discard unused return values (for debug, quick prototyping, etc).{.noreturn.}
Force a function to never return{.asmnostackframe.}
Force a function to not have theresult =
auto-injected, useful with{.emit.}
{.injectStmt.}
Inject code before every other statement in the current module.- Pragmas are just
macro
, so you can create your own.
See also:
static:
/static()
Force a block of code to run at compile-time.when
Compile-timeif
jsre
module for an example of actual code.jsconsole
module for an example of actual code.
- Nim "code template" pseudocode that you can Edit to create your own JavaScript libs:
func functionName(argument: SomeNumber | string | bool): SomeNumber {.importjs: """
(function () {
"use strict";
console.log("CODE HERE");
console.log(#);
return 42;
})(); """, exportc: "functionName".} ## Documentation Comment.
func functionName2(namedArgument: SomeNumber | string | bool): SomeNumber = {.emit: """
console.log("CODE HERE");
console.log( `namedArgument` );
return 42;
""", exportc: "functionName2".} ## Documentation Comment.
This is a community maintained open source project thats a HTML/CSS WYSYWIG Editor with Nim Support without JavaScript required, for creating Source Code Filter starter templates (similar to Pug templating, etc), so you dont have to start by hand with an empty blank file, not meant to produce a "finished" page but a template to edit. Ideas to make it more useful welcome. Pull Requests welcome.
- Drag&drop your components in the page and press the "View Code" green button.
- Documentation of each component clicking on the "?" link.
- Ready-made full-page templates on the "Templates" category.
To not duplicate all documentation here, please continue reading:
It is Python-oriented, but the same concepts also work for Frontend too!.
Intro
Getting Started
- Install
- Docs
- Curated Packages
- Editor Support
- Unofficial FAQ
- Nim for C programmers
- Nim for Python programmers
- Nim for TypeScript programmers
- Nim for D programmers
- Nim for Java programmers
- Nim for Haskell programmers
Developing
- Build
- Contribute
- Creating a release
- Compiler module reference
- Consts defined by the compiler
- Debugging the compiler
- GitHub Actions/Travis CI/Circle CI/Appveyor
- GitLab CI setup
- Standard library and the JavaScript backend
Misc