Part 1 — Syntax rewrites
SuperJS rejects twelve TypeScript construct categories at parse or type-check time. There is no flag or pragma to re-enable them (ADR-004).
Quick reference
| TypeScript | SuperJS | Error |
|---|---|---|
any | dynamic | SJS-E004 |
A & B intersection | type C extends A, B { } | SJS-E005 |
T extends U ? A : B | sum type + match | SJS-E008 |
infer T | not supported — explicit types | SJS-E009 |
enum E { ... } | sum type / unit variants | SJS-E010 |
value! non-null assertion | if / ?. narrowing | SJS-E011 |
namespace N { } | ES import / export | SJS-E012 |
{ [K in keyof T]: U } mapped | structural index signature | SJS-E006 |
T['key'] indexed access | name fields explicitly | SJS-E006 |
typeof x in type position | explicit type declaration | SJS-E006 |
<T>expr angle cast | expr as T only | parse error |
== / != | === / !== | SJS-L003 |
@Decorator | not supported | banned |
prop?: T (optional) | prop: T? or prop?: T per semantics | — |
Decorators are not on the roadmap — they are incompatible with the ECMAScript-first emit model.
any → dynamic
// TypeScript
function load(raw: any): string {
return raw.toUpperCase()
}// SuperJS
function load(raw: dynamic): string {
if (typeof raw !== "string") {
throw new Error("expected string")
}
return raw.toUpperCase()
}dynamic is explicit and lintable. See dynamic type.
Intersection A & B → structural extends
// TypeScript
type Named = { name: string }
type Aged = { age: number }
type Person = Named & Aged// SuperJS
type Named { name: string; }
type Aged { age: number; }
type Person extends Named, Aged { }Conditional types → sum types + match
// TypeScript
type ApiResult<T> = T extends string ? { text: T } : { value: T }// SuperJS
type TextResult(text: string) | ValueResult(value: dynamic)
function wrap(x: dynamic): TextResult | ValueResult {
if (typeof x === "string") return TextResult(x)
return ValueResult(x)
}enum → sum types
// TypeScript
enum Status { Active, Inactive }// SuperJS — unit variants
type Status = Active | Inactive
// With payload
type Status = Active(string) | InactiveNon-null assertion ! → narrowing
// TypeScript
function len(s: string | null): number {
return s!.length
}// SuperJS
function len(s: string?): number {
if (s === null) return 0
return s.length
}namespace → ES modules
// TypeScript
namespace Util {
export function id<T>(x: T): T { return x }
}// SuperJS — util.sjs
export function id<T>(x: T): T {
return x
}Mapped / indexed / typeof types → explicit shapes
// TypeScript
type Partial<T> = { [K in keyof T]?: T[K] }
type Name = Person["name"]
type Inferred = typeof someValue// SuperJS — declare the shape you need
type StringMap {
[key: string]: string;
}
type Person { name: string; age: number; }
// Use Person.name fields directly; no T["key"] operatorOptional and nullable properties
// TypeScript
interface Config {
host: string
port?: number
}// SuperJS — nullable field (may be null when present)
type Config {
host: string;
port: number?;
}See null safety: T? means T | null; optional prop?: T on object types means T | undefined when absent.