en un clic
typescript-write
Write TypeScript and JavaScript code following Metabase coding standards and best practices. Use when developing or refactoring TypeScript/JavaScript code.
Menu
Write TypeScript and JavaScript code following Metabase coding standards and best practices. Use when developing or refactoring TypeScript/JavaScript code.
Review TypeScript and JavaScript code changes for compliance with Metabase coding standards, style violations, and code quality issues. Use when reviewing pull requests or diffs containing TypeScript/JavaScript code.
Guide Clojure and ClojureScript development using REPL-driven workflow, coding conventions, and best practices. Use when writing, developing, or refactoring Clojure/ClojureScript code.
Export content from a running Metabase instance, validate with checkers, edit YAML, and import back. Use when the user wants to export, import, or run the full serdes round-trip workflow.
Drive Metabase's UI with the Playwright MCP browser tools (mcp__playwright__browser_*). Covers the snapshot/act/check pattern, Mantine component pitfalls (Menu race, Select/MultiSelect, the Escape-closes-modal trap, portal scoping), and Metabase-specific login flows. Use whenever a session needs to interact with the Metabase UI through Playwright MCP.
Review Cypress E2E spec files for Metabase conventions, common gotchas, and flakiness/performance issues. Use when reviewing pull requests or diffs containing Cypress spec files in e2e/test/scenarios/.
Run Cypress E2E tests, analyze failures including screenshots, and stress test for flakiness
| name | typescript-write |
| description | Write TypeScript and JavaScript code following Metabase coding standards and best practices. Use when developing or refactoring TypeScript/JavaScript code. |
@./../_shared/development-workflow.md @./../_shared/typescript-commands.md @./../_shared/react-redux-patterns.md
any — hard ruleany, explicit or implicit. No any annotations, no as any / as unknown as, no untyped parameters or returns that infer any, no implicitly-any destructures or array/object literals.unknown + type guard, or a small typed wrapper) — never let any propagate inward.goToDefinition to confirm sources) and run the project type-check.unknown — fix the signature instead.Partial<T>, Pick<T, K>, Record<K, V>, and generics before reaching for a cast.<T>) when a value flows through unchanged and the caller knows the type.unknown over loose typing and narrow before use — an unknown value forces a guard at the point of use.satisfies for object literals that must conform without widening (config objects, lookup maps, discriminated literals) — better than : T (widens) or as T (unsafe).!). Prefer a guard, early return, or ?.. Use ! only when non-nullness is provably true and localized, with a comment.Number() / String() / Boolean().frontend/src/metabase-types/guards/. Do not redefine them locally.metabase-types/api (FieldId, TableId, ConcreteTableId, SchemaName, …) and key data structures by them (new Map<ConcreteTableId, …>()). Don't duplicate generated/API types — compose or derive (Pick, Omit, indexed access SomeType["field"], ReturnType).field?: T for a key that may be absent, field: T | undefined only when the key is always present but the value may be undefined, | null for explicit API nulls. Prefer domain unions over broad string / number / loose Record..exhaustive() so adding a variant becomes a compile error:
import { match } from "ts-pattern";
const result = match(status)
.with({ type: "loading" }, () => <Spinner />)
.with({ type: "error", error: P.select() }, (error) => <Error message={error.message} />)
.with({ type: "success", data: P.select() }, (data) => <Content data={data} />)
.exhaustive(); // Compile-time guarantee all cases handled
as const + typeof/keyof) so the type and the values can't drift.readonly / immutability where mutation isn't intended — component props, shared constants, exported config. Prefer readonly T[] / ReadonlyArray<T> for inputs you don't mutate.undefined through every layer — guard at the producer.?. and ?? at the consumer.X != null) when X can never be null — use a strict check or narrow the type. Use checkNotNull where necessary.Base, New, Old, Initial need a real semantic distinction, otherwise drop them.v, n, $n) for domain values; short names are fine only in tiny conventional contexts (loop index i, coordinates x/y, generic params T/K/V).what.why is non-obvious: a workaround, a hidden invariant, a subtle ordering constraint, a clever reduction. Never document the actual implementation, focus on the intent and the why.When touching existing JavaScript files, propose to convert them to TypeScript first. Create a separate PR for the conversion, then implement the changes.