Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

12. A TypeScript widget

widgets/typed_counter/ is the same counter as notebook 1, but the JS module is written in TypeScript and bundled with esbuild — meant as the “copy this when starting a new widget” template for this repo.

The widget surface is identical: value, label, widget_id, three buttons. What’s different sits on disk:

  • src/index.ts — render function with a CounterModel interface and RenderProps<CounterModel> from @anywidget/types, so model.get("value") is typed as number.

  • package.jsonnpm run build invokes esbuild, which strips types and emits dist/widget.js. npm run typecheck runs tsc --noEmit for type-only checks.

  • tsconfig.json — strict, ES2020, moduleResolution: bundler.

  • widget.py — same anywidget.AnyWidget subclass pattern, with _esm pointing at the built bundle in dist/.

Nothing in the static-export plugin or the runtime knows or cares that the source was TypeScript — by the time the widget reaches the browser it’s plain ESM JavaScript.

import sys
import pathlib
sys.path.insert(0, str(pathlib.Path().absolute().parent))

from widgets.typed_counter import TypedCounterWidget

Construct one and display it:

TypedCounterWidget(label="Typed counter", widget_id="ts_counter_demo", value=3)

Two on the same page work the same way as the JS counter — independent state, no shared globals:

TypedCounterWidget(label="Another one", widget_id="ts_counter_demo_2", value=10)