TJS Grammar Reference
Comprehensive reference covering all major TJS syntax features.
Type declarations (require full tjs-lang runtime — shown here for reference):
Type Name = 'Alice'
Type Age 'a non-negative age' {
example: 25
predicate(x) { return typeof x === 'number' && x >= 0 }
}
Generic Pair<A, B> {
description: 'a pair of values'
predicate(obj, A, B) { ... }
}
Enum Direction 'cardinal direction' { North, East, South, West }
Enum Color 'CSS color' { Red = 'red', Green = 'green', Blue = 'blue' }
Union Status 'task status' 'pending' | 'active' | 'done'
All other features are exercised in the runnable code below:
// ═══════════════════════════════════════════════════════════
// 1. SAFETY DIRECTIVE
// Must appear before any other code.
// ═══════════════════════════════════════════════════════════
safety inputs
// Honest equality, callable-without-new classes and the `var` ban are simply how
// `.tjs` behaves — there are no mode directives to enable them.
/#
# TJS Grammar Reference
A runnable reference for TJS syntax. Each section demonstrates a
feature with a test proving it works.
## Quick Index
| Feature | Section |
|---------|---------|
| Safety directive | `safety` |
| Parameters | Colon `:`, optional `=`, destructured `{}` |
| Numeric narrowing | `42` int, `3.14` float, `+0` non-negative |
| Return types | `:`, `:?`, `:!` |
| Safety markers | `(! ...)` unsafe, `(? ...)` safe |
| Type/Generic/Enum/Union | See above (requires full runtime) |
| Bare assignments | `Uppercase = ...` |
| Classes | Callable without `new` |
| Polymorphic functions | Same name, different signatures |
| Polymorphic constructors | Multiple `constructor()` |
| Local extensions | `extend String { ... }` |
| Equality | `==` honest, `===` identity, `Is`/`IsNot` |
| Try without catch | Monadic error conversion |
| Inline tests | Test blocks |
| TDoc comments | Slash-star-hash markdown blocks |
#/
// ═══════════════════════════════════════════════════════════
// 2. PARAMETER SYNTAX
// ═══════════════════════════════════════════════════════════
/#
## Parameters
Colon `:` = required (example value infers type).
Equals `=` = optional (default value).
Question mark `?:` = optional (TS-style).
#/
// Required params (colon shorthand)
function greet(name: 'Alice'): 'Hello, Alice' {
return 'Hello, ' + name
}
// Optional params (equals = default)
function greetOpt(name = 'World'): 'Hello, World' {
return 'Hello, ' + name
}
// Destructured object params (colon = required, equals = optional)
function createUser({ name: 'Anon', role = 'user' }): { name: 'Anon', role: 'user' } {
return { name, role }
}
// Numeric type narrowing: 42 = integer, 3.14 = float, +0 = non-negative int
function calc(count: 42, rate: 3.14, index: +0): 131.88 {
return (count + index) * rate
}
test 'parameter syntax' {
expect(greet('Bob')).toBe('Hello, Bob')
expect(greetOpt()).toBe('Hello, World')
expect(createUser({ name: 'Eve' })).toEqual({ name: 'Eve', role: 'user' })
expect(calc(10, 1.5, 2)).toBe(18)
}
// ═══════════════════════════════════════════════════════════
// 3. RETURN TYPES
// ═══════════════════════════════════════════════════════════
/#
## Return Types
`:` signature test at transpile time.
`:?` signature test + runtime output validation.
`:!` skip signature test entirely.
#/
// : transpile-time check (double(5) must equal 10)
function double(x: 5): 10 {
return x * 2
}
// :! skip test (useful when return shape varies)
function safeDivide(a: 10, b: 2):! 5 {
if (b === 0) return { error: 'div by zero' }
return a / b
}
test 'return types' {
expect(double(7)).toBe(14)
expect(safeDivide(10, 0)).toEqual({ error: 'div by zero' })
}
// ═══════════════════════════════════════════════════════════
// 4. SAFETY MARKERS
// ═══════════════════════════════════════════════════════════
/#
## Safety Markers
`!` = unsafe (skip input validation). Fast path for trusted callers.
`?` = safe (force validation even inside `unsafe` blocks).
#/
function fastAdd(! a: 0, b: 0): 0 {
return a + b
}
function safeAdd(? a: 0, b: 0): 0 {
return a + b
}
test 'safety markers' {
expect(fastAdd(3, 4)).toBe(7)
expect(safeAdd(3, 4)).toBe(7)
}
// ═══════════════════════════════════════════════════════════
// 5. BARE ASSIGNMENTS
// ═══════════════════════════════════════════════════════════
/#
## Bare Assignments
Uppercase identifiers auto-get `const` — a native-TJS convenience (off for plain
JS / TS / VM). It fires only on the **first** assignment of an undeclared name;
a reassignment of an already-declared binding (`let B = null; B = 2`) is left
alone. Since the first assignment becomes `const`, use `let` if you need it to
change later.
#/
Greeting = 'Hello'
MaxRetries = 3
test 'bare assignments' {
expect(Greeting).toBe('Hello')
expect(MaxRetries).toBe(3)
}
// ═══════════════════════════════════════════════════════════
// 7. CLASSES (callable without new)
// ═══════════════════════════════════════════════════════════
/#
## Classes
Classes are callable without `new` by default in native TJS.
#/
class Point {
constructor(x: 0.0, y: 0.0) {
this.x = x
this.y = y
}
magnitude() {
return Math.sqrt(this.x * this.x + this.y * this.y)
}
}
test 'classes callable without new' {
const p = Point(3, 4)
expect(p instanceof Point).toBe(true)
expect(p.magnitude()).toBe(5)
}
// ═══════════════════════════════════════════════════════════
// 8. POLYMORPHIC FUNCTIONS
// ═══════════════════════════════════════════════════════════
/#
## Polymorphic Functions
Same name, different signatures. Dispatched by arity/type.
See the **Polymorphic Functions** example for more.
#/
function describe(value: 0) {
return 'number: ' + value
}
function describe(first: '', last: '') {
return first + ' ' + last
}
test 'polymorphic dispatch by arity' {
expect(describe(42)).toBe('number: 42')
expect(describe('Jane', 'Doe')).toBe('Jane Doe')
}
// ═══════════════════════════════════════════════════════════
// 9. POLYMORPHIC CONSTRUCTORS
// ═══════════════════════════════════════════════════════════
/#
## Polymorphic Constructors
Multiple `constructor()` declarations in a class.
See the **Polymorphic Constructors** example for more.
#/
class Vec2 {
constructor(x: 0.0, y: 0.0) {
this.x = x
this.y = y
}
constructor(obj: { x: 0.0, y: 0.0 }) {
this.x = obj.x
this.y = obj.y
}
}
test 'polymorphic constructors' {
const a = Vec2(1, 2)
const b = Vec2({ x: 1, y: 2 })
expect(a.x).toBe(b.x)
expect(a.y).toBe(b.y)
}
// ═══════════════════════════════════════════════════════════
// 10. LOCAL EXTENSIONS
// ═══════════════════════════════════════════════════════════
/#
## Local Extensions
Add methods to built-in types without prototype pollution.
Rewritten to `.call()` at transpile time for known types.
See the **Local Extensions** example for a runnable demo.
extend String {
capitalize() { return this[0].toUpperCase() + this.slice(1) }
}
extend Array {
last() { return this[this.length - 1] }
}
'hello'.capitalize() // 'Hello'
[1, 2, 3].last() // 3
#/
// ═══════════════════════════════════════════════════════════
// 11. EQUALITY OPERATORS
// ═══════════════════════════════════════════════════════════
/#
## Equality
Honest equality is on by default in native TJS:
- `==` / `!=` use honest equality (no coercion, unwraps boxed primitives)
- `===` / `!==` are identity checks (same reference)
- `Is` / `IsNot` are explicit structural operators (deep comparison)
const a = { x: 1, y: [2, 3] }
const b = { x: 1, y: [2, 3] }
a == b // true (structural: same shape)
a === b // false (identity: different objects)
a Is b // true (explicit structural)
a IsNot b // false
#/
// ═══════════════════════════════════════════════════════════
// 12. TRY WITHOUT CATCH
// ═══════════════════════════════════════════════════════════
/#
## Try Without Catch
A bare `try` block auto-converts exceptions to monadic errors.
#/
function parseJSON(s: '{"a":1}'):! { a: 1 } {
try {
return JSON.parse(s)
}
}
test 'try without catch' {
expect(parseJSON('{"ok":true}')).toEqual({ ok: true })
const bad = parseJSON('not json')
expect(bad instanceof Error).toBe(true)
}
// ═══════════════════════════════════════════════════════════
// 13. INLINE TESTS
// ═══════════════════════════════════════════════════════════
/#
## Inline Tests
Test blocks run at transpile time and are stripped from
output. They have full access to the module scope, so you
can test private functions without exporting them.
#/
function _private(x: 0): 0 {
return x * x
}
test 'inline tests can reach private functions' {
expect(_private(5)).toBe(25)
}
// ═══════════════════════════════════════════════════════════
// 14. MODULE EXPORTS
// ═══════════════════════════════════════════════════════════
/#
## Module Exports
Standard ES module syntax works. Functions and values
can be exported for use by other modules.
#/
export function publicHelper(x: 0): 1 {
return x + 1
}
// ═══════════════════════════════════════════════════════════
// 15. TDOC COMMENTS
// ═══════════════════════════════════════════════════════════
/#
## TDoc Comments
These comment blocks (opened with slash-star-hash) contain
markdown that becomes rich documentation in the playground
and API docs. Every such block you've seen above is a TDoc.
#/
// ═══════════════════════════════════════════════════════════
// OUTPUT
// ═══════════════════════════════════════════════════════════
console.log('TJS Grammar Reference — all tests passed!')
console.log('Features demonstrated:', [
'safety directive', 'colon params', 'optional params',
'destructured params', 'numeric narrowing',
'return types (: :? :!)', 'safety markers (! ?)',
'Type', 'Generic', 'Enum', 'Union', 'bare assignments',
'classes', 'polymorphic functions', 'polymorphic constructors',
'local extensions', 'structural equality', 'Is/IsNot',
'try without catch', 'inline tests', 'module exports', 'TDoc'
].join(', '))