Skip to content

TypeScript

Flick works with TypeScript out of the box.

Configuration

Add a tsconfig.json:

json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "jsx": "preserve",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"]
}

Typing Fx

tsx
import { fx } from '@flickjs/runtime';

// Type is inferred
const count = fx(0); // Fx<number>

// Explicit typing
const user = fx<{ name: string; age: number } | null>(null);

// Update with type safety
user.set({ name: 'John', age: 30 });

Typing Components

Props Interface

tsx
interface ButtonProps {
  children: any;
  onclick: () => void;
  variant?: 'primary' | 'secondary' | 'danger';
  disabled?: boolean;
}

function Button({ children, onclick, variant = 'primary', disabled = false }: ButtonProps) {
  return (
    <button class={`btn btn-${variant}`} onclick={onclick} disabled={disabled}>
      {children}
    </button>
  );
}

Generic Components

tsx
interface ListProps<T> {
  items: T[];
  renderItem: (item: T) => any;
}

function List<T>({ items, renderItem }: ListProps<T>) {
  return (
    <ul>
      {items.map((item) => (
        <li>{renderItem(item)}</li>
      ))}
    </ul>
  );
}

// Usage
<List items={[1, 2, 3]} renderItem={(num) => <span>{num * 2}</span>} />;

Typing Events

tsx
function Form() {
  const value = fx('');

  const handleInput = (e: Event) => {
    const target = e.target as HTMLInputElement;
    value.set(target.value);
  };

  const handleSubmit = (e: Event) => {
    e.preventDefault();
    console.log('Submitted:', value());
  };

  return (
    <form onsubmit={handleSubmit}>
      <input type="text" value={value()} oninput={handleInput} />
      <button type="submit">Submit</button>
    </form>
  );
}

Type Definitions for Router

When using the router, add type definitions for dynamic params:

tsx
import { params } from '@flickjs/router';

interface BlogParams {
  slug: string;
}

function BlogPost() {
  const typedParams = params<BlogParams>();

  return <h1>Blog Post: {typedParams.slug}</h1>;
}

Type Definitions for Queries

tsx
import { query } from '@flickjs/runtime';

interface User {
  id: number;
  name: string;
  email: string;
}

const user = query<User>(() => fetch('/api/user').then((res) => res.json()));

// user() returns User | undefined
// user.loading() returns boolean
// user.error() returns Error | undefined

Strict Null Checks

With strict mode enabled, handle potential undefined values:

tsx
const user = query<User>(() => fetchUser());

function Profile() {
  // Handle loading and error states
  if (user.loading()) return <p>Loading...</p>;
  if (user.error()) return <p>Error: {user.error()?.message}</p>;

  // Now TypeScript knows user() is defined
  const userData = user()!;

  return (
    <div>
      <h1>{userData.name}</h1>
      <p>{userData.email}</p>
    </div>
  );
}

Released under the MIT License.