TypeScript
Pyxle gives you typed editor support and an opt-in type-check gate without asking you to write TypeScript in your .pyxl files. The model is consumption, not authoring: your server code is typed Python, your client code is plain JSX, and Pyxle generates the TypeScript declarations and config that make the framework surface fully typed in your editor.
What "TypeScript support" means in Pyxle today
Three concrete things, all generated for you on every pyxle dev / pyxle build:
- A
tsconfig.jsonunder.pyxle-build/client/, with strict mode and the right path aliases. - A full set of
.d.tsdeclarations for everypyxle/clienthook and component, so imports resolve to real types. - The
pyxle typecheckcommand, which runstsc --noEmitover the generated client so you can gate CI on type errors.
What it does not include is TypeScript syntax in the JSX block of a .pyxl file. The compiler emits the client component as .jsx, and esbuild (used by both the SSR runtime and Vite) loads it with its JSX loader, not the TypeScript loader — so type annotations, interface, generics, and x as T casts in a client block aren't transpiled. Pyxle flags them at compile time with a clear error pointing at your .pyxl file, rather than letting them fail later in the bundler (see Limitations). In short: the client block is plain JSX that gets type-checked, never type-annotated.
Server-side types (Python)
The @server and @action halves of a .pyxl file are ordinary Python — type them with normal Python hints. An @action body parameter can be annotated with a Pydantic model for automatic request validation, and pyxle openapi generates an OpenAPI 3.1 document straight from those models. This is independent of the client TypeScript toolchain below. See Server actions → Validating request bodies.
The generated client project
Every build writes a self-contained TypeScript project under .pyxle-build/client/. It is framework-owned and regenerated each run — don't edit it. It contains the tsconfig.json, the compiled .jsx pages, and a pyxle/ folder of declarations:
| Declaration | Types |
|---|---|
pyxle/index.d.ts |
the navigation runtime: navigate, prefetch, refresh, invalidate, getRouter, PyxleRouter, NavigationOptions (re-exports the component types below) |
pyxle/link.d.ts |
Link, LinkProps |
pyxle/image.d.ts |
Image, ImageProps, ImageLoader |
pyxle/head.d.ts |
Head, HeadProps |
pyxle/script.d.ts |
Script, ScriptProps |
pyxle/client-only.d.ts |
ClientOnly, ClientOnlyProps |
pyxle/slot.d.ts |
Slot, SlotProvider, useSlot, useSlots, slot types |
pyxle/use-action.d.ts |
useAction<TData>, ActionInvoker, ActionResult, ActionFieldErrors |
pyxle/use-pathname.d.ts |
usePathname() |
pyxle/use-auth.d.ts |
useAuth(), PyxleUser, AuthResult |
pyxle/use-websocket.d.ts |
useWebSocket(), UseWebSocketResult, WebSocketStatus |
pyxle/form.d.ts |
Form, FormProps |
Typed framework imports
Because of those declarations, importing from pyxle/client gives you full completions, hover, and signature help:
import { useAction, Link, Form } from 'pyxle/client';
export default function Page({ data }) {
// useAction is generic in the action's return type:
const subscribe = useAction('subscribe');
return (
<Form action="subscribe" onError={(message, fields) => console.log(fields)}>
<input name="email" />
<button disabled={subscribe.pending}>Subscribe</button>
</Form>
);
}useAction<TData> is generic in the data your action returns. For Pydantic 422 validation errors, the useAction invoker exposes a reactive fields map, and <Form> delivers field errors to its onError(message, fields) callback. These types describe the framework surface — they do not infer the shape of your own loader's data (see Limitations).
The generated tsconfig.json
The scaffolded config turns on strict mode and wires the import aliases Pyxle uses:
{
"compilerOptions": {
"strict": true,
"jsx": "react-jsx",
"module": "ESNext",
"moduleResolution": "Bundler", // your code is bundled by Vite/esbuild
"allowJs": true, // your pages are .jsx, checked against the .d.ts
"skipLibCheck": true,
"types": ["vite/client"],
"paths": { // resolved relative to this tsconfig (no baseUrl)
"/pages/*": ["./pages/*"],
"/routes/*": ["./routes/*"],
"pyxle/client": ["./pyxle/client"],
"pyxle/client/*": ["./pyxle/*"]
}
},
"include": ["./client-entry.js", "./pages/**/*.jsx", "./pyxle/**/*"]
}Resolution is "bundler" (not the legacy "node"/node10) because your code is
bundled by Vite + esbuild, not resolved by Node at runtime — and TypeScript 7.0
deprecates node10 resolution and baseUrl outright, so a current tsc would
reject them. paths work without baseUrl: they resolve relative to the
tsconfig.json.
Two honest notes on coverage:
checkJsis not enabled, so your own page JSX isn't deeply type-checked on its own. The real value is that misuse of the typed framework imports is caught — wrong props on<Image>, a baduseActioncall, etc.includecovers./pages/**/*.jsxbut not./routes/**/*.jsx, so files under aroutes/source root currently fall outside the type-check glob.
pyxle typecheck
pyxle typecheck compiles your .pyxl files to .jsx and runs tsc --noEmit against the generated tsconfig.json, surfacing any type errors against the framework declarations. It looks for tsc in your local node_modules/.bin, then globally, then via npx, so install typescript (locally is recommended) to use it. It's the recommended CI gate for catching framework-API misuse. See the CLI reference.
Editor setup
The pyxle-langkit language server runs a TypeScript language service over the JSX section of your .pyxl files, so you get completions and hover from the generated declarations as you type. It needs Node and typescript available in the project. See Editor setup.
Limitations
TypeScript support today is consumption-only. The following are not yet available and are tracked for a later phase — they are roadmap items, not bugs:
- No TypeScript syntax in the client block. The compiler emits
.jsxand the bundlers use a JSX loader, soconst x: number = 1,interface, generics, andascasts aren't supported in a.pyxlJSX block — keep the client half plain JSX. Pyxle's compiler flags TypeScript syntax there with a clear, source-located error. - No
.tsxauthoring. You can't write the client half as full.tsx/TypeScript yet. - No per-page loader-type generation. Your loader's
dataarrives on the client as an untyped object — there's no generated.d.tsdescribing a specific loader's return shape. - No OpenAPI → TypeScript client generation from your action models.
- No single
pyxle/client.d.tsbarrel. Types are per-module plusindex.d.ts; thepyxle/client.jsbarrel itself resolves as plain JS viaallowJs.
These are tracked for a later phase. The strict tsconfig scaffold and the framework .d.ts set are delivered today; loader-type generation and .tsx authoring remain pending.
Next steps
- Configure your editor: Editor setup
- Validate action inputs with types: Server actions
- CLI reference for
pyxle typecheck: CLI