template-kit/typecheck
The template-kit/typecheck authoring rule — what it enforces, why, and how to fix it.
The template must compile. Every type error is one violation.
The template’s TypeScript must compile cleanly. The check resolves typescript from your
workspace’s node_modules — the compiler that runs is the one you installed, at the
version you pinned, against your own tsconfig.json.
If typescript is not installed, that is itself the diagnostic (with the fix), not a crash.
Reason
Section titled “Reason”The schema system is the section contract, and it is enforced through the type system.
SectionProps<typeof schema> is not a convenience — it is the mechanism that keeps
schema.ts and Renderer.tsx in agreement: a slot you add to the schema and forget to
render, a slot you render under the wrong name, an optional slot you treat as always
present, a slot whose type you assume wrong. Each of those is a compile error rather than a
surprise on a stranger’s published page.
A template that does not typecheck has opted out of the only thing checking that the two files still describe the same section.
Fix the type error. If the compiler itself is missing, install it and point your
tsconfig.json at the kit’s base config, which sets the module resolution and JSX settings
the kit’s types expect:
{ "extends": "@homepages/template-kit/tsconfig", "include": ["sections", "theme.ts"] }npm install --save-dev typescriptBefore
Section titled “Before”year_built is declared required: false, so SectionProps resolves it to
string | null — and the Renderer treats it as a string:
import { Section, Slot } from "@homepages/template-kit";
import type { Props } from "./schema.js";
export function Renderer({ slots }: Props) { return ( <Section> {/* error TS18047: 'slots.year_built' is possibly 'null'. */} <Slot id="year_built" textLeaf> <p>Built {slots.year_built.slice(0, 4)}</p> </Slot> </Section> );}That is not a pedantic complaint. required: false means a thin listing really does arrive
with no year, and this line really does throw on it.
import { Section, Slot } from "@homepages/template-kit";
import type { Props } from "./schema.js";
export function Renderer({ slots }: Props) { return ( <Section> {slots.year_built ? ( <Slot id="year_built" textLeaf> <p>Built {slots.year_built.slice(0, 4)}</p> </Slot> ) : null} </Section> );}If the slot should never be absent, the other correct fix is in the schema — declare it
required: true, and the type narrows to string everywhere.
See also
Section titled “See also”- The schema system —
how
SectionPropsresolves each slot to its runtime type. props-from-schema— the rule that stops a hand-writtenPropsfrom disabling this inference in the first place.render-invariant— the same class of bug caught at render time, for the cases the types cannot see.