Augmenting & Extracting Types
Last stop: the rest of the Marko namespace — helpers that extract
types from what you already have, and augmentations that add to
what Marko knows.
Extractors. badge-row accepts an array of props for
<stat-badge> — and it re-declared that shape by hand. Hand copies
drift, and this one did: it says value: string, the real tag wants
a number, and the page passes numbers. Run pnpm check — TS2322,
the drift caught. The fix isn’t correcting the copy, it’s deleting
it:
import StatBadge from "<stat-badge>";
export interface Input { stats: Marko.Input<typeof StatBadge>[]; chrome: Marko.Renderable;}- Make that change in
badge-row/index.marko—pnpm checkgoes quiet. One contract, one source of truth; ifstat-badge’sInputever changes, every consumer follows automatically.
Marko.Input<...> works on custom tags (pass the imported template)
and native tags (Marko.Input<"button">). Its siblings:
Marko.Return<...> extracts a tag’s return — note it’s the
wrapped shape, so Marko.Return<typeof StatBadge>["value"] is the
raw type your tag variable holds (the page’s satisfies line proves
it) — and Marko.BodyParameters<B> / Marko.BodyReturnType<B> pull
the pieces back out of a Marko.Body.
Marko.Renderable is the type of anything a dynamic tag
accepts — string, template, or body — which is why chrome takes
plain text here and could take a component tomorrow. It’s the type
behind part 4’s dynamic components.
Augmenting Marko.Global. The page reads $global.buildLabel.
With marko-run’s generated route types in play, $global is
permissive about extra reads — but the proper way to declare an
app-wide global is a one-time interface merge, which also powers
editor completion and stricter setups:
- In
src/types.d.ts, inside thenamespace Markoblock, addinterface Global { buildLabel?: string; }— check stays quiet; the property is now first-class. (In a real app the server sets the value — part 5’s globals lesson is where it would come from.)
Three more declare global recipes, for reference — same technique:
interface NativeTags { "my-custom-element": MyAttributes; } // new native taginterface HTMLAttributes { "data-track"?: string; } // attr on ALL tagsnamespace CSS { interface Properties { "--brand"?: string } } // CSS custom propsAnd for completeness, the template-level types — Marko.Template,
Marko.TemplateInput, Marko.RenderedTemplate,
Marko.MountedTemplate — type a template you render or mount
programmatically; they matter when embedding Marko in other apps,
the advanced territory part 5’s capstone points at.
You should see the badge row rendering, “Summary returned: Anvils: 7”, “Global build: dev”, and a silent check. That’s the TypeScript chapter — next chapter puts more tools on your belt: formatting, testing, and a component workshop.
- Installing dependencies
- Starting dev server