GETTING STARTED
TypeScript
Write .ts and amo build / amo dev strip the types themselves — node's own type-stripper, still zero dependencies — and emit .js. Type checking stays your toolchain's job: the build erases types, it does not check them.
Two rules, both loud build errors
Erasable syntax only. An enum, namespace or parameter property has runtime meaning and is not a type annotation — write it as plain JS. It is the same subset TypeScript's own erasableSyntaxOnly flag enforces. Import the emitted path: ./util.js, not ./util.ts — TypeScript's own convention for ESM, and the error names the cure.
import { layout } from '../lib/layout.js'; // import the EMITTED path, never ../lib/layout.ts
enum Mode { A, B } // build error: not erasable — an enum has runtime meaning
namespace Util {} // build error, same reason
class P { constructor(public x: number) {} } // build error: a parameter property emits code
The types that ship
Core's .d.ts files are generated from its JSDoc. signal, computed and each are generic; Computed has a getter and no setter, so writing to one is a compile error, not a runtime surprise; isSignal is a type guard. Components are plain functions, so props are typed parameters — no prop-types layer to learn.
import { signal, html, type Signal } from '@amojs.dev/core';
// props are parameters; a reactive prop is a Signal<T>
const Price = (amount: Signal<number>, currency = 'EUR') =>
html`<b>${() => `${amount.value} ${currency}`}</b>`;
const double = computed(() => n.value * 2);
double.value = 1; // compile error: Computed has no setter
The one config file
This is what npm create amojs --ts writes, plus "types": ["node"], which a page that reads files at build time needs — pages are node code. .d.ts files in src/ are skipped by the build.
{
"compilerOptions": {
"strict": true,
"noEmit": true,
"target": "es2022",
"module": "esnext",
"moduleResolution": "bundler",
"lib": ["es2022", "dom"],
"types": ["node"],
"erasableSyntaxOnly": true,
"verbatimModuleSyntax": true,
"skipLibCheck": true
},
"include": ["src"]
}
With Vite
@amojs.dev/vite is a one-hook plugin over the same compileModule. It runs in the normal phase on purpose: Vite's own esbuild has already stripped TypeScript by then, so the plugin only ever sees JavaScript.