agentleFS
DanzteeDanztee/javascript-typescript-agent-skills/skills/typescript-engineering/SKILL.md

typescript-engineering

Skill1 starsChanged 2 months agoMITView on GitHub

Use when building, changing, debugging, or reviewing TypeScript, especially when type design, runtime boundaries, async behavior, module resolution, declarations, or compiler/tooling choices matter.

3 other projects use a version of this.

Adopted by

Most recently taken up by

  • Other versions3 projects use a version of this.

    Word for word · 0

    None found.

    Edited · 0

    None found.

    Wrote their own version · 3

    1. bluenzzz/typescript-engineering★ 9same name
    2. ldilov/harness-forge★ 4same name
    3. nafeeur/MaskShift★ 2same name

    How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.

  • In this repositoryOne other file it gives its agents.

The file

Show its rules and text

We split this skill into the rules it gives an agent

39 rules. 0 are common, 24 are distinctive, and 15 are very specific to this project.

  • 0

    Common

    Most of the 3 edited versions we read keep these.

  • 24

    Distinctive

    This author's own choices. Worth a look.

  • 15

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Use this skill for TypeScript implementation, refactoring, debugging, type design, and code review.

    line 9
  2. Distinctive:

    Keep compile-time contracts and runtime validation separate, and implement the smallest complete change.

    line 15
  3. Distinctive:

    Verify both type-level and runtime behavior when the change can affect either.

    line 16
  4. Distinctive:

    Before finishing, review the change against the red flags below and report the checks that ran.

    line 17
  5. Very specific:

    Keep the patch scoped. Do not rewrite unrelated code, upgrade dependencies, change tsconfig, or add abstractions unless the requested …

    line 22
  6. Distinctive:

    Never invent an API, package export, compiler option, or framework pattern from memory.

    line 23
  7. Distinctive:

    Do not declare success from a clean typecheck alone when the change affects emitted JavaScript, module loading, serialization, …

    line 25
  8. Very specific:

    Do not hide a failing check with any, as, @ts-ignore, a lint disable, a broad catch, skipLibCheck, or an unrelated config change.

    line 26
  9. Very specific:

    Read package.json, tsconfig files, source conventions, and existing test/lint/format/build scripts before making design decisions.

    line 30
  10. Very specific:

    Before editing, look for applicable AGENTS.md, CONTRIBUTING.md, STYLEGUIDE.md, README.md, .editorconfig, formatter/linter configs, package …

    line 36
  11. Distinctive:

    Follow explicit scoped project instructions and configured tools, then the nearest maintained implementation.

    line 38
  12. Distinctive:

    Use the repository's formatter and lint commands for changed files.

    line 39
  13. Distinctive:

    If the repository is silent, use the defaults below and use them consistently throughout the change.

    line 47
  14. Distinctive:

    Do not perform an unrelated type-style migration just to normalize older code.

    line 48
  15. Distinctive:

    When multiple implementations are equally sound, select one rather than making agents or users choose between equivalent patterns.

    line 50
  16. Distinctive:

    When the repository is silent, prefer:

    line 52
  17. Distinctive:

    Do not concatenate untrusted values into SQL, shell commands, HTML, templates, filesystem paths, dynamic code, or redirects.

    line 81
  18. Very specific:

    Treat unknown JSON, deserialized data, request parameters, uploaded files, environment values, and third-party responses as untrusted until …

    line 82
  19. Distinctive:

    Keep secrets, tokens, passwords, and unnecessary sensitive data out of logs, thrown errors, and client responses.

    line 85
  20. Distinctive:

    Prefer inference for obvious local values.

    line 91
  21. Very specific:

    Use unknown for values whose type is not known, then narrow them.

    line 93
  22. Very specific:

    Use satisfies when a value must conform to a type while retaining its useful inferred literal type.

    line 94
  23. Very specific:

    Avoid broad assertions (as SomeType), non-null assertions (!), Function, boxed primitives, and empty-object types as shortcuts.

    line 95
  24. Distinctive:

    Prefer readonly types or immutable data only where they communicate an actual ownership/invariant.

    line 96
  25. Very specific:

    Treat unknown as a prompt to narrow at the boundary, not as a reason to sprinkle typeof checks throughout trusted business logic.

    line 105
  26. Distinctive:

    Keep security controls regardless of type safety: authorization, input constraints, injection prevention, output encoding, secret handling, …

    line 107
  27. Very specific:

    Consider strict, useUnknownInCatchVariables, noUncheckedIndexedAccess, and exactOptionalPropertyTypes based on the codebase's actual data …

    line 112
  28. Very specific:

    If typed linting is already configured, use its diagnostics to catch issues such as floating promises, unsafe any flow, and unnecessary …

    line 114
  29. Distinctive:

    Use Prettier or the repository's formatter for formatting.

    line 115
  30. Very specific:

    Do not leave promises unhandled.

    line 120
  31. Very specific:

    Keep runtime API contracts aligned with the types.

    line 122
  32. Very specific:

    When an operation owns timers, listeners, subscriptions, streams, sockets, or clients, define its cleanup and cancellation path.

    line 124
  33. Distinctive:

    Test behavior and contracts, not TypeScript syntax or implementation trivia.

    line 128
  34. Distinctive:

    Keep type-level tests only for public type APIs or inference guarantees that users rely on.

    line 129
  35. Very specific:

    After changes, run the relevant tests plus the repository's typecheck/build/lint commands when available.

    line 130
  36. Distinctive:

    Follow the project's existing logger, structured fields, tracing, metrics, severity, and correlation/request-ID conventions.

    line 134
  37. Distinctive:

    Add instrumentation when it supports debugging, operations, security, or a stated product requirement—not as boilerplate on every function.

    line 137
  38. Distinctive:

    Do not recommend a type-level abstraction, compiler flag, or validation layer merely because it exists.

    line 153
  39. Distinctive:

    Read references/decision-rules.md when you need concrete before/after examples for the highest-impact rules.

    line 157

“Common” is measured: kept by at least 6 in 10 of the 3 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

TypeScript engineering

Use this skill for TypeScript implementation, refactoring, debugging, type design, and code review. Treat the type system as a tool for making valid states easy to represent—not as a reason to add ceremony or encode every possibility in types.

Operating procedure

  1. Inspect the repository, compiler/build configuration, and the nearest code with the same API shape.
  2. Choose the existing convention; if none exists, apply the defaults in this skill consistently.
  3. Keep compile-time contracts and runtime validation separate, and implement the smallest complete change.
  4. Verify both type-level and runtime behavior when the change can affect either; do not treat a clean typecheck as runtime proof.
  5. Before finishing, review the change against the red flags below and report the checks that ran.

Agent reliability contract

  • Inspect before proposing: read the relevant files, package.json, compiler/build configuration, installed versions, and nearby tests before choosing an API or type pattern.
  • Keep the patch scoped. Do not rewrite unrelated code, upgrade dependencies, change tsconfig, or add abstractions unless the requested behavior requires it.
  • Never invent an API, package export, compiler option, or framework pattern from memory. Verify it against installed declarations/source, repository configuration, or current official documentation.
  • Tests must assert behavior and meaningful failure cases. Do not add tests that only assert a mock was called, return a constant, or reproduce the implementation's branches without exercising the contract.
  • Do not declare success from a clean typecheck alone when the change affects emitted JavaScript, module loading, serialization, declarations, decorators, JSX, or runtime loaders. Run the narrowest real check available.
  • Do not hide a failing check with any, as, @ts-ignore, a lint disable, a broad catch, skipLibCheck, or an unrelated config change. Preserve the failure until its cause is understood or report the concrete blocker.

First inspect the project

  • Read package.json, tsconfig files, source conventions, and existing test/lint/format/build scripts before making design decisions.
  • Determine whether the project emits JavaScript, uses a bundler/runtime transpiler, runs types directly, or uses TypeScript only for checking. Respect path aliases, module resolution, ESM/CommonJS settings, declaration output, and package exports.
  • Match the project's TypeScript and typescript-eslint versions. Do not change compiler options or migrate tooling unless the request calls for it.

Project conventions and engineering style

  • Before editing, look for applicable AGENTS.md, CONTRIBUTING.md, STYLEGUIDE.md, README.md, .editorconfig, formatter/linter configs, package scripts, templates, examples, and nearby tests. Check parent and nested directories when the repository is a monorepo.
  • Extract the conventions that affect the change: naming, file placement, imports/exports, module boundaries, type shape, async and error handling, state ownership, logging, tests/type tests/mocks, comments, documentation, and public API shape.
  • Follow explicit scoped project instructions and configured tools, then the nearest maintained implementation. Treat a one-off inconsistency as weaker evidence than repeated patterns; if sources genuinely conflict, preserve the narrowest existing contract and report the ambiguity.
  • Use the repository's formatter and lint commands for changed files. Do not reformat, rename, or migrate unrelated code merely to make the repository look uniform.

Consistency protocol

TypeScript offers several ways to encode the same idea. Reduce variation with this order of precedence:

  1. Match the nearest working code and public API conventions.
  2. Match the repository's compiler, lint, runtime, and build configuration.
  3. If the repository is silent, use the defaults below and use them consistently throughout the change.
  4. Do not perform an unrelated type-style migration just to normalize older code.

When multiple implementations are equally sound, select one rather than making agents or users choose between equivalent patterns. Call out alternatives only when they change the runtime contract, emitted output, API compatibility, or meaningful maintenance cost.

When the repository is silent, prefer:

  • Inferred types for local variables; explicit types for exported functions, public data, callback contracts, and non-obvious return values.
  • type for unions, tuples, mapped/conditional types, and composition; interface for object contracts intended to be extended or implemented. Follow local convention when either works.
  • String-literal unions over enums unless runtime enum objects, reverse mapping, or an established project convention is actually needed.
  • unknown at untrusted boundaries, followed by one narrow/parse step; localized any only for a documented, unavoidable integration gap.
  • satisfies for config-like values that need checking while retaining their inferred literals; narrowing or a purpose-built type guard instead of broad as assertions.
  • Optional properties (field?: T) when omission has meaning; field: T | undefined when the property is required but its value may be absent.
  • A generic only when it represents a real relationship between multiple inputs/outputs or materially improves a reusable API.
  • import type for type-only imports when the project uses or requires it; do not rewrite imports without a compiler/tooling reason.

Ecosystem pain-point guardrails

These are common sources of wasted time and “almost correct” agent output:

  • Architecture and state: trace ownership and data flow before introducing a store, singleton, event bus, cache, or elaborate generic abstraction. Keep state close to its consumer when possible; do not use types to hide unclear runtime ownership.
  • Dependencies: search the existing dependency set and platform APIs first. Add a package only when it solves a meaningful problem the current stack cannot solve simply, and change manifests/lockfiles with the project's package manager.
  • Build versus runtime: separate typechecking, transpilation, bundling, and execution in your reasoning. Verify the actual emitted/imported code when module resolution, package exports, decorators, JSX, declarations, or runtime loaders are involved. Do not “fix” a runtime problem with any, skipLibCheck, or a path alias that only works in the editor.
  • Compiler performance: avoid giant anonymous inferred types, deeply recursive conditional types, and unnecessary type-aware linting across generated/config files. Add named boundaries or split types when a measured compile, declaration-emit, editor, or lint slowdown justifies it; do not annotate everything preemptively.
  • Configuration and upgrades: preserve reproducible lockfiles and existing tsconfig inheritance. Enable stricter compiler/lint profiles incrementally; do not enable every rule or flag during an unrelated feature change, especially when the resulting errors cannot be triaged in scope.
  • Dates and time: make timezone, locale, precision, and serialization explicit. Do not rely on ambiguous external date parsing or silently treat a local time as UTC. Follow the project's date library or supported platform API.
  • Debugging: reproduce the failure, form a narrow hypothesis, inspect source/config/generated output, and make the smallest fix that explains the behavior. Do not change several unrelated variables and call the first green run proof.
  • Package and API boundaries: inspect workspace scripts, package manager, exports, module type, declaration output, generated artifacts, and the actual consumer/runtime before changing a package boundary. Identify public exports and serialization contracts before making a potentially breaking change.
  • Agent drift: do not copy a remembered framework recipe over the installed version. Do not add eslint-disable, @ts-ignore, broad assertions, or a new package merely to silence a diagnostic; explain and verify the underlying issue instead.

Security at runtime boundaries

TypeScript types do not enforce security after compilation. Keep controls where data crosses a trust boundary, using the framework and libraries already established by the project:

  • Do not concatenate untrusted values into SQL, shell commands, HTML, templates, filesystem paths, dynamic code, or redirects. Use parameterized/structured APIs, safe sinks, output encoding, sanitization, or allowlists appropriate to the context.
  • Treat unknown JSON, deserialized data, request parameters, uploaded files, environment values, and third-party responses as untrusted until parsing and the boundary contract are complete. Never replace that step with as, any, eval, or an unsafe deserialization shortcut.
  • For user-controlled outbound URLs, enforce the application's intended schemes, hosts, ports, redirect behavior, and network policy where SSRF or open-redirect risk is real.
  • Preserve authentication, authorization, CSRF, rate/resource limits, secret handling, and dependency security controls already required by the application. Do not replace them with generic checks or type assertions.
  • Keep secrets, tokens, passwords, and unnecessary sensitive data out of logs, thrown errors, and client responses. Follow the project's redaction and privacy conventions.

Apply the smallest control that addresses the actual threat. Do not add speculative sanitization or validation that changes valid data or duplicates a trusted boundary.

Type design

  • Prefer inference for obvious local values. Add explicit types where they document or constrain public APIs, exported data, callbacks, complex return values, and important boundaries.
  • Model meaningful alternatives with discriminated unions and exhaustiveness checks. Prefer a small precise type over a large hierarchy or a generic that exists only to look reusable.
  • Use unknown for values whose type is not known, then narrow them. Avoid any; if it is genuinely required for an untyped dependency or an unrepresentable edge, keep it local, explain the boundary, and avoid allowing it to spread.
  • Use satisfies when a value must conform to a type while retaining its useful inferred literal type. Use as const, utility types, and generics when they make the contract clearer—not as substitutes for understanding the data flow.
  • Avoid broad assertions (as SomeType), non-null assertions (!), Function, boxed primitives, and empty-object types as shortcuts. Replace them with a real invariant, a narrow guard, a better API, or a justified localized escape hatch.
  • Prefer readonly types or immutable data only where they communicate an actual ownership/invariant. Do not impose blanket immutability on code that intentionally mutates local state.

Runtime boundaries and validation

TypeScript types are erased at runtime. Separate compile-time contracts from runtime facts:

  • Parse, validate, and normalize values entering from HTTP, CLI, environment, files, JSON, databases, browser storage, webhooks, and third-party packages. Use the project's existing schema/parser library when one exists.
  • Perform boundary validation once and pass the resulting typed representation inward. Do not duplicate the same checks in every internal function.
  • For internal functions whose callers and types establish a non-null, correctly-shaped value, do not add speculative guards, fallback values, or branches for impossible states. If the invariant is wrong, fix the type or caller and let the failure remain visible.
  • Treat unknown as a prompt to narrow at the boundary, not as a reason to sprinkle typeof checks throughout trusted business logic.
  • Be precise about absence. undefined, null, an omitted optional property, an empty collection, and a falsy value are not interchangeable unless the domain says they are.
  • Keep security controls regardless of type safety: authorization, input constraints, injection prevention, output encoding, secret handling, and resource limits are runtime concerns.

Compiler and linting posture

  • For new projects, prefer a strict compiler posture when compatible with the runtime and migration plan. For existing projects, make incremental, local improvements instead of turning on every strictness flag opportunistically.
  • Consider strict, useUnknownInCatchVariables, noUncheckedIndexedAccess, and exactOptionalPropertyTypes based on the codebase's actual data model and tolerance for migration noise. Explain the tradeoff before changing them.
  • Let the TypeScript compiler own checks it can prove. Avoid duplicating compiler diagnostics as manual runtime conditions or redundant ESLint rules.
  • If typed linting is already configured, use its diagnostics to catch issues such as floating promises, unsafe any flow, and unnecessary conditions. Do not enable expensive type-aware linting blindly on every generated, config, or JavaScript file.
  • Use Prettier or the repository's formatter for formatting; use ESLint for correctness and maintainability rules rather than arguing about whitespace in review.

Async, errors, and APIs

  • Give exported async APIs accurate Promise<T> behavior through inference or explicit return types. Handle independent operations concurrently with the appropriate Promise combinator.
  • Do not leave promises unhandled. An intentional fire-and-forget call should have an explicit, repository-appropriate convention and a known rejection strategy; void alone documents intent but does not handle a rejection.
  • Catch unknown errors safely at the layer that can recover, translate, clean up, or report. Preserve causes and useful domain context; do not swallow errors or log the same failure at every layer.
  • Keep runtime API contracts aligned with the types. If a function can return undefined, make callers handle that; if it cannot, do not add a meaningless fallback just to silence a concern.
  • For collections whose size comes from users, files, queues, or external services, do not use unbounded Promise.all by reflex. Use the project's existing limiter, batching, or incremental processing when concurrency, memory, rate limits, or cancellation require it.
  • When an operation owns timers, listeners, subscriptions, streams, sockets, or clients, define its cleanup and cancellation path. Reuse an existing AbortSignal or lifecycle convention instead of inventing a one-off abstraction.

Testing and verification

  • Test behavior and contracts, not TypeScript syntax or implementation trivia. Include boundary parsing, discriminant branches, important failure behavior, and regression cases.
  • Keep type-level tests only for public type APIs or inference guarantees that users rely on; keep them small and use the project's established tool.
  • After changes, run the relevant tests plus the repository's typecheck/build/lint commands when available. Check emitted/runtime behavior when module resolution, declarations, decorators, or build transforms are involved.

Observability and logging

  • Follow the project's existing logger, structured fields, tracing, metrics, severity, and correlation/request-ID conventions. Do not introduce a second logging system for one feature.
  • Record useful context at the layer that owns the operation or can act on the failure. Preserve error causes and avoid logging the same error at every layer.
  • Include the event, outcome, relevant operation/resource identifiers, and timing or status data when the project uses them. Never log secrets, access tokens, passwords, or raw sensitive payloads.
  • Add instrumentation when it supports debugging, operations, security, or a stated product requirement—not as boilerplate on every function. Ensure logging failures do not turn a recoverable application failure into a new outage unless the contract requires fail-closed behavior.

Review checklist

Prioritize:

  1. Type/runtime mismatches, unsound assertions, leaked any, incorrect optionality, and cleanup failures.
  2. Missing validation at real external boundaries and duplicated validation inside trusted code.
  3. Async errors, floating promises, race conditions, and cleanup behavior.
  4. Public API compatibility, module-resolution/package-export mistakes, and generated declaration quality.
  5. Type complexity, speculative generics, unnecessary compiler/lint churn, and defensive branches that obscure the real invariant.
  6. as used to silence an error, non-null assertions, leaked any, dumping unknown data into Record<string, unknown>, and Partial<T> used where the API really requires a complete value.
  7. forEach(async ...), un-awaited map(async ...), accidental sequential awaits, inconsistent type/interface or enum/union choices, and runtime checks duplicated after parsing.
  8. Date/time ambiguity, dependency churn, typechecking-only verification, runtime/module-resolution mismatches, unexplained compiler or typed-lint slowdowns, and configuration changes unrelated to the requested behavior.
  9. Invented or stale APIs, accidental public-contract breaks, tests with weak assertions or excessive mocks, unrelated file churn, suppressed diagnostics, and completion claims without evidence.

Do not recommend a type-level abstraction, compiler flag, or validation layer merely because it exists. Recommend it when it prevents a demonstrated class of bugs or materially clarifies the contract.

Reference material

Read references/decision-rules.md when you need concrete before/after examples for the highest-impact rules.

These references inform decisions; they do not override project constraints or the user's preference for proportionate validation and low ceremony.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.