Derived Values
You no longer need a separate transformer registry to build reusable column logic. In practice, a small accessor helper or transform helper is enough and tends to stay easier to read.
Reusable accessor helpers
import { createExcelSchema } from "@chronicstone/typed-xlsx";
type User = {
firstName: string;
lastName: string;
roles: string[];
};
const fullName = (user: User) => `${user.firstName} ${user.lastName}`;
const roleList = (user: User) => user.roles.join(", ");
const schema = createExcelSchema<User>()
.column("fullName", {
header: "Full name",
accessor: fullName,
})
.column("roles", {
header: "Roles",
accessor: roleList,
})
.build();
Transform after access
Use transform when the accessor should stay focused on extraction and the display logic should stay explicit.
import { createExcelSchema } from "@chronicstone/typed-xlsx";
const schema = createExcelSchema<{ cents: number }>()
.column("amount", {
accessor: "cents",
transform: (value) => Number(value) / 100,
style: {
numFmt: "$#,##0.00",
},
})
.build();
Transform value is typed from the accessor
The value parameter in transform is inferred directly from what the accessor returns. No manual annotation is needed.
When the accessor is a valid dot-path string, value gets the field's type:
import { createExcelSchema } from "@chronicstone/typed-xlsx";
createExcelSchema<{ price: number; label: string }>()
.column("price", {
accessor: "price",
transform: (value) => `$${value.toFixed(2)}`,
})
.build();
When the accessor is a callback, value matches whatever the callback returns:
import { createExcelSchema } from "@chronicstone/typed-xlsx";
type Product = { price: number; qty: number; discount: number };
createExcelSchema<Product>()
.column("lineTotal", {
accessor: (row) => row.price * row.qty * (1 - row.discount),
transform: (value) => value.toFixed(2),
})
.build();
When the accessor string does not match any field on the row type, TypeScript raises an error directly on the accessor property — not somewhere downstream in transform. The error message includes the full list of valid paths:
import { createExcelSchema } from "@chronicstone/typed-xlsx";
createExcelSchema<{ price: number }>()
.column("amount", {
accessor: "nonExistent", // Error: not assignable to "price"
})
.build();
This catches the typo immediately, at the source, rather than giving a silent undefined at runtime.