

Also from Kynth Studios


Also from Kynth Studios


Also from Kynth Studios
1234567# Any inside generic functions89When building generic functions, you may need to use any inside the function10body.1112This is because TypeScript often cannot match your runtime logic to the logic13done inside your types.1415One example:1617```ts18const youSayGoodbyeISayHello = <TInput extends "hello" | "goodbye">(19 input: TInput20): TInput extends "hello" ? "goodbye" : "hello" => {21 if (input === "goodbye") {22 return "hello"; // Error!23 } else {24 return "goodbye"; // Error!25 }26};27```2829On the type level (and the runtime), this function returns `goodbye` when the30input is `hello`.3132There is no way to make this work concisely in TypeScript.3334So using `any` is the most concise solution:3536```ts37const youSayGoodbyeISayHello = <TInput extends "hello" | "goodbye">(38 input: TInput39): TInput extends "hello" ? "goodbye" : "hello" => {40 if (input === "goodbye") {41 return "hello" as any;42 } else {43 return "goodbye" as any;44 }45};46```4748Outside of generic functions, use `any` extremely sparingly.4950# Default exports5152Unless explicitly required by the framework, do not use default exports.5354```ts55// BAD56export default function myFunction() {57 return <div>Hello</div>;58}59```6061```ts62// GOOD63export function myFunction() {64 return <div>Hello</div>;65}66```6768Default exports create confusion from the importing file.6970```ts71// BAD72import myFunction from "./myFunction";73```7475```ts76// GOOD77import { myFunction } from "./myFunction";78```7980There are certain situations where a framework may require a default export. For81instance, Next.js requires a default export for pages.8283```tsx84// This is fine, if required by the framework85export default function MyPage() {86 return <div>Hello</div>;87}88```8990# Discriminated unions9192Proactively use discriminated unions to model data that can be in one of a few93different shapes.9495For example, when sending events between environments:9697```ts98type UserCreatedEvent = {99 type: "user.created";100 data: { id: string; email: string };101};102103type UserDeletedEvent = {104 type: "user.deleted";105 data: { id: string };106};107108type Event = UserCreatedEvent | UserDeletedEvent;109```110111Use switch statements to handle the results of discriminated unions:112113```ts114const handleEvent = (event: Event) => {115 switch (event.type) {116 case "user.created":117 console.log(event.data.email);118 break;119 case "user.deleted":120 console.log(event.data.id);121 break;122 }123};124```125126Use discriminated unions to prevent the 'bag of optionals' problem.127128For example, when describing a fetching state:129130```ts131// BAD - allows impossible states132type FetchingState<TData> = {133 status: "idle" | "loading" | "success" | "error";134 data?: TData;135 error?: Error;136};137138// GOOD - prevents impossible states139type FetchingState<TData> =140 | { status: "idle" }141 | { status: "loading" }142 | { status: "success"; data: TData }143 | { status: "error"; error: Error };144```145146# Enums147148Do not introduce new enums into the codebase. Retain existing enums.149150If you require enum-like behaviour, use an `as const` object:151152```ts153const backendToFrontendEnum = {154 xs: "EXTRA_SMALL",155 sm: "SMALL",156 md: "MEDIUM",157} as const;158159type LowerCaseEnum = keyof typeof backendToFrontendEnum; // "xs" | "sm" | "md"160161type UpperCaseEnum = (typeof backendToFrontendEnum)[LowerCaseEnum]; // "EXTRA_SMALL" | "SMALL" | "MEDIUM"162```163164Remember that numeric enums behave differently to string enums. Numeric enums165produce a reverse mapping:166167```ts168enum Direction {169 Up,170 Down,171 Left,172 Right,173}174175const direction = Direction.Up; // 0176const directionName = Direction[0]; // "Up"177```178179This means that the enum `Direction` above will have eight keys instead of four.180181```ts182enum Direction {183 Up,184 Down,185 Left,186 Right,187}188189Object.keys(Direction).length; // 8190```191192# Import type193194Use import type whenever you are importing a type.195196Prefer top-level `import type` over inline `import { type ... }`.197198```ts199// BAD200import { type User } from "./user";201```202203```ts204// GOOD205import type { User } from "./user";206```207208The reason for this is that in certain environments, the first version's import209will not be erased. So you'll be left with:210211```ts212// Before transpilation213import { type User } from "./user";214215// After transpilation216import "./user";217```218219# Installing packages220221When installing libraries, do not rely on your own training data.222223Your training data has a cut-off date. You're probably not aware of all of the224latest developments in the JavaScript and TypeScript world.225226This means that instead of picking a version manually (via updating the227`package.json` file), you should use a script to install the latest version of a228library.229230```bash231bun add -D @typescript-eslint/eslint-plugin232```233234This will ensure you're always using the latest version.235236Prefer to install packages, not in the root, but in the mono repo packages237238# Interface extends239240ALWAYS prefer interfaces when modelling inheritance.241242The `&` operator has terrible performance in TypeScript. Only use it where243`interface extends` is not possible.244245```ts246// BAD247248type A = {249 a: string;250};251252type B = {253 b: string;254};255256type C = A & B;257```258259```ts260// GOOD261262interface A {263 a: string;264}265266interface B {267 b: string;268}269270interface C extends A, B {271 // Additional properties can be added here272}273```274275# Jsdoc276277Use JSDoc comments to annotate functions and types.278279Be concise in JSDoc comments, and only provide JSDoc comments if the function's280behaviour is not self-evident for a novice developer.281282Use the JSDoc inline `@link` tag to link to other functions and types within the283same file.284285```ts286/**287 * Subtracts two numbers288 */289const subtract = (a: number, b: number) => a - b;290291/**292 * Does the opposite to {@link subtract}293 */294const add = (a: number, b: number) => a + b;295```296297# Naming conventions298299- Use kebab-case for file names (e.g., `my-component.ts`)300- Use camelCase for variables and function names (e.g., `myVariable`,301 `myFunction()`)302- Use UpperCamelCase (PascalCase) for classes, types, and interfaces (e.g.,303 `MyClass`, `MyInterface`)304- Use ALL_CAPS for constants and enum values (e.g., `MAX_COUNT`, `Color.RED`)305- Inside generic types, functions or classes, prefix type parameters with `T`306 (e.g., `TKey`, `TValue`)307308```ts309type RecordOfArrays<TItem> = Record<string, TItem[]>;310```311312# No unchecked access313314If the user has this rule enabled in their `tsconfig.json`, indexing into315objects and arrays will behave differently from how you expect.316317```ts318const obj: Record<string, string> = {};319320// With noUncheckedIndexedAccess, value will321// be `string | undefined`322// Without it, value will be `string`323const value = obj.key;324```325326```ts327const arr: string[] = [];328329// With noUncheckedIndexedAccess, value will330// be `string | undefined`331// Without it, value will be `string`332const value = arr[0];333```334335# Optional properties336337Use optional properties extremely sparingly. Only use them when the property is338truly optional, and consider whether bugs may be caused by a failure to pass the339property.340341In the example below we always want to pass user ID to `AuthOptions`. This is342because if we forget to pass it somewhere in the code base, it will cause our343function to be not authenticated.344345```ts346// BAD347type AuthOptions = {348 userId?: string;349};350351const func = (options: AuthOptions) => {352 const userId = options.userId;353};354```355356```ts357// GOOD358type AuthOptions = {359 userId: string | undefined;360};361362const func = (options: AuthOptions) => {363 const userId = options.userId;364};365```366367# Readonly properties368369Use `readonly` properties for object types by default. This will prevent370accidental mutation at runtime.371372Omit `readonly` only when the property is genuinely mutable.373374```ts375// BAD376type User = {377 id: string;378};379380const user: User = {381 id: "1",382};383384user.id = "2";385```386387```ts388// GOOD389type User = {390 readonly id: string;391};392393const user: User = {394 id: "1",395};396397user.id = "2"; // Error398```399400# Return types401402When declaring functions on the top-level of a module, declare their return403types. This will help future AI assistants understand the function's purpose.404405```ts406const myFunc = (): string => {407 return "hello";408};409```410411One exception to this is components which return JSX. No need to declare the412return type of a component, as it is always JSX.413414```tsx415const MyComponent = () => {416 return <div>Hello</div>;417};418```419420# Throwing421422Think carefully before implementing code that throws errors.423424If a thrown error produces a desirable outcome in the system, go for it. For425instance, throwing a custom error inside a backend framework's request handler.426427However, for code that you would need a manual try catch for, consider using a428result type instead:429430```ts431type Result<T, E extends Error> =432 | { ok: true; value: T }433 | { ok: false; error: E };434```435436For example, when parsing JSON:437438```ts439const parseJson = (input: string): Result<unknown, Error> => {440 try {441 return { ok: true, value: JSON.parse(input) };442 } catch (error) {443 return { ok: false, error: error as Error };444 }445};446```447448This way you can handle the error in the caller:449450```ts451const result = parseJson('{"name": "John"}');452453if (result.ok) {454 console.log(result.value);455} else {456 console.error(result.error);457}458```459
One repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| settlemint/sdk.cursor/rules/commands.mdc · 15 | Cursor rules | setup | 31/100 | 14 days ago | |
| settlemint/sdk.cursor/rules/solidity.mdc · 15 | Cursor rules | teststylearchtypes+6 | 73/100 | 14 days ago | |
| settlemint/sdk.cursor/rules/bun.mdc · 15 | Cursor rules | setupbuildtest | 73/100 | 14 days ago | |
| settlemint/sdk.cursor/rules/commits.mdc · 15 | Cursor rules | lint-formattypesgitdependencies | 48/100 | 14 days ago | |
| settlemint/sdk.cursor/rules/git-workflow.mdc · 15 | Cursor rules | gitdo-notagent-behaviour | 65/100 | 14 days ago | |
| settlemint/sdk.cursor/rules/mcp.mdc · 15 | Cursor rules | no sections | 4/100 | 14 days ago | |
| settlemint/sdk.cursor/rules/shadcn.mdc · 15 | Cursor rules | ui | 55/100 | 14 days ago | |
| settlemint/sdkCLAUDE.md · 15 | CLAUDE.md | setupbuildtestlint-format+10 | 96/100 | 14 days ago |
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| hiromaily/go-crypto-wallet.cursor/rules/typescript.mdc · 126 | Cursor rules | setupbuildtestlint-format+6 | 100/100 | 14 days ago | |
| TechSquidTV/Hermes.cursor/rules/10-hermes-api.mdc · 45 | Cursor rules | testlint-formatstylearch+5 | 100/100 | 14 days ago | |
| dodgecfr/combatfilms-webapp.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 14 days ago | |
| deifos/clipmira-subtitles.cursor/rules/frontend.mdc · 1 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 14 days ago | |
| markstev/mark-starter.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+6 | 99/100 | 14 days ago | |
| Allymahmoud/case-intake-platform.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 14 days ago | |
| langflow-ai/langflow.cursor/rules/docs_development.mdc · 153k | Cursor rules | setupbuildtestlint-format+7 | 97/100 | 14 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-api-tests.mdc · 45 | Cursor rules | teststyletesting-strategysecurity+3 | 97/100 | 14 days ago |
A badge carrying the measured quality of the strongest agent config file in this repository, out of 100. It reads from this index every time somebody loads your page, so it changes when the measurement changes and there is nothing to keep up to date. Free, no account, and the value is not something you or we can set by hand.
[](https://rulestack.kynth.studio/configs/settlemint-sdk-cursor-rules-typescript)Would rather not hotlink us? Every badge is also served in shields.io’s endpoint schema, so shields renders the image and your readers never talk to our domain:
Published by Toolproof, the masthead over this index and eight others. The method behind the number is at toolproof.kynth.studio/methodology, and the whole thing is readable as JSON with no key at /api.
Directory