From metabase-typescript-write
Writes TypeScript/JavaScript code following Metabase coding standards, with strict rules against `any`, type narrowing, and type modeling best practices.
How this skill is triggered — by the user, by Claude, or both
Slash command
/metabase-typescript-write:typescript-writeThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
@./../_shared/development-workflow.md
@./../_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.
npx claudepluginhub p/metabase-metabase-typescript-write-claude-skills-typescript-writeProvides idiomatic TypeScript/JavaScript conventions: immutability, destructuring, arrow functions, and options objects. Useful when writing or reviewing TS/JS code.
Enforces TypeScript type safety with strict mode, no `any`, and discriminated unions. Use when writing or reviewing TypeScript code.
Enforces TypeScript strict mode, ESLint rules, type safety, React patterns, naming conventions, and function length guidelines. Useful for writing or reviewing TypeScript/JavaScript frontend code.