@@ -67,9 +67,9 @@ All four of TypeScript's `?` uses (`?.`, `??`, `?:`, `? :`) are removed. `?` now
6767| ` ? ` operator | ` fetchUser(id)? ` | early return on Err/None |
6868| Branded types | ` type UserId = Brand<string, "UserId"> ` | ` string ` at runtime |
6969| Opaque types | ` opaque type HashedPw = string ` | ` string ` , but only the defining module can create/read |
70- | Tagged unions | ` type Route = Home \| Profile( id: string) ` | discriminated union |
70+ | Tagged unions | ` type Route { \| Home \| Profile { id: string } } ` | discriminated union |
7171| String literal unions | ` type Method = "GET" \| "POST" \| "PUT" ` | ` "GET" \| "POST" \| "PUT" ` (pass-through for npm interop) |
72- | Nested unions | ` type ApiError = Network( NetworkError) \| NotFound ` | nested discriminated union (compiler generates tags) |
72+ | Nested unions | ` type ApiError { \| Network { NetworkError } \| NotFound } ` | nested discriminated union (compiler generates tags) |
7373| Multi-depth match | ` Network(Timeout(ms)) -> ... ` | nested if/else with tag checks |
7474| Type constructors | ` User(name: "Ryan", email: e) ` | ` { name: "Ryan", email: e } ` (compiler adds tags for unions) |
7575| Record spread | ` User(..user, name: "New") ` | ` { ...user, name: "New" } ` |
@@ -383,7 +383,7 @@ The return type of `collect { ... }` is `Result<T, Array<E>>` where:
383383### Option<T > - No Null, No Undefined
384384
385385``` floe
386- type User = {
386+ type User {
387387 name: string // always present
388388 nickname: Option<string> // might not exist
389389 avatar: Option<Url> // might not exist
@@ -411,86 +411,95 @@ const avatar = user.nickname |> Option.flatMap(fn(n) findAvatar(n))
411411` type ` does everything. No ` | ` = record. Has ` | ` = union. Unions nest infinitely.
412412
413413``` floe
414- // Record type (no |)
415- type User = {
414+ // Record type
415+ type User {
416416 id: UserId
417417 name: string
418418 email: Email
419419}
420420
421421// Record type composition with spread
422- type BaseProps = {
422+ type BaseProps {
423423 className: string,
424424 disabled: boolean,
425425}
426426
427- type ButtonProps = {
427+ type ButtonProps {
428428 ...BaseProps,
429429 onClick: fn() -> (),
430430 label: string,
431431}
432432// ButtonProps has: className, disabled, onClick, label
433433
434434// Multiple spreads
435- type A = { x: number }
436- type B = { y: string }
437- type C = { ...A, ...B, z: boolean }
435+ type A { x: number }
436+ type B { y: string }
437+ type C { ...A, ...B, z: boolean }
438438// C has: x, y, z
439439
440440// Simple union type (has |)
441- type Route =
441+ type Route {
442442 | Home
443- | Profile( id: string)
444- | Settings( tab: string)
443+ | Profile { id: string }
444+ | Settings { tab: string }
445445 | NotFound
446+ }
446447
447448// String literal union (for npm interop)
448449type HttpMethod = "GET" | "POST" | "PUT" | "DELETE"
449450
450451// Union types can contain other union types — nest as deep as you want
451- type NetworkError =
452- | Timeout(ms: number)
453- | DnsFailure(host: string)
454- | ConnectionRefused(port: number)
452+ type NetworkError {
453+ | Timeout { ms: number }
454+ | DnsFailure { host: string }
455+ | ConnectionRefused { port: number }
456+ }
455457
456- type ValidationError =
457- | Required(field: string)
458- | InvalidFormat(field: string, expected: string)
459- | TooLong(field: string, max: number)
458+ type ValidationError {
459+ | Required { field: string }
460+ | InvalidFormat { field: string, expected: string }
461+ | TooLong { field: string, max: number }
462+ }
460463
461- type AuthError =
464+ type AuthError {
462465 | InvalidCredentials
463- | TokenExpired(expiredAt: Date)
464- | InsufficientRole(required: Role, actual: Role)
466+ | TokenExpired { expiredAt: Date }
467+ | InsufficientRole { required: Role, actual: Role }
468+ }
465469
466470// Parent union containing sub-unions
467- type ApiError =
468- | Network( NetworkError)
469- | Validation( ValidationError)
470- | Auth( AuthError)
471+ type ApiError {
472+ | Network { NetworkError }
473+ | Validation { ValidationError }
474+ | Auth { AuthError }
471475 | NotFound
472- | ServerError(status: number, body: string)
476+ | ServerError { status: number, body: string }
477+ }
473478
474479// Go deeper — a full app error hierarchy
475- type HttpError =
476- | Network(NetworkError)
477- | Status(code: number, body: string)
478- | Decode(JsonError)
479-
480- type UserError =
481- | Http(HttpError)
482- | NotFound(id: UserId)
483- | Banned(reason: string)
484-
485- type PaymentError =
486- | Http(HttpError)
487- | InsufficientFunds(needed: number, available: number)
488- | CardDeclined(reason: string)
489-
490- type AppError =
491- | User(UserError)
492- | Payment(PaymentError)
493- | Auth(AuthError)
480+ type HttpError {
481+ | Network { NetworkError }
482+ | Status { code: number, body: string }
483+ | Decode { JsonError }
484+ }
485+
486+ type UserError {
487+ | Http { HttpError }
488+ | NotFound { id: UserId }
489+ | Banned { reason: string }
490+ }
491+
492+ type PaymentError {
493+ | Http { HttpError }
494+ | InsufficientFunds { needed: number, available: number }
495+ | CardDeclined { reason: string }
496+ }
497+
498+ type AppError {
499+ | User { UserError }
500+ | Payment { PaymentError }
501+ | Auth { AuthError }
502+ }
494503```
495504
496505### Multi-Depth Matching
@@ -625,7 +634,7 @@ Records and functions use the same call syntax: `Name(args)` with optional label
625634``` floe
626635// --- Record Construction ---
627636
628- type User = {
637+ type User {
629638 id: UserId
630639 name: string
631640 email: Email
@@ -669,7 +678,7 @@ createUser("Ryan", role: Admin, email: Email("r@test.com"))
669678// --- Default Values ---
670679
671680// On record types
672- type Config = {
681+ type Config {
673682 baseUrl: string // required — no default
674683 timeout: number = 5000 // default value
675684 retries: number = 3 // default value
@@ -697,7 +706,7 @@ fetchUsers(page: 3) // override one
697706fetchUsers(limit: 50, sort: Descending) // override two
698707
699708// On React component props
700- type ButtonProps = {
709+ type ButtonProps {
701710 label: string // required
702711 onClick: fn() -> () // required
703712 variant: Variant = Primary // default
@@ -732,7 +741,7 @@ Two syntactic forms are supported:
732741** Block form** — group multiple functions:
733742
734743``` floe
735- type User = { name: string, age: number, active: bool }
744+ type User { name: string, age: number, active: bool }
736745
737746for User {
738747 fn display(self) -> string {
@@ -851,7 +860,7 @@ Trait rules:
851860Record types can auto-derive trait implementations with ` deriving ` :
852861
853862``` floe
854- type User = {
863+ type User {
855864 id: string,
856865 name: string,
857866 email: string,
@@ -956,13 +965,17 @@ fn double(x: number) -> number { x * 2 } // correct
956965``` floe
957966import { useState } from "react"
958967
959- type Todo = {
968+ type Todo {
960969 id: string
961970 text: string
962971 done: boolean
963972}
964973
965- type Tab = Overview | Team | Analytics
974+ type Tab {
975+ | Overview
976+ | Team
977+ | Analytics
978+ }
966979
967980export fn Dashboard(userId: UserId) -> JSX.Element {
968981 const [tab, setTab] = useState<Tab>(Overview)
@@ -1611,7 +1624,7 @@ fn deleteUser(id: UserId) -> Result<(), ApiError> {
16111624}
16121625
16131626// Callbacks
1614- type ButtonProps = {
1627+ type ButtonProps {
16151628 onClick: fn() -> ()
16161629}
16171630```
@@ -1717,6 +1730,7 @@ const c = { ...a, ...b } // WARNING: 'y' from 'a' is overwritten by 'b'
17171730| Spread overlap | Warning on statically-known key overlap | Catches silent overwrites at compile time |
17181731| Compiler language | Rust | Fast, WASM-ready for browser playground, good LSP story |
17191732| Inline tests | ` test "name" { assert expr } ` co-located with code | Gleam/Rust-inspired; type-checked always, stripped from production output |
1733+ | Type definitions | ` type Foo { fields } ` for records, ` type Foo { \| A \| B } ` for unions | Unified syntax: all nominal types use ` type Name { ... } ` . ` = ` only for aliases and string literal unions |
17201734| For blocks | ` for Type { fn f(self) ... } ` groups functions under a type | Rust/Swift-like method chaining DX without OOP. ` self ` is explicit, no ` this ` magic |
17211735
17221736---
0 commit comments