typescript-guidelines
Skill1 starsChanged 7 months agoMITView on GitHub
Mandatory TypeScript strict mode and type-safety guidelines. Use whenever writing new TypeScript, improving existing TypeScript, reviewing TypeScript, refactoring TypeScript, deciding between schemas and plain TypeScript definitions, choosing type vs interface, modeling domain values, configuring tsconfig, or checking TypeScript code for unsafe patterns.
4 other projects use a version of this.
Adopted by
Most recently taken up by
- youssef-tharwat/lang-guidelinestheir own version, first seen Nov 8, 2026
- enso-org/ensotheir own version, first seen Oct 8, 2026
- stevencarpenter/agentstheir own version, first seen Jul 21, 2026
- Hotakus/hotakus-useful-skillstheir own version, first seen Jul 2, 2026
Other versions4 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 4
- enso-org/enso★ 7.4ksame name
- youssef-tharwat/lang-guidelines★ 7same name
- Hotakus/hotakus-useful-skills★ 2same name
- stevencarpenter/agents★ 1same 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 repository3 other files it gives its agents.
- react-best-practicesSkill
- script-creatorSkill
- web-best-practicesSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
34 rules. 0 are common, 26 are distinctive, and 8 are very specific to this project.
0
Common
Most of the 3 edited versions we read keep these.
26
Distinctive
This author's own choices. Worth a look.
8
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 14
Use TypeScript to make illegal states hard to represent, parse unknown input at system boundaries, and keep internal code simple, explicit, …
- Distinctive: line 20
Use this skill whenever TypeScript is being written, changed, reviewed, or configured.
- Distinctive: line 33
Do not treat this as optional guidance.
- Distinctive: line 37
Run TypeScript in strict mode.
- Very specific: line 38
Do not use any; use unknown when a value is genuinely unknown.
- Distinctive: line 39
Do not use type assertions unless the justification is explicit and local.
- Very specific: line 40
Do not use @ts-ignore or @ts-expect-error without an explanation of why the type system cannot express the case.
- Distinctive: line 42
Prefer domain-specific types over generic primitives when values are easy to mix up.
- Distinctive: line 45
Use real shared schemas and types in tests.
- Distinctive: line 49
Prefer this compiler baseline unless the existing project has a stricter standard:
- Distinctive: line 72
Use types that encode meaning, not just structure.
- Distinctive: line 87
Use explicit annotations when they improve clarity.
- Very specific: line 89
Use Pick, Omit, Partial, Required, and mapped types when they communicate intent better than duplicating a shape.
- Distinctive: line 90
Use discriminated unions for state and workflow variants.
- Very specific: line 95
Use type for domain data. Use interface for call signatures and behavior contracts.
- Very specific: line 144
Use a Standard Schema-compatible library.
- Distinctive: line 175
Keep parsing and validation close to the boundary.
- Distinctive: line 176
Avoid revalidating parsed internal values repeatedly.
- Distinctive: line 180
Use a schema when any of these are true:
- Distinctive: line 185
Test factories need runtime validation against a real production contract.
- Distinctive: line 229
Ask these questions in order:
- Distinctive: line 236
If the answer to any question is yes, use a schema and derive the type from it.
- Distinctive: line 238
If the value is purely internal and compile-time-only, use a plain TypeScript definition.
- Distinctive: line 242
Test files must import real schemas and types from shared locations.
- Distinctive: line 243
Test factories should return complete valid objects with sensible defaults.
- Very specific: line 244
Test factories may accept Partial<T> overrides for focused variation.
- Distinctive: line 245
When a production schema exists, validate factory output with that schema.
- Distinctive: line 246
Never redefine API, database, event, or shared domain contracts inside tests.
- Very specific: line 263
Use kebab-case file names.
- Distinctive: line 265
Use dedicated suffixes when a module exports only schemas or only types:
- Distinctive: line 279
Keep local-only types near the implementation.
- Distinctive: line 289
Do tests import real schemas and shared types?
- Very specific: line 290
Do file names follow kebab-case, *.schemas.ts, and *.types.ts conventions?
- Distinctive: line 294
When applying this skill, produce one of these outputs depending on the task:
“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 Guidelines
Purpose
Use TypeScript to make illegal states hard to represent, parse unknown input at system boundaries, and keep internal code simple, explicit, and refactorable.
This skill turns the project TypeScript guidance into an operational checklist for code generation, review, and convention decisions.
When to Use
Use this skill whenever TypeScript is being written, changed, reviewed, or configured.
This includes:
- Writing new TypeScript application code, tests, utilities, or shared contracts.
- Improving old TypeScript code, even when the requested change is small.
- Refactoring TypeScript without changing behavior.
- Reviewing TypeScript for correctness, maintainability, type safety, boundary parsing, unsafe assertions, or schema duplication.
- Deciding whether a value needs a runtime schema or only a compile-time TypeScript definition.
- Choosing between
typeandinterface, especially for data shapes, component props, argument objects, and configuration options. - Setting up or auditing
tsconfigstrictness. - Creating test factories or shared test data for TypeScript code.
Do not treat this as optional guidance. If a task involves TypeScript, apply these rules unless the user explicitly asks to ignore them.
Core Rules
- Run TypeScript in strict mode.
- Do not use
any; useunknownwhen a value is genuinely unknown. - Do not use type assertions unless the justification is explicit and local.
- Do not use
@ts-ignoreor@ts-expect-errorwithout an explanation of why the type system cannot express the case. - Apply the same type-safety rules to tests as production code.
- Prefer domain-specific types over generic primitives when values are easy to mix up.
- Define schemas first at trust boundaries and derive types from schemas.
- Parse once at the boundary, then trust parsed types internally.
- Use real shared schemas and types in tests; never redefine production contracts in test files.
Strict Mode Baseline
Prefer this compiler baseline unless the existing project has a stricter standard:
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"alwaysStrict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
}
}Type Modeling
Use types that encode meaning, not just structure.
type UserId = string & { brand: unique symbol };
type PaymentAmount = number & { brand: unique symbol };
type User = {
id: UserId;
email: string;
role: UserRole;
};Guidelines:
- Use explicit annotations when they improve clarity.
- Rely on inference when the inferred type is already precise.
- Use
Pick,Omit,Partial,Required, and mapped types when they communicate intent better than duplicating a shape. - Use discriminated unions for state and workflow variants.
- Introduce branded types when plain primitives would allow mixing distinct domain values.
Type vs Interface
Use type for domain data. Use interface for call signatures and behavior contracts.
Use type for:
- Object data shapes.
- Unions and discriminated unions.
- Intersections.
- Mapped types.
- Domain data structures.
- Internal state shapes.
Use interface for:
- Component props.
- Argument objects.
- Configuration options.
- Ports.
- Adapters.
- Dependency injection contracts.
- Behavior-oriented abstractions.
Example:
type PaymentRequest = {
amount: number;
currency: string;
};
interface PaymentFormProps {
payment: PaymentRequest;
onSubmit: (payment: PaymentRequest) => void;
}
interface ChargePaymentOptions {
request: PaymentRequest;
idempotencyKey: string;
}
interface Logger {
log(message: string): void;
error(message: string, error: Error): void;
}Schema-First Boundaries
Parse data at trust boundaries. Do not trust external input because it has a TypeScript type.
Use a Standard Schema-compatible library. Zod is the default example.
import { z } from "zod";
const AddressDetailsSchema = z.object({
houseNumber: z.string(),
addressLine1: z.string().min(1),
city: z.string().min(1),
postcode: z.string().min(1),
});
const PaymentRequestSchema = z.object({
cardAccountId: z.string().length(16),
amount: z.number().positive(),
source: z.enum(["Web", "Mobile", "API"]),
addressDetails: AddressDetailsSchema,
});
type AddressDetails = z.infer<typeof AddressDetailsSchema>;
type PaymentRequest = z.infer<typeof PaymentRequestSchema>;
export const parsePaymentRequest = (input: unknown): PaymentRequest => {
return PaymentRequestSchema.parse(input);
};Rules:
- Define schemas first when data crosses a runtime boundary.
- Derive types from schemas instead of duplicating shape definitions.
- Keep parsing and validation close to the boundary.
- Avoid revalidating parsed internal values repeatedly.
When Schemas Are Required
Use a schema when any of these are true:
- Data crosses a trust boundary.
- The type encodes runtime validation rules.
- The shape is a shared contract between systems.
- Test factories need runtime validation against a real production contract.
Typical schema-required cases:
- API requests and responses.
- Database records at adapter boundaries.
- Queue messages and domain events.
- URL params, form data, and environment variables.
- Reusable test data validated against production contracts.
When Plain TypeScript Definitions Are Enough
Plain TypeScript definitions are enough when the value is purely internal and has no runtime validation need.
Typical plain-type cases:
- Internal state.
- Utility types.
- Discriminated unions for control flow.
- Branded primitives.
- Behavior contracts.
- Component props, argument objects, and configuration options that do not cross a trust boundary.
Example:
type Point = {
x: number;
y: number;
};
type Result<T, E = Error> =
| { ok: true; value: T }
| { ok: false; error: E };
type LoadingState =
| { status: "idle" }
| { status: "loading" }
| { status: "success"; data: unknown }
| { status: "error"; error: Error };Decision Framework
Ask these questions in order:
- Does this value enter from outside the system?
- Does it have runtime validation rules?
- Is it a shared contract?
- Will tests rely on the real contract?
If the answer to any question is yes, use a schema and derive the type from it.
If the value is purely internal and compile-time-only, use a plain TypeScript definition.
Test Data Rules
- Test files must import real schemas and types from shared locations.
- Test factories should return complete valid objects with sensible defaults.
- Test factories may accept
Partial<T>overrides for focused variation. - When a production schema exists, validate factory output with that schema.
- Never redefine API, database, event, or shared domain contracts inside tests.
Example:
const buildUser = (overrides: Partial<User> = {}): User => {
return UserSchema.parse({
id: crypto.randomUUID(),
email: "person@example.com",
role: "user",
...overrides,
});
};File Naming
Use kebab-case file names.
Use dedicated suffixes when a module exports only schemas or only types:
*.schemas.tsfor runtime schemas.*.types.tsfor compile-time-only shared types.
Examples:
user.schemas.ts
user.types.ts
payment.schemas.ts
payment.types.tsKeep local-only types near the implementation. Split schemas or types into dedicated modules when they are shared or when separation materially improves clarity.
Review Checklist
- Does the code avoid
anyand unjustified assertions? - Are unknown inputs typed as
unknownuntil parsed? - Are trust boundaries parsed once with real schemas?
- Are schema-derived types used instead of duplicated contract shapes?
- Are internal-only values modeled with simple plain types?
- Are
typeandinterfaceused according to domain data vs call signature and behavior semantics? - Do tests import real schemas and shared types?
- Do file names follow
kebab-case,*.schemas.ts, and*.types.tsconventions?
Output
When applying this skill, produce one of these outputs depending on the task:
- For implementation: TypeScript code that passes strict type checking and uses schemas at boundaries.
- For review: Findings ordered by risk, with file references and concrete fixes.
- For convention decisions: A direct recommendation with the schema/plain-type rationale.
- For setup: A strict
tsconfigbaseline and migration notes for any existing incompatibilities.
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.

