Typed-xlsx
Schema Builder

Derived Values

Building reusable accessor and transform helpers for columns whose values are computed from the row.

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"
No overload matches this call. Overload 1 of 2, '(id: "amount", definition: Omit<ColumnDefinition<{ price: number; }, "price">, "id" | "accessor"> & { accessor: "price"; }): SchemaBuilder<{ price: number; }, "amount", never, {}>', gave the following error. Type '"nonExistent"' is not assignable to type '"price"'. Overload 2 of 2, '(id: "amount", definition: Omit<ColumnDefinition<{ price: number; }, (row: { price: number; }) => unknown>, "id">): SchemaBuilder<{ price: number; }, "amount", never, {}>', gave the following error. Type 'string' is not assignable to type '(row: { price: number; }) => unknown'.
}) .
build
();

This catches the typo immediately, at the source, rather than giving a silent undefined at runtime.

Copyright © 2026 Cyprien Thao. Released under the MIT License.