Developer TypeScript
Use when the user needs TypeScript help: array formatting, typing, narrowing, generics, strict mode, declaration files, or moving from JavaScript. Plain language; explain terms the first time. document-voice.
Inputs
- Context – TypeScript or JS migration; types, generics, strict mode, or declaration files. Optional: project path.
- Source – User question or file; apply the rules below.
Output
Guidance applied (types, patterns, fixes). No new repo unless requested.
Process
1. Format arrays vertically
Rule: All arrays vertical for consistency and readability. Opening bracket on same line as assignment/property, items on separate lines, closing bracket on own line.
// ✅ All arrays: vertical format (one item per line)
const clean = [
"verify-task",
"clean",
];
const save = [
"verify-paths",
"document-paths",
"save",
];
const learn = [
"verify-task",
"research",
"verify-task",
"document",
];
const discover = [
"verify-task",
"research",
"verify-task",
"document",
"verify-task",
"analyst-diagnostics",
"verify-task",
"document",
"verify-task",
"research",
"verify-task",
"document",
"verify-task",
"analyst-diagnostics",
"verify-task",
"document",
"verify-task",
"document-ticket",
];
Why:
- Short arrays are faster to read horizontally
- Long arrays are easier to scan vertically
- Use common sense: if it fits comfortably, keep it on one line
Guidelines:
- 2-3 items: usually horizontal
- 4 items: horizontal if they're short strings
- 5+ items or long strings: vertical
2. Stop using any
Use unknown when the type is unknown; narrow (e.g. check the shape) before use. any turns off checking. Type API results or use unknown; start with unknown so you do not get stuck with any.
3. Narrowing
TypeScript narrows to a more specific type after a check.
filter(Boolean)does not narrow; use a type guard:.filter((x): x is T => Boolean(x)).Object.keys(obj)isstring[], not the key union; objects can have extra keys at runtime.Array.isArray()narrows to array but element type may beany; add assertion if needed.innarrows only when the property appears in exactly one branch of the union.
4. Literal types
let x = "hello"has typestring; useconstoras constfor literal"hello".- Object properties widen:
{ status: "ok" }givesstatus: string; useas constor a type to keep the literal. - Generic
<T extends string>with a literal may inferstring; useas constor explicit type if needed.
5. Inference
TypeScript infers when it can. Inference is often lost in callbacks (e.g. array methods); add a parameter type when wrong. Generic fn<T>() cannot infer T without a value or explicit type. Nested generics often fail; use an intermediate type.
6. Discriminated unions
Use a literal field (type or kind) on each variant so TypeScript can discriminate. Exhaustiveness: default: const _never: never = x so a missing case errors. Do not mix optional properties into the same union or narrowing breaks.
7. satisfies vs type annotation
const x: Type = valgivesxtypeTypeand can drop literal details.const x = val satisfies Typekeeps the literal and checks it fitsType. Prefer for config objects.
8. Strict null
?.givesundefinedwhen missing, notnull; matters for APIs that usenull.??replaces onlynullandundefined;||replaces any falsy (including0and"").- Use
!only as a last resort; prefer narrowing or early return.
9. Module boundaries
import typefor types only; removed at build time.- Re-export with
export type { X }so you do not pull in runtime code. .d.ts:declare module "x"must match the import string exactly. No import/export = global script; addexport {}for a module.declare global { }for globals inside a module.interfacemerges from other files;typedoes not.pathsin tsconfig needbaseUrl; path mapping is compiler-only; bundler may need its own config. Prefer named exports in.d.ts.
10. Generics
useState<User>()isUser | undefineduntil set; handle undefined.Promise.all([a(), b()])keeps tuple type only withas const.<T = any>leaksany; avoid.<T extends object>allows arrays; useRecord<string, unknown>for object-only.keyof Tisstring | number | symbol. Arrays are covariant (invalid push can type-check); function params are contravariant. Mapped type{ [K in keyof T]: X }can lose optional or readonly; use-?or-readonlyto change.
11. Utility types
Partial<T>andRequired<T>only affect top level; nested unchanged.Required<T>does not removeundefinedfrom a union.Omit<T, K>andPick<T, K>do not check keys exist; typos still compile.Record<string, T>: missing keys still type as T, notT | undefined.Extract<T, U>isneverwhen nothing matches.ReturnType/Parameterswith overloads use only the last signature.NonNullable<T>removesnullandundefined.Awaited<T>unwraps promises (nested too).
12. Migration from JavaScript
- Turning off
noImplicitAnyhides errors; untyped callbacks becomeany.strictNullChecksorstrictPropertyInitializationbreak code; add inits or!where needed. as Typeis not runtime check;as unknown as Typebypasses the type system; avoid when you can.JSON.parsereturnsany; assert or validate.@types/can be out of date.skipLibCheck: truehides.d.tserrors. Prefer@ts-expect-errorover@ts-ignoreso it fails when the error is fixed.outDirdoes not delete old files; leftover .js can confuse.
Reference
document-voice. developer-electron for Electron/desktop apps.
Source: ryanallen/product-studio — distributed by TomeVault.