End-to-End Type Safety
Types flow from your database schema through server handlers to frontend components across all frameworks : React, Svelte, Vue, and Angular.
#How It Works
AbsoluteJS enforces a single type flow across your entire stack: Database Schema → Inferred Types → Server Handler → Page Component. Every step is validated by TypeScript at compile time.
Compile-Time Errors
Missing or incorrectly typed props are caught by TypeScript before your code runs. No runtime surprises.
Refactoring Safety
Rename a field in your database schema and TypeScript shows every place that needs updating across your entire application.
#Schema to Types
Define your database schema with Drizzle and infer TypeScript types directly from your table definitions. These types are the single source of truth for your entire application.
// db/schema.ts
import { pgTable, text, timestamp, boolean } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: text('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull().unique()
});
export const notifications = pgTable('notifications', {
id: text('id').primaryKey(),
userId: text('user_id').notNull().references(() => users.id),
message: text('message').notNull(),
read: boolean('read').notNull().default(false),
createdAt: timestamp('created_at').notNull().defaultNow()
});
// types/databaseTypes.ts
// Infer types directly from your table definitions
export type User = typeof users.$inferSelect;
export type NewUser = typeof users.$inferInsert;
export type Notification = typeof notifications.$inferSelect;
export type NewNotification = typeof notifications.$inferInsert;#React
Use handleReactPageRequest with a generic Props parameter to enforce type safety at the server boundary. TypeScript ensures the props you pass match the component's expected types exactly.
// backend/server.ts
import { User, Notification } from '../types/databaseTypes';
import { Dashboard } from '../frontend/pages/Dashboard';
type DashboardProps = {
user: User;
notifications: Notification[];
unreadCount: number;
};
new Elysia()
.get('/dashboard', async ({ cookie }) => {
const user = await getAuthenticatedUser(cookie);
const notifications = await getNotifications(user.id);
// Type error if props don't match DashboardProps!
return handleReactPageRequest({
Page: Dashboard,
index: asset(manifest, 'DashboardIndex'),
props: {
user,
notifications,
unreadCount: notifications.filter(n => !n.read).length
}
});
})#Svelte
Svelte components enjoy the same end-to-end type safety as React. Define your database schema and infer types directly from your table definitions using Drizzle:
// db/schema.ts
import { pgTable, text, integer } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: text('id').primaryKey(),
name: text('name').notNull(),
avatar: text('avatar')
});
export const stats = pgTable('stats', {
id: text('id').primaryKey(),
userId: text('user_id').notNull().references(() => users.id),
views: integer('views').notNull().default(0),
revenue: integer('revenue').notNull().default(0)
});
// types/databaseTypes.ts
export type User = typeof users.$inferSelect;
export type Stats = typeof stats.$inferSelect;Use those types in your server handlers to ensure props match your components:
// backend/server.ts
import { User, Stats } from '../types/databaseTypes';
type DashboardProps = {
user: User;
stats: Stats;
};
new Elysia()
.get('/dashboard', async ({ cookie }) => {
const user = await getUser(cookie);
const stats = await getStats(user.id);
// TypeScript error if props don't match DashboardProps!
return handleSveltePageRequest({
indexPath: asset(manifest, 'DashboardIndex'),
pagePath: asset(manifest, 'DashboardPage'),
props: { user, stats }
});
})#Vue
Vue 3's defineProps<T> with TypeScript generics provides complete compile-time type checking. The generic parameter is validated against the props passed from the server, so errors are caught before your code runs.
// Vue 3 with TypeScript provides complete type safety
// defineProps<T>() ensures compile-time type checking
<script setup lang="ts">
// Types are enforced at compile time
type User = {
id: string;
name: string;
role: 'admin' | 'user';
};
type AdminDashboardProps = {
user: User;
systemStats: SystemStats;
};
// TypeScript error if server sends wrong types!
const props = defineProps<AdminDashboardProps>();
// Computed properties are also type-safe
const isAdmin = computed(() => props.user.role === 'admin');
</script>#Angular
Angular components receive typed props via @Input() decorators. The page handler acts as a typed provider, injecting props into the component at render time. TypeScript catches mismatched props at compile time.
// Types flow from your server to Angular components via props
// The page handler enforces type safety at the boundary
import { defineAngularPage } from '@absolutejs/absolute/angular';
import { Component, inject, InjectionToken } from '@angular/core';
// 1. Define your props type
type SettingsProps = {
user: User;
preferences: UserPreferences;
};
// 2. Angular component receives typed props via matching InjectionTokens
export const USER = new InjectionToken<SettingsProps['user']>('USER');
export const PREFERENCES = new InjectionToken<SettingsProps['preferences']>(
'PREFERENCES'
);
@Component({
selector: 'app-settings',
standalone: true,
template: `
<div>
<h1>Settings for {{ user.name }}</h1>
<label>
Theme:
<select [value]="preferences.theme">
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</label>
</div>
`
})
export class Settings {
readonly user = inject(USER);
readonly preferences = inject(PREFERENCES);
}
export const page = defineAngularPage<SettingsProps>({
component: Settings
});
// 3. Server passes type-safe props
.get('/settings', async ({ cookie }) => {
const user = await getUser(cookie);
const preferences = await getPreferences(user.id);
// TypeScript error if props don't match SettingsProps!
return handleAngularPageRequest<typeof SettingsPage>({
pagePath: asset(manifest, 'Settings'),
indexPath: asset(manifest, 'SettingsIndex'),
headTag: generateHeadElement({ title: 'Settings' }),
props: { user, preferences }
});
})#PropsOf Utilities
AbsoluteJS provides helper types that extract the prop types from any component. Use these when you need to reference a component's props without importing the type definition directly.
These utility types are especially useful when building shared layouts or higher-order components that need to pass through props without manually duplicating type definitions.