Skip to content

Examples

Practical SJS examples organized by category. Every example uses valid SJS syntax and can be compiled with superjs build.

1. Basics

Hello World

// hello-world.sjs
const message: string = "Hello, World!"
console.log(message)

function greet(name: string): string {
  return `Hello, ${name}!`
}
console.log(greet("Super.js"))

Variables and Type Annotations

// variables.sjs
const count: number = 42
const label: string = "items"
const active: boolean = true

// Type inference — annotation optional when value is clear
const ratio = 0.75       // inferred: number
const title = "SuperJS"  // inferred: string

console.log(`${count} ${label}`)

Functions

// functions.sjs
function add(a: number, b: number): number {
  return a + b
}

function repeat(text: string, times: number): string {
  return text.repeat(times)
}

const double = (n: number): number => n * 2

console.log(add(3, 4))         // 7
console.log(repeat("ha", 3))   // hahaha
console.log(double(10))        // 20

2. Null Safety

SJS types are non-nullable by default. Append ? to allow null or undefined.

// null-safety.sjs
function findUser(id: number): string? {
  if (id === 1) return "Alice"
  return null
}

const name = findUser(42)      // type: string?
const display = name ?? "Unknown"

console.log(display)           // "Unknown"

Optional Chaining and Nullish Coalescing

type Address {
  street: string
  city: string
  zip: string?
}

type User {
  name: string
  address: Address?
}

function getZip(user: User): string {
  return user.address?.zip ?? "N/A"
}

Nullable Return Types

function parseInt10(s: string): number? {
  const n = parseInt(s, 10)
  return isNaN(n) ? null : n
}

const result = parseInt10("abc")
if (result !== null) {
  console.log(result * 2)
}

3. Sum Types and Match

Sum types declare a closed set of variants. The match expression handles each variant and the compiler enforces exhaustiveness (SJS-E007).

Result Type

// result.sjs
type Result<T, E> = Ok(T) | Err(E)

function divide(a: number, b: number): Result<number, string> {
  if (b === 0) return Err("division by zero")
  return Ok(a / b)
}

const r = divide(10, 2)
const msg = match r {
  Ok(val) => `Result: ${val}`,
  Err(e)  => `Error: ${e}`,
}
console.log(msg)   // "Result: 5"

Option Type

// option.sjs
type Option<T> = Some(T) | None

function head<T>(arr: T[]): Option<T> {
  return arr.length > 0 ? Some(arr[0]) : None
}

const first = head([10, 20, 30])
const display = match first {
  Some(v) => `First: ${v}`,
  None    => "Empty list",
}
console.log(display)   // "First: 10"

Exhaustiveness Checking

Missing a variant produces a SJS-E007 error at compile time:

type Color = Red | Green | Blue

function label(c: Color): string {
  return match c {
    Red   => "red",
    Green => "green",
    Blue  => "blue",
  }
}

4. Generics

Generic Stack

// stack.sjs
class Stack<T> {
  private items: T[] = []

  push(item: T): void { this.items.push(item) }

  pop(): T? {
    if (this.items.length === 0) return null
    return this.items.pop() ?? null
  }

  peek(): T? {
    return this.items.length > 0 ? this.items[this.items.length - 1] : null
  }

  get size(): number { return this.items.length }
}

const stack = new Stack<number>()
stack.push(1)
stack.push(2)
stack.push(3)
console.log(stack.pop())   // 3
console.log(stack.size)    // 2

Generic Functions

function identity<T>(value: T): T {
  return value
}

function first<T>(arr: T[]): T? {
  return arr.length > 0 ? arr[0] : null
}

function zip<A, B>(as: A[], bs: B[]): [A, B][] {
  const len = Math.min(as.length, bs.length)
  const result: [A, B][] = []
  for (let i = 0; i < len; i++) {
    result.push([as[i], bs[i]])
  }
  return result
}

console.log(zip([1, 2, 3], ["a", "b", "c"]))
// [[1, "a"], [2, "b"], [3, "c"]]

5. Structural Object Types

SJS object types are structural — a class satisfies an object type by having the right shape, no implements keyword required. Object types use the brace form of type (no =).

type Shape {
  area(): number
  perimeter(): number
}

class Circle {
  constructor(private radius: number) {}

  area(): number {
    return Math.PI * this.radius ** 2
  }

  perimeter(): number {
    return 2 * Math.PI * this.radius
  }
}

class Rectangle {
  constructor(private width: number, private height: number) {}

  area(): number {
    return this.width * this.height
  }

  perimeter(): number {
    return 2 * (this.width + this.height)
  }
}

function describe(s: Shape): string {
  return `area=${s.area().toFixed(2)}, perimeter=${s.perimeter().toFixed(2)}`
}

console.log(describe(new Circle(5)))
console.log(describe(new Rectangle(4, 6)))

Object Type Composition

type Named {
  name: string
}

type Aged {
  age: number
}

type Person extends Named, Aged {
  email: string
}

function greet(p: Person): string {
  return `Hello, ${p.name} (age ${p.age})`
}

6. JSX

JSX is enabled by default in SJS. Use it directly in .sjs files with no extra configuration.

Typed Props

// card.sjs
type CardProps {
  title: string
  body: string
  footer?: string
}

function Card({ title, body, footer }: CardProps) {
  return (
    <div className="card">
      <h2>{title}</h2>
      <p>{body}</p>
      {footer && <footer>{footer}</footer>}
    </div>
  )
}

Component with Event Handlers

type ButtonProps {
  label: string
  onClick: () => void
  disabled?: boolean
}

function Button({ label, onClick, disabled = false }: ButtonProps) {
  return (
    <button
      className={disabled ? "btn btn-disabled" : "btn"}
      onClick={onClick}
      disabled={disabled}
    >
      {label}
    </button>
  )
}

List Rendering

type ListProps {
  items: string[]
  emptyMessage?: string
}

function List({ items, emptyMessage = "Nothing here." }: ListProps) {
  if (items.length === 0) {
    return <p>{emptyMessage}</p>
  }
  return (
    <ul>
      {items.map((item, i) => (
        <li key={i}>{item}</li>
      ))}
    </ul>
  )
}

7. Node.js Usage

File Analysis CLI Tool

// analyze.sjs
import fs from 'fs'
import path from 'path'

type FileStats {
  path: string
  lines: number
  sizeBytes: number
}

type AnalysisResult<T> = Success(T) | Failure(string)

function analyzeFile(filePath: string): AnalysisResult<FileStats> {
  try {
    const content = fs.readFileSync(filePath, 'utf-8')
    return Success({
      path: filePath,
      lines: content.split('\n').length,
      sizeBytes: Buffer.byteLength(content),
    })
  } catch (e) {
    return Failure(`Cannot read ${filePath}`)
  }
}

const result = analyzeFile(process.argv[2])
match result {
  Success(stats) => console.log(`${stats.lines} lines, ${stats.sizeBytes} bytes`),
  Failure(msg)   => console.error(msg),
}

Directory Walker

// walk.sjs
import fs from 'fs'
import path from 'path'

function walkDir(dir: string): string[] {
  const entries = fs.readdirSync(dir, { withFileTypes: true })
  const files: string[] = []

  for (const entry of entries) {
    const full = path.join(dir, entry.name)
    if (entry.isDirectory()) {
      files.push(...walkDir(full))
    } else {
      files.push(full)
    }
  }

  return files
}

const target = process.argv[2] ?? "."
const files = walkDir(target)
console.log(`Found ${files.length} files`)
files.forEach(f => console.log(f))

HTTP Server

// server.sjs
import http from 'http'

type Route {
  method: string
  path: string
  handler: (body: string) => string
}

const routes: Route[] = [
  {
    method: "GET",
    path: "/",
    handler: () => JSON.stringify({ message: "Hello from SJS!" }),
  },
]

const server = http.createServer((req, res) => {
  const route = routes.find(
    r => r.method === req.method && r.path === req.url
  )

  if (!route) {
    res.writeHead(404)
    res.end(JSON.stringify({ error: "Not found" }))
    return
  }

  let body = ""
  req.on("data", chunk => { body += chunk })
  req.on("end", () => {
    res.writeHead(200, { "Content-Type": "application/json" })
    res.end(route.handler(body))
  })
})

server.listen(3000, () => console.log("Listening on :3000"))