Columns
Each column is identified by a stable id and configured with an accessor.
Typed paths
import { createExcelSchema } from "@chronicstone/typed-xlsx";
const schema = createExcelSchema<{ user: { email: string; name: string } }>()
.column("name", {
header: "Name",
accessor: "user.name",
})
.column("email", {
header: "Email",
accessor: "user.email",
})
.build();
Available path types
The Path<T> utility lists every valid dot-path string for your row type. Hover over UserPaths to see the full union at compile time:
import type { Path } from "@chronicstone/typed-xlsx";
type User = {
name: string;
email: string;
address: { city: string; zip: string };
};
type UserPaths = Path<User>;
Autocomplete on accessor strings
Because accessor is typed as Path<T>, your editor offers completions the moment you open the string. Nested paths like "address." expand into their sub-fields. This works out of the box in VS Code and any LSP-capable editor — no plugin required.
When a path string does not belong to Path<T>, TypeScript rejects it directly at the accessor property — not somewhere downstream. The error message names the valid paths:
import { createExcelSchema } from "@chronicstone/typed-xlsx";
type User = { name: string };
createExcelSchema<User>()
.column("name", {
accessor: "typo", // Error: 'typo' is not assignable to 'name'
})
.build();
Accessor callbacks
import { createExcelSchema } from "@chronicstone/typed-xlsx";
const schema = createExcelSchema<{ firstName: string; lastName: string }>()
.column("fullName", {
header: "Full name",
accessor: (row) => `${row.firstName} ${row.lastName}`,
})
.build();
Accessor type inference
The row parameter is typed to your row type. The value the accessor returns automatically types the transform parameter downstream — no manual annotation needed:
import { createExcelSchema } from "@chronicstone/typed-xlsx";
createExcelSchema<{ price: number; qty: number }>()
.column("total", {
accessor: (row) => row.price * row.qty,
transform: (value) => `$${value.toFixed(2)}`,
})
.build();
The same inference applies when the accessor is a dot-path string — a valid path gives the transform a precise type, an invalid one gives unknown:
import { createExcelSchema } from "@chronicstone/typed-xlsx";
createExcelSchema<{ cents: number }>()
.column("amount", {
accessor: "cents",
transform: (value) => value / 100,
})
.build();
Multi-value cells
If an accessor or transform returns an array, typed-xlsx expands the logical row into multiple physical rows and merges single-value columns automatically.
import { createExcelSchema } from "@chronicstone/typed-xlsx";
const schema = createExcelSchema<{ tags: string[]; title: string }>()
.column("title", {
accessor: "title",
})
.column("tags", {
accessor: "tags",
})
.build();
Styling and formatting
Static styles are applied to every cell in the column. Pass a callback to compute the style from the row — the row parameter is fully typed:
import { createExcelSchema, type CellStyle } from "@chronicstone/typed-xlsx";
const moneyStyle: CellStyle = {
numFmt: "$#,##0.00",
alignment: { horizontal: "right" },
};
const schema = createExcelSchema<{ amount: number; status: "paid" | "pending" }>()
.column("amount", {
accessor: "amount",
style: moneyStyle,
})
.column("status", {
accessor: "status",
style: (row) => ({
font: {
bold: true,
color: { rgb: row.status === "paid" ? "166534" : "B42318" },
},
}),
})
.build();