Typescript Integration
Refract provides comprehensive TypeScript support with full type inference, strict type checking, and enhanced developer experience. The framework is built with TypeScript from the ground up, ensuring seamless integration and excellent IntelliSense support.
Getting started
Installation
Install Refract with TypeScript support:
npm install refract-js
npm install -D typescript @types/node
Basic TypeScript configuration
Create or update your tsconfig.json:
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"jsx": "react-jsx",
"jsxImportSource": "refract",
"strict": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
Refract TypeScript configuration
Update your refract.config.js for TypeScript support:
export default {
typescript: {
enabled: true,
strict: true,
generateTypes: true,
typeCheck: "build" // Options: "build", "watch", "off"
}
};
Component typing
Basic component types
Components are strongly typed with automatic prop inference:
import { createComponent, ComponentProps } from "refract";
interface CounterProps {
initialValue?: number;
onCountChange?: (count: number) => void;
disabled?: boolean;
}
const Counter = createComponent<CounterProps>(({ lens, initialValue = 0, onCountChange, disabled }) => {
const count = lens.useRefraction(initialValue);
const increment = () => {
if (!disabled) {
const newCount = count.value + 1;
count.set(newCount);
onCountChange?.(newCount);
}
};
return (
<button onClick={increment} disabled={disabled}>
Count: {count.value}
</button>
);
});
// Usage with full type checking
<Counter
initialValue={10}
onCountChange={(count) => console.log(count)} // count is inferred as number
/>
Generic components
Create reusable generic components:
interface ListProps<T> {
items: T[];
renderItem: (item: T, index: number) => JSX.Element;
keyExtractor: (item: T) => string | number;
}
const List = <T,>(props: ListProps<T>) => createComponent<ListProps<T>>(({ lens, items, renderItem, keyExtractor }) => {
return (
<div>
{items.map((item, index) => (
<div key={keyExtractor(item)}>
{renderItem(item, index)}
</div>
))}
</div>
);
});
// Usage with type inference
<List
items={users} // users: User[]
renderItem={(user) => <span>{user.name}</span>} // user is inferred as User
keyExtractor={(user) => user.id} // user.id must exist and be string | number
/>
Component prop validation
Use strict prop typing with validation:
import { PropTypes } from "refract";
interface UserCardProps {
user: {
id: string;
name: string;
email: string;
avatar?: string;
};
size?: "small" | "medium" | "large";
onClick?: (userId: string) => void;
}
const UserCard = createComponent<UserCardProps>(({ lens, user, size = "medium", onClick }) => {
return (
<div
className={`user-card user-card--${size}`}
onClick={() => onClick?.(user.id)}
>
<img src={user.avatar || "/default-avatar.png"} alt={user.name} />
<h3>{user.name}</h3>
<p>{user.email}</p>
</div>
);
});
// TypeScript will error if props don't match
UserCard.propTypes = {
user: PropTypes.shape({
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
email: PropTypes.string.isRequired,
avatar: PropTypes.string
}).isRequired,
size: PropTypes.oneOf(["small", "medium", "large"]),
onClick: PropTypes.func
};
Refraction typing
Typed refractions
Refractions are fully typed with inference:
import { useRefraction, Refraction } from "refract";
// Explicit typing
const count: Refraction<number> = useRefraction<number>(0);
const user: Refraction<User | null> = useRefraction<User | null>(null);
// Type inference (preferred)
const theme = useRefraction("light"); // Inferred as Refraction<string>
const isLoading = useRefraction(false); // Inferred as Refraction<boolean>
// Complex types
interface AppState {
user: User | null;
theme: "light" | "dark";
notifications: Notification[];
}
const appState = useRefraction<AppState>({
user: null,
theme: "light",
notifications: []
});
// TypeScript enforces type safety
appState.set({
user: currentUser,
theme: "dark", // Must be "light" | "dark"
notifications: []
});
Refraction utilities with types
import { computed, effect } from "refract";
const firstName = useRefraction("John");
const lastName = useRefraction("Doe");
// Computed values maintain type safety
const fullName = computed(() => `${firstName.value} ${lastName.value}`); // string
const nameLength = computed(() => fullName.value.length); // number
// Effects with proper typing
effect(() => {
console.log(fullName.value.toUpperCase()); // fullName.value is known to be string
}, [fullName]);
Custom optics typing
Strongly typed optics
Create type-safe custom optics:
import { createOptic, useRefraction, useEffect } from "refract";
// Define the return type interface
interface UseApiResult<T> {
data: T | null;
loading: boolean;
error: string | null;
refetch: () => Promise<void>;
}
// Generic optic with proper typing
function useApi<T>(url: string): UseApiResult<T> {
return createOptic(() => {
const data = useRefraction<T | null>(null);
const loading = useRefraction(false);
const error = useRefraction<string | null>(null);
const fetchData = async (): Promise<void> => {
loading.set(true);
error.set(null);
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const result: T = await response.json();
data.set(result);
} catch (err) {
error.set(err instanceof Error ? err.message : "Unknown error");
} finally {
loading.set(false);
}
};
useEffect(() => {
fetchData();
}, [url]);
return {
data: data.value,
loading: loading.value,
error: error.value,
refetch: fetchData
};
});
}
// Usage with type inference
interface User {
id: string;
name: string;
email: string;
}
const UserProfile = createComponent<{ userId: string }>(({ lens, userId }) => {
const { data, loading, error } = useApi<User>(`/api/users/${userId}`);
// data is typed as User | null
// loading is typed as boolean
// error is typed as string | null
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
if (!data) return <div>No user found</div>;
// TypeScript knows data is User here
return (
<div>
<h1>{data.name}</h1>
<p>{data.email}</p>
</div>
);
});
Optic composition with types
interface AuthState {
user: User | null;
token: string | null;
isAuthenticated: boolean;
}
const useAuth = createOptic((): AuthState => {
const user = useRefraction<User | null>(null);
const token = useRefraction<string | null>(localStorage.getItem('token'));
const isAuthenticated = computed(() => Boolean(user.value && token.value));
return {
user: user.value,
token: token.value,
isAuthenticated: isAuthenticated.value
};
});
// Combine optics with preserved typing
const useAuthenticatedApi = <T>(url: string) => {
const auth = useAuth();
const api = useApi<T>(auth.isAuthenticated ? url : '');
return {
...api,
isAuthenticated: auth.isAuthenticated
};
};
Event handling types
Strongly typed event handlers
import { MouseEvent, ChangeEvent, FormEvent } from "refract";
interface FormData {
email: string;
password: string;
}
const LoginForm = createComponent(() => {
const formData = useRefraction<FormData>({ email: '', password: '' });
const errors = useRefraction<Partial<FormData>>({});
const handleInputChange = (field: keyof FormData) =>
(event: ChangeEvent<HTMLInputElement>) => {
formData.set({
...formData.value,
[field]: event.target.value
});
// Clear error for this field
if (errors.value[field]) {
errors.set({
...errors.value,
[field]: undefined
});
}
};
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
const newErrors: Partial<FormData> = {};
if (!formData.value.email) newErrors.email = 'Email is required';
if (!formData.value.password) newErrors.password = 'Password is required';
if (Object.keys(newErrors).length > 0) {
errors.set(newErrors);
return;
}
// Submit form
submitLogin(formData.value);
};
const handleButtonClick = (action: string) =>
(event: MouseEvent<HTMLButtonElement>) => {
console.log(`Button clicked: ${action}`);
};
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={formData.value.email}
onChange={handleInputChange('email')}
placeholder="Email"
/>
{errors.value.email && <span className="error">{errors.value.email}</span>}
<input
type="password"
value={formData.value.password}
onChange={handleInputChange('password')}
placeholder="Password"
/>
{errors.value.password && <span className="error">{errors.value.password}</span>}
<button type="submit">Login</button>
<button type="button" onClick={handleButtonClick('forgot-password')}>
Forgot Password?
</button>
</form>
);
});
Advanced TypeScript features
Discriminated unions
type LoadingState<T> =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: T }
| { status: 'error'; error: string };
const useAsyncState = <T>(asyncFn: () => Promise<T>) => {
return createOptic(() => {
const state = useRefraction<LoadingState<T>>({ status: 'idle' });
const execute = async () => {
state.set({ status: 'loading' });
try {
const data = await asyncFn();
state.set({ status: 'success', data });
} catch (error) {
state.set({
status: 'error',
error: error instanceof Error ? error.message : 'Unknown error'
});
}
};
return {
state: state.value,
execute
};
});
};
// Usage with type narrowing
const DataComponent = createComponent(() => {
const { state, execute } = useAsyncState(() => fetchUserData());
const renderContent = () => {
switch (state.status) {
case 'idle':
return <button onClick={execute}>Load Data</button>;
case 'loading':
return <div>Loading...</div>;
case 'success':
// TypeScript knows state.data exists and is properly typed
return <div>Welcome, {state.data.name}!</div>;
case 'error':
// TypeScript knows state.error exists
return <div>Error: {state.error}</div>;
}
};
return <div>{renderContent()}</div>;
});
Conditional types and utility types
import { ComponentProps, Refraction } from "refract";
// Extract prop types from components
type CounterProps = ComponentProps<typeof Counter>;
// Create conditional types for refractions
type RefractionValue<T> = T extends Refraction<infer U> ? U : never;
// Utility type for optic return values
type OpticReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// Create mapped types for form validation
type ValidationErrors<T> = {
[K in keyof T]?: string;
};
type FormState<T> = {
values: T;
errors: ValidationErrors<T>;
touched: { [K in keyof T]?: boolean };
isValid: boolean;
isSubmitting: boolean;
};
const useForm = <T extends Record<string, any>>(initialValues: T) => {
return createOptic((): FormState<T> => {
const values = useRefraction<T>(initialValues);
const errors = useRefraction<ValidationErrors<T>>({});
const touched = useRefraction<{ [K in keyof T]?: boolean }>({});
const isSubmitting = useRefraction(false);
const isValid = computed(() =>
Object.keys(errors.value).length === 0
);
return {
values: values.value,
errors: errors.value,
touched: touched.value,
isValid: isValid.value,
isSubmitting: isSubmitting.value
};
});
};
Template literal types
// Create type-safe CSS class builders
type Size = 'sm' | 'md' | 'lg';
type Variant = 'primary' | 'secondary' | 'danger';
type ButtonClass = `btn btn--${Variant} btn--${Size}`;
interface ButtonProps {
size?: Size;
variant?: Variant;
children: React.ReactNode;
onClick?: () => void;
}
const Button = createComponent<ButtonProps>(({
lens,
size = 'md',
variant = 'primary',
children,
onClick
}) => {
const className: ButtonClass = `btn btn--${variant} btn--${size}`;
return (
<button className={className} onClick={onClick}>
{children}
</button>
);
});
// Type-safe API endpoint builders
type ApiEndpoint<T extends string> = `/api/${T}`;
type UserEndpoint = ApiEndpoint<'users' | 'profile' | 'settings'>;
const useApiEndpoint = <T extends string>(endpoint: ApiEndpoint<T>) => {
return createOptic(() => {
// Implementation
});
};
Type guards and narrowing
// Custom type guards for runtime type checking
function isUser(obj: unknown): obj is User {
return (
typeof obj === 'object' &&
obj !== null &&
'id' in obj &&
'name' in obj &&
'email' in obj
);
}
function isUserArray(obj: unknown): obj is User[] {
return Array.isArray(obj) && obj.every(isUser);
}
const useTypeSafeApi = <T>(url: string, typeGuard: (obj: unknown) => obj is T) => {
return createOptic(() => {
const data = useRefraction<T | null>(null);
const error = useRefraction<string | null>(null);
const fetchData = async () => {
try {
const response = await fetch(url);
const result = await response.json();
if (typeGuard(result)) {
data.set(result);
} else {
error.set('Invalid data format received from API');
}
} catch (err) {
error.set(err instanceof Error ? err.message : 'Unknown error');
}
};
return {
data: data.value,
error: error.value,
fetchData
};
});
};
// Usage with type safety
const UserList = createComponent(() => {
const { data, error } = useTypeSafeApi('/api/users', isUserArray);
// TypeScript knows data is User[] | null
return (
<div>
{data?.map(user => (
<div key={user.id}>{user.name}</div>
))}
</div>
);
});
Configuration and tooling
ESLint configuration
{
"extends": [
"@typescript-eslint/recommended",
"plugin:refract/recommended"
],
"rules": {
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/explicit-function-return-type": "warn",
"refract/exhaustive-deps": "error",
"refract/rules-of-optics": "error"
}
}
IDE configuration
Configure VS Code for optimal TypeScript support:
// .vscode/settings.json
{
"typescript.preferences.includePackageJsonAutoImports": "on",
"typescript.suggest.autoImports": true,
"typescript.preferences.importModuleSpecifier": "relative",
"refract.typescript.enabled": true,
"refract.typescript.strict": true
}
Build script integration
{
"scripts": {
"type-check": "tsc --noEmit",
"type-check:watch": "tsc --noEmit --watch",
"build": "refract build --typescript",
"dev": "refract dev --typescript --type-check"
}
}
Testing with TypeScript
Type-safe component testing
import { render, screen } from '@testing-library/refract';
import { ComponentProps } from 'refract';
// Type-safe test helpers
const renderCounter = (props?: Partial<ComponentProps<typeof Counter>>) => {
const defaultProps: ComponentProps<typeof Counter> = {
initialValue: 0
};
return render(<Counter {...defaultProps} {...props} />);
};
describe('Counter component', () => {
test('increments count when clicked', () => {
renderCounter({ initialValue: 5 });
const button = screen.getByRole('button');
expect(button).toHaveTextContent('Count: 5');
button.click();
expect(button).toHaveTextContent('Count: 6');
});
test('calls onCountChange with correct type', () => {
const onCountChange = jest.fn<void, [number]>();
renderCounter({
initialValue: 0,
onCountChange
});
screen.getByRole('button').click();
expect(onCountChange).toHaveBeenCalledWith(1);
// TypeScript ensures onCountChange receives number
});
});
Testing custom optics
import { renderOptic } from '@testing-library/refract-optics';
describe('useApi optic', () => {
test('returns properly typed data', async () => {
const { result } = renderOptic(() => useApi<User[]>('/api/users'));
expect(result.current.data).toBeNull();
expect(result.current.loading).toBe(true);
await waitFor(() => {
expect(result.current.loading).toBe(false);
expect(result.current.data).toEqual(expect.arrayContaining([
expect.objectContaining({
id: expect.any(String),
name: expect.any(String),
email: expect.any(String)
})
]));
});
});
});
Common patterns and best practices
Prop drilling prevention
// Create type-safe context
interface ThemeContextValue {
theme: 'light' | 'dark';
toggleTheme: () => void;
}
const ThemeContext = createContext<ThemeContextValue>();
const useTheme = (): ThemeContextValue => {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
};
// Provider with proper typing
const ThemeProvider = createComponent<{ children: React.ReactNode }>(({
lens,
children
}) => {
const theme = lens.useRefraction<'light' | 'dark'>('light');
const toggleTheme = () => {
theme.set(theme.value === 'light' ? 'dark' : 'light');
};
const contextValue: ThemeContextValue = {
theme: theme.value,
toggleTheme
};
return (
<ThemeContext.Provider value={contextValue}>
{children}
</ThemeContext.Provider>
);
});
Error boundaries with types
interface ErrorInfo {
componentStack: string;
errorBoundary?: string;
}
interface ErrorBoundaryState {
hasError: boolean;
error?: Error;
errorInfo?: ErrorInfo;
}
const ErrorBoundary = createComponent<{
children: React.ReactNode;
fallback?: (error: Error, errorInfo: ErrorInfo) => React.ReactNode;
}>(({ lens, children, fallback }) => {
const errorState = lens.useRefraction<ErrorBoundaryState>({
hasError: false
});
lens.useErrorBoundary((error: Error, errorInfo: ErrorInfo) => {
errorState.set({
hasError: true,
error,
errorInfo
});
});
if (errorState.value.hasError && errorState.value.error && errorState.value.errorInfo) {
if (fallback) {
return fallback(errorState.value.error, errorState.value.errorInfo);
}
return (
<div>
<h2>Something went wrong.</h2>
<pre>{errorState.value.error.message}</pre>
</div>
);
}
return children;
});
Performance considerations
TypeScript integration in Refract is designed for optimal performance:
- Compile-time optimizations: Type information is stripped during compilation
- Tree shaking: Unused type definitions don't affect bundle size
- Incremental compilation: Only changed files are re-type-checked
- Worker-based type checking: Type checking runs in parallel with builds
Migration strategies
Gradual TypeScript adoption
// Start with .js files and add types incrementally
// File: components/Counter.js
const Counter = createComponent(({ lens, initialValue = 0 }) => {
// Implementation
});
// Migrate to TypeScript
// File: components/Counter.tsx
interface CounterProps {
initialValue?: number;
}
const Counter = createComponent<CounterProps>(({ lens, initialValue = 0 }) => {
// Same implementation with type safety
});
Legacy code integration
// Handle untyped legacy components
declare module "legacy-components" {
export const LegacyComponent: any;
}
// Gradually add proper types
interface LegacyComponentProps {
data: unknown;
callback: (result: any) => void;
}
const TypedLegacyWrapper = createComponent<LegacyComponentProps>(({
lens,
data,
callback
}) => {
return <LegacyComponent data={data} callback={callback} />;
});
Troubleshooting common TypeScript issues
Type errors with JSX
// Error: JSX element type 'Component' does not have any construct or call signatures
// Solution: Ensure proper component typing
interface MyComponentProps {
title: string;
}
const MyComponent = createComponent<MyComponentProps>(({ lens, title }) => {
return <h1>{title}</h1>;
});
// Correct usage
<MyComponent title="Hello World" />
Refraction type inference issues
// Issue: Type inference fails with complex initial values
const complexState = useRefraction({
user: null as User | null, // Explicit type annotation needed
settings: {} as UserSettings,
preferences: new Map<string, any>()
});
// Better: Use generic parameter
const complexState = useRefraction<{
user: User | null;
settings: UserSettings;
preferences: Map<string, any>;
}>({
user: null,
settings: {},
preferences: new Map()
});
Optic return type issues
// Problem: Return type not inferred correctly
const useUserData = createOptic(() => {
const user = useRefraction<User | null>(null);
const loading = useRefraction(false);
// TypeScript might not infer the complete return type
return {
user: user.value,
loading: loading.value,
setUser: user.set // Method reference might lose types
};
});
// Solution: Explicit return type annotation
interface UseUserDataReturn {
user: User | null;
loading: boolean;
setUser: (user: User | null) => void;
}
const useUserData = createOptic((): UseUserDataReturn => {
// Implementation
});
Generic component constraints
// Add constraints to generic types for better type safety
interface DataTableProps<T extends Record<string, any>> {
data: T[];
columns: Array<{
key: keyof T;
header: string;
render?: (value: T[keyof T]) => React.ReactNode;
}>;
}
const DataTable = <T extends Record<string, any>>(
props: DataTableProps<T>
) => createComponent<DataTableProps<T>>(({ lens, data, columns }) => {
return (
<table>
<thead>
<tr>
{columns.map(col => (
<th key={String(col.key)}>{col.header}</th>
))}
</tr>
</thead>
<tbody>
{data.map((row, index) => (
<tr key={index}>
{columns.map(col => (
<td key={String(col.key)}>
{col.render ? col.render(row[col.key]) : String(row[col.key])}
</td>
))}
</tr>
))}
</tbody>
</table>
);
});
Advanced type manipulation
Creating type-safe stores
// Define store schema with strict typing
interface AppStore {
auth: {
user: User | null;
token: string | null;
isAuthenticated: boolean;
};
ui: {
theme: 'light' | 'dark';
sidebarOpen: boolean;
notifications: Notification[];
};
data: {
users: User[];
products: Product[];
orders: Order[];
};
}
// Type-safe store selectors
type StoreSelector<T> = (state: AppStore) => T;
type StoreUpdater<T> = (state: AppStore, payload: T) => AppStore;
const createTypedStore = <T extends Record<string, any>>(initialState: T) => {
return createOptic(() => {
const state = useRefraction<T>(initialState);
const select = <R>(selector: (state: T) => R): R => {
return selector(state.value);
};
const update = <P>(updater: (state: T, payload: P) => T) =>
(payload: P) => {
const newState = updater(state.value, payload);
state.set(newState);
};
return { state: state.value, select, update };
});
};
// Usage with full type safety
const store = createTypedStore<AppStore>({
auth: { user: null, token: null, isAuthenticated: false },
ui: { theme: 'light', sidebarOpen: false, notifications: [] },
data: { users: [], products: [], orders: [] }
});
// Type-safe selectors
const currentUser = store.select(state => state.auth.user); // User | null
const themeMode = store.select(state => state.ui.theme); // 'light' | 'dark'
Conditional rendering with types
// Type-safe conditional rendering patterns
type RenderProps<T> = {
data: T;
loading: boolean;
error: string | null;
};
interface ConditionalRenderProps<T> {
render: (props: RenderProps<T>) => React.ReactNode;
loading?: () => React.ReactNode;
error?: (error: string) => React.ReactNode;
empty?: () => React.ReactNode;
}
const ConditionalRender = <T,>({
render,
loading,
error,
empty
}: ConditionalRenderProps<T> & RenderProps<T>) =>
createComponent<ConditionalRenderProps<T> & RenderProps<T>>(({
lens,
data,
loading: isLoading,
error: errorMessage,
render,
loading: LoadingComponent,
error: ErrorComponent,
empty: EmptyComponent
}) => {
if (isLoading && LoadingComponent) {
return LoadingComponent();
}
if (errorMessage && ErrorComponent) {
return ErrorComponent(errorMessage);
}
if (!data && EmptyComponent) {
return EmptyComponent();
}
return render({ data, loading: isLoading, error: errorMessage });
});
Performance monitoring with types
interface PerformanceMetrics {
renderTime: number;
refractionUpdates: number;
opticExecutions: number;
memoryUsage: number;
}
const usePerformanceMonitor = (): PerformanceMetrics => {
return createOptic(() => {
const metrics = useRefraction<PerformanceMetrics>({
renderTime: 0,
refractionUpdates: 0,
opticExecutions: 0,
memoryUsage: 0
});
const startTime = performance.now();
lens.useEffect(() => {
const endTime = performance.now();
metrics.set(prev => ({
...prev,
renderTime: endTime - startTime
}));
});
return metrics.value;
});
};
// Type-safe performance tracking
const PerformanceTracker = createComponent<{
children: React.ReactNode;
onMetrics?: (metrics: PerformanceMetrics) => void;
}>(({ lens, children, onMetrics }) => {
const metrics = usePerformanceMonitor();
lens.useEffect(() => {
onMetrics?.(metrics);
}, [metrics]);
return <>{children}</>;
});
Build and deployment considerations
Type checking in CI/CD
# .github/workflows/build.yml
name: Build and Type Check
on: [push, pull_request]
jobs:
type-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
- run: npm ci
- run: npm run type-check
- run: npm run build
- run: npm run test:types
Production type stripping
// refract.config.js - Production optimization
export default {
typescript: {
production: {
stripTypes: true, // Remove type annotations
preserveSourceMaps: false, // Disable source maps
optimizeImports: true // Tree-shake type-only imports
}
}
};
Bundle analysis for types
# Analyze TypeScript impact on bundle size
npx refract analyze --typescript
TypeScript Analysis:
┌─────────────────────┬─────────┬─────────────┐
│ Category │ Size │ After Strip │
├─────────────────────┼─────────┼─────────────┤
│ Type annotations │ 0KB │ 0KB │
│ Interface definitions│ 0KB │ 0KB │
│ Type imports │ 2.1KB │ 0KB │
│ Runtime helpers │ 1.8KB │ 1.8KB │
└─────────────────────┴─────────┴─────────────┘
Recommendations:
• All type information successfully stripped
• No runtime TypeScript overhead detected
• Consider enabling aggressive type checking for better DX
Future TypeScript features
Preparing for TypeScript 5.x
// Using newer TypeScript features as they become available
const useAdvancedTypes = createOptic(() => {
// Using 'const' type parameters (TS 5.0+)
const config = useRefraction({
mode: 'development' as const,
features: ['typeCheck', 'hotReload'] as const
});
// Using 'satisfies' operator for better inference
const apiEndpoints = {
users: '/api/users',
products: '/api/products',
orders: '/api/orders'
} satisfies Record<string, string>;
return {
config: config.value,
endpoints: apiEndpoints
};
});
Experimental features
// tsconfig.json - Enable experimental features
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"exactOptionalPropertyTypes": true,
"noUncheckedIndexedAccess": true
}
}
Summary
TypeScript integration in Refract provides:
- Complete type safety across components, optics, and refractions
- Excellent developer experience with IntelliSense and error checking
- Performance optimizations with compile-time type stripping
- Gradual adoption path for existing JavaScript codebases
- Advanced type features for complex application patterns
- Comprehensive testing support with type-safe testing utilities
By following these patterns and best practices, you can leverage TypeScript's full power to build robust, maintainable Refract applications with confidence and clarity.