Type-Safety System
MountSQLI derives row types from your schema with phantom types and inference helpers. No generators, no decorators.
The trick: phantom type params
Section titled “The trick: phantom type params”Each ColumnBuilder carries a phantom type param for its SQL type and
nullability:
const users = defineTable("users", { id: int().pk(), // ColumnBuilder<"int", false> email: text().notNull(), // ColumnBuilder<"text", false> age: int(), // ColumnBuilder<"int", true> (nullable)});The phantom params are erased at runtime but drive inference at compile time.
InferTable maps them
Section titled “InferTable maps them”InferTable uses a TypeMap to turn each column’s phantom type into a TS type,
and nullability decides | null:
type User = InferTable<typeof users>;// { id: number; email: string; age: number | null }The TypeMap
Section titled “The TypeMap”| SQL type | TS type |
|---|---|
int |
number |
text |
string |
bool |
boolean |
blob |
Uint8Array |
json |
unknown |
timestamp |
Date |
Why no codegen?
Section titled “Why no codegen?”Because the type lives in the column builders themselves, inference is immediate
and always in sync. Change a column, and every query result type updates on the
next tsc — there’s nothing to regenerate.
Best practices
Section titled “Best practices”- Export
type User = InferTable<typeof users>next to each table. - Use
DbFromConfig<typeof config>for the app-wideDb. - Let type errors catch bad column names before runtime.
Common mistakes
Section titled “Common mistakes”- Hand-writing row interfaces that drift from the table.
- Using
anyand losing the inference.
Related
Section titled “Related”- InferTable — the user-facing API.
- Defining Tables — the column builders.
