Skip to content

Types Reference

TypeDescriptionExample
stringText"hello"
numberInteger or float42, 3.14
booleanBooleantrue, false
TypeDescription
Result<T, E>Success (Ok(T)) or failure (Err(E))
Option<T>Present (Some(T)) or absent (None)
Settable<T>Three-state: Value(T), Clear, or Unchanged (for partial updates)
Array<T>Ordered collection
Map<K, V>Immutable key-value map
Set<T>Immutable unique collection
Promise<T>Async value
JSX.ElementReact JSX element

Floe has two forms of type declaration:

SyntaxMeaningUsed for
type Name { ... }Define a new Floe typeRecords, unions, newtypes, opaque types
type Name = ...Alias a TypeScript typeTS interop aliases, string literal unions, & intersections

Named product types with fields:

type User {
name: string,
email: string,
age: number,
}

Compiles to TypeScript type:

type User = {
name: string;
email: string;
age: number;
};

Record fields can have default values, which are used when the field is omitted at construction:

type Config {
baseUrl: string,
timeout: number = 5000,
retries: number = 3,
}
const cfg = Config(baseUrl: "https://api.com")
// timeout is 5000, retries is 3

Include fields from other record types using ... spread:

type BaseProps {
className: string,
disabled: boolean,
}
type ButtonProps {
...BaseProps,
onClick: () -> (),
label: string,
}

Compiles to TypeScript intersection:

type BaseProps = { className: string; disabled: boolean };
type ButtonProps = BaseProps & { onClick: () => void; label: string };

Multiple spreads are allowed. Field name conflicts are compile errors.

Spreads work with generic types, typeof, and npm imports:

import trusted { tv, VariantProps } from "tailwind-variants"
const cardVariants = tv({ base: "rounded-xl", variants: { padding: { sm: "p-4" } } })
type CardProps {
...VariantProps<typeof cardVariants>,
className: string,
}

Compiles to:

type CardProps = VariantProps<typeof cardVariants> & { className: string };

Tagged discriminated unions. Positional fields use ( ), named fields use { }:

type Shape {
| Circle(number) // positional
| Rectangle { width: number, height: number } // named
| Point
}

Compiles to TypeScript discriminated union:

type Shape =
| { tag: "Circle"; value: number }
| { tag: "Rectangle"; width: number; height: number }
| { tag: "Point" };

Positional: single field uses value, multiple use _0, _1. Named fields keep their names.

Single-variant wrappers that are distinct at compile time but erase to their base type at runtime:

type UserId(string)
type PostId(string)

UserId and PostId are both string at runtime, but the compiler prevents mixing them up.

Types where internals are hidden from other modules:

opaque type Email { string }

Only code in the module that defines Email can construct or destructure it. Other modules see it as an opaque blob.

String literal unions for npm interop:

type HttpMethod = "GET" | "POST" | "PUT" | "DELETE"

Compiles to the same TypeScript type (pass-through):

type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";

Match arms use string comparisons instead of tag checks:

match method {
"GET" -> "fetching",
"POST" -> "creating",
"PUT" -> "updating",
"DELETE" -> "removing",
}

Exhaustiveness is checked — missing a variant is a compile error.

type DivProps = ComponentProps<"div">

Combine TypeScript types with & (only valid in = declarations):

type CardProps = VariantProps<typeof variants> & { className: string }

For Floe-native record composition, use ...Spread in { } definitions instead.

// Named
User
string
// Generic
Array<number>
Result<User, Error>
Option<string>
// Function
(number, number) -> number
// Record (inline)
{ name: string, age: number }
// Array
Array<T>
// Tuple
(string, number)
// String literal (for npm interop)
ComponentProps<"div">
TypeScriptFloe equivalent
anyunknown + pattern matching
null, undefinedOption<T> with None
enumUnion types
interfacetype
voidUnit type ()