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
- nafeeur/MaskShifttheir own version, first seen Sep 12, 2026
- bluenzzz/typescript-engineeringtheir own version, first seen Sep 10, 2026
- ldilov/harness-forgetheir own version, first seen May 19, 2026
Other versions3 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 3
- bluenzzz/typescript-engineering★ 9same name
- ldilov/harness-forge★ 4same name
- 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
- Distinctive: line 9
Use this skill for TypeScript implementation, refactoring, debugging, type design, and code review.
- Distinctive: line 15
Keep compile-time contracts and runtime validation separate, and implement the smallest complete change.
- Distinctive: line 16
Verify both type-level and runtime behavior when the change can affect either.
- Distinctive: line 17
Before finishing, review the change against the red flags below and report the checks that ran.
- Very specific: line 22
Keep the patch scoped. Do not rewrite unrelated code, upgrade dependencies, change tsconfig, or add abstractions unless the requested …
- Distinctive: line 23
Never invent an API, package export, compiler option, or framework pattern from memory.
- Distinctive: line 25
Do not declare success from a clean typecheck alone when the change affects emitted JavaScript, module loading, serialization, …
- Very specific: line 26
Do not hide a failing check with any, as, @ts-ignore, a lint disable, a broad catch, skipLibCheck, or an unrelated config change.
- Very specific: line 30
Read package.json, tsconfig files, source conventions, and existing test/lint/format/build scripts before making design decisions.
- Very specific: line 36
Before editing, look for applicable AGENTS.md, CONTRIBUTING.md, STYLEGUIDE.md, README.md, .editorconfig, formatter/linter configs, package …
- Distinctive: line 38
Follow explicit scoped project instructions and configured tools, then the nearest maintained implementation.
- Distinctive: line 39
Use the repository's formatter and lint commands for changed files.
- Distinctive: line 47
If the repository is silent, use the defaults below and use them consistently throughout the change.
- Distinctive: line 48
Do not perform an unrelated type-style migration just to normalize older code.
- Distinctive: line 50
When multiple implementations are equally sound, select one rather than making agents or users choose between equivalent patterns.
- Distinctive: line 52
When the repository is silent, prefer:
- Distinctive: line 81
Do not concatenate untrusted values into SQL, shell commands, HTML, templates, filesystem paths, dynamic code, or redirects.
- Very specific: line 82
Treat unknown JSON, deserialized data, request parameters, uploaded files, environment values, and third-party responses as untrusted until …
- Distinctive: line 85
Keep secrets, tokens, passwords, and unnecessary sensitive data out of logs, thrown errors, and client responses.
- Distinctive: line 91
Prefer inference for obvious local values.
- Very specific: line 93
Use unknown for values whose type is not known, then narrow them.
- Very specific: line 94
Use satisfies when a value must conform to a type while retaining its useful inferred literal type.
- Very specific: line 95
Avoid broad assertions (as SomeType), non-null assertions (!), Function, boxed primitives, and empty-object types as shortcuts.
- Distinctive: line 96
Prefer readonly types or immutable data only where they communicate an actual ownership/invariant.
- Very specific: line 105
Treat unknown as a prompt to narrow at the boundary, not as a reason to sprinkle typeof checks throughout trusted business logic.
- Distinctive: line 107
Keep security controls regardless of type safety: authorization, input constraints, injection prevention, output encoding, secret handling, …
- Very specific: line 112
Consider strict, useUnknownInCatchVariables, noUncheckedIndexedAccess, and exactOptionalPropertyTypes based on the codebase's actual data …
- Very specific: line 114
If typed linting is already configured, use its diagnostics to catch issues such as floating promises, unsafe any flow, and unnecessary …
- Distinctive: line 115
Use Prettier or the repository's formatter for formatting.
- Very specific: line 120
Do not leave promises unhandled.
- Very specific: line 122
Keep runtime API contracts aligned with the types.
- Very specific: line 124
When an operation owns timers, listeners, subscriptions, streams, sockets, or clients, define its cleanup and cancellation path.
- Distinctive: line 128
Test behavior and contracts, not TypeScript syntax or implementation trivia.
- Distinctive: line 129
Keep type-level tests only for public type APIs or inference guarantees that users rely on.
- Very specific: line 130
After changes, run the relevant tests plus the repository's typecheck/build/lint commands when available.
- Distinctive: line 134
Follow the project's existing logger, structured fields, tracing, metrics, severity, and correlation/request-ID conventions.
- Distinctive: line 137
Add instrumentation when it supports debugging, operations, security, or a stated product requirement—not as boilerplate on every function.
- Distinctive: line 153
Do not recommend a type-level abstraction, compiler flag, or validation layer merely because it exists.
- Distinctive: line 157
Read references/decision-rules.md when you need concrete before/after examples for the highest-impact rules.
“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.
Files
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
- Inspect the repository, compiler/build configuration, and the nearest code with the same API shape.
- Choose the existing convention; if none exists, apply the defaults in this skill consistently.
- Keep compile-time contracts and runtime validation separate, and implement the smallest complete change.
- Verify both type-level and runtime behavior when the change can affect either; do not treat a clean typecheck as runtime proof.
- 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,tsconfigfiles, 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-eslintversions. 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:
- Match the nearest working code and public API conventions.
- Match the repository's compiler, lint, runtime, and build configuration.
- If the repository is silent, use the defaults below and use them consistently throughout the change.
- 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.
typefor unions, tuples, mapped/conditional types, and composition;interfacefor 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.
unknownat untrusted boundaries, followed by one narrow/parse step; localizedanyonly for a documented, unavoidable integration gap.satisfiesfor config-like values that need checking while retaining their inferred literals; narrowing or a purpose-built type guard instead of broadasassertions.- Optional properties (
field?: T) when omission has meaning;field: T | undefinedwhen 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 typefor 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 withany,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
tsconfiginheritance. 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
unknownJSON, 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 withas,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
unknownfor values whose type is not known, then narrow them. Avoidany; 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
satisfieswhen a value must conform to a type while retaining its useful inferred literal type. Useas 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
unknownas a prompt to narrow at the boundary, not as a reason to sprinkletypeofchecks 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, andexactOptionalPropertyTypesbased 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
anyflow, 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 appropriatePromisecombinator. - Do not leave promises unhandled. An intentional fire-and-forget call should have an explicit, repository-appropriate convention and a known rejection strategy;
voidalone documents intent but does not handle a rejection. - Catch
unknownerrors 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.allby 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
AbortSignalor 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:
- Type/runtime mismatches, unsound assertions, leaked
any, incorrect optionality, and cleanup failures. - Missing validation at real external boundaries and duplicated validation inside trusted code.
- Async errors, floating promises, race conditions, and cleanup behavior.
- Public API compatibility, module-resolution/package-export mistakes, and generated declaration quality.
- Type complexity, speculative generics, unnecessary compiler/lint churn, and defensive branches that obscure the real invariant.
asused to silence an error, non-null assertions, leakedany, dumping unknown data intoRecord<string, unknown>, andPartial<T>used where the API really requires a complete value.forEach(async ...), un-awaitedmap(async ...), accidental sequential awaits, inconsistenttype/interfaceor enum/union choices, and runtime checks duplicated after parsing.- 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.
- 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.
- TypeScript Handbook: https://www.typescriptlang.org/docs/handbook/
- TypeScript narrowing: https://www.typescriptlang.org/docs/handbook/2/narrowing.html
strict: https://www.typescriptlang.org/tsconfig/strict.htmlnoUncheckedIndexedAccess: https://www.typescriptlang.org/tsconfig/noUncheckedIndexedAccess.htmlexactOptionalPropertyTypes: https://www.typescriptlang.org/tsconfig/exactOptionalPropertyTypes.htmluseUnknownInCatchVariables: https://www.typescriptlang.org/tsconfig/useUnknownInCatchVariables.html- TypeScript utility types: https://www.typescriptlang.org/docs/handbook/utility-types.html
- typescript-eslint shared configs: https://typescript-eslint.io/users/configs/
no-explicit-any: https://typescript-eslint.io/rules/no-explicit-any/no-floating-promises: https://typescript-eslint.io/rules/no-floating-promises/no-unnecessary-condition: https://typescript-eslint.io/rules/no-unnecessary-condition/- TypeScript compiler performance guidance: https://github.com/microsoft/TypeScript/wiki/Performance
- TypeScript configuration/upgrade tradeoffs: https://github.com/microsoft/TypeScript/issues/50997
- OWASP input validation guidance: https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html
- OWASP XSS prevention: https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html
- OWASP injection prevention: https://cheatsheetseries.owasp.org/cheatsheets/Injection_Prevention_Cheat_Sheet.html
- OWASP SSRF prevention: https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html
- OWASP logging guidance: https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html
- State of JavaScript 2025 pain points: https://2025.stateofjs.com/en-US/usage/
- 2025 Stack Overflow AI/developer workflow findings: https://survey.stackoverflow.co/2025/ai
These references inform decisions; they do not override project constraints or the user's preference for proportionate validation and low ceremony.
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
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.

