{"id":19026587,"url":"https://github.com/sbdchd/tslint-cake","last_synced_at":"2026-05-01T00:30:15.400Z","repository":{"id":152740743,"uuid":"166339932","full_name":"sbdchd/tslint-cake","owner":"sbdchd","description":":cake: TSLint rules for sweet code","archived":false,"fork":false,"pushed_at":"2019-03-21T14:26:44.000Z","size":77,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-02T02:29:15.142Z","etag":null,"topics":["tslint","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sbdchd.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-01-18T03:47:31.000Z","updated_at":"2019-03-21T14:26:45.000Z","dependencies_parsed_at":null,"dependency_job_id":"8d42cb46-18f7-4af4-a8f3-a6fe4c3fc2b1","html_url":"https://github.com/sbdchd/tslint-cake","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sbdchd%2Ftslint-cake","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sbdchd%2Ftslint-cake/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sbdchd%2Ftslint-cake/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sbdchd%2Ftslint-cake/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sbdchd","download_url":"https://codeload.github.com/sbdchd/tslint-cake/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240072219,"owners_count":19743551,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["tslint","typescript"],"created_at":"2024-11-08T20:49:44.240Z","updated_at":"2026-05-01T00:30:15.344Z","avatar_url":"https://github.com/sbdchd.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tslint-cake [![npm version](https://badge.fury.io/js/tslint-cake.svg)](https://www.npmjs.com/package/tslint-cake) [![CircleCI](https://circleci.com/gh/sbdchd/tslint-cake.svg?style=svg)](https://circleci.com/gh/sbdchd/tslint-cake)\n\n\u003e TSLint rules for sweet code\n\n## Usage\n\n1. Install\n\n```shell\nyarn add tslint-cake\n```\n\n2. Update `tslint.json`\n\n```json\n{\n  \"extends\": [\"tslint-cake\"],\n  \"rules\": {\n    \"react-prefer-simple-fragment\": true\n    // ...\n  }\n}\n```\n\n## Why?\n\nTo have a place to add miscellaneous TSLint rules that don't exist in TSLint\nor common TSLint libraries.\n\n## Rules\n\n### `no-pointless-computed-property-name`\n\nUse `{ foo: bar }` instead of `{ [\"foo\"]: bar }`\n\n### `react-prefer-simple-fragment` [Fixer]\n\nUse `\u003c\u003e\u003c/\u003e` instead of `\u003cReact.Fragment\u003e\u003cReact.Fragment/\u003e`\n\n### `jsx-no-true-attribute` [Fixer]\n\nUse `\u003cFoo bar/\u003e` instead of `\u003cFoo bar={true}/\u003e`\n\n### `no-template-string-cast`\n\nPrefer `String()` or `.toString()` to cast as a string instead of `` `${}` ``.\n\n### `no-pointless-case-scope`\n\nRemove unnecessary scopes in `switch` statement `case`s when the only child\nexpression is a `return` statement.\n\nE.g.,\n\n```typescript\nswitch (foo) {\n  case bar: {\n    return \"foo\"\n  }\n}\n// can become\nswitch (foo) {\n  case bar:\n    return \"foo\"\n}\n```\n\n### `no-name-never`\n\nUsing a variable `name` with type `never` is likely a mistake.\n\n`name` is defined globally if you include `--lib dom`.\n\nsee: \u003chttps://github.com/Microsoft/TypeScript/blob/3a2f6a3ed1a598a241e7c750873105f22e7a2463/lib/lib.dom.d.ts#L17405\u003e\n\n### `improper-map-prefer-foreach`\n\nPrefer `forEach` instead of `map` when the result isn't used\n\n```typescript\nfoo.map(x =\u003e {\n  x.id = 10\n})\n\n// should be\n\nfoo.forEach(x =\u003e {\n  x.id = 10\n})\n```\n\n### `no-promise-catch`\n\nUsing `.catch()` on a `Promise` usually means that you could better describe\nthe outputs of the async function using a union or `Result\u003cT, E\u003e` types.\n\n```typescript\ndeclare const getFooAsync: () =\u003e Promise\u003cnumber\u003e\n\ngetFooAsync()\n  .then(r =\u003e console.log(r))\n  .catch(e =\u003e console.error(e)) // `e` could be anything. We can't type the arg to catch.\n\n// instead we can do the following\n\ndeclare const getBarAsync: () =\u003e Promise\u003cnumber | Error\u003e\n\ngetBarAsync().then(r =\u003e {\n  if (r instanceof Error) {\n    console.error(r)\n  } else {\n    console.log(r)\n  }\n})\n```\n\n### `object-index-must-return-possibly-undefined`\n\nThe values of an index signature of a type are always possibly `undefined`\neven though TypeScript won't warn you. This lint forces you to define your\nindex signature to possibly return `undefined`.\n\n```typescript\ninterface IFoo {\n  [key: string]: number // Error: Value of an object key is possibly undefined.\n}\n\ninterface IBar {\n  [key: string]: number | undefined // ok\n}\n```\n\n### `exact-object-spread`\n\nThis rule is an attempt at gaining some semblence of\n[exactness](https://github.com/Microsoft/TypeScript/issues/12936) with object\nspread.\n\nCurrently, there are cases where TypeScript won't warn about adding extra,\nnon-existing properties to an object when spreading. This lint fills in some\nof those gaps and warns you when adding non-existent properties.\n\nNote, this rule attempts to enforce\n[exactness](https://flow.org/en/docs/types/objects/#exact-object-types) on\nall spreads and this might not be what you want.\n\n```typescript\ninterface IState {\n  id: number\n  name: string\n  address: {\n    street: string\n    state: string\n    country: string\n  }\n}\n\nfunction update(state: IState): IState {\n  return {\n    ...state,\n    notProp: false // TypeScript error\n  }\n}\n\n// TypeScript will also warn with nested spreading\nfunction update(state: IState): IState {\n  return {\n    ...state,\n    address: {\n      ...state.address,\n      notProp: false // TypeScript error\n    }\n  }\n}\n\n// However, if we pull the nested spread out into a variable TypeScript won't\n// warn us about extra properties\n\n// no errors with TypeScript\nfunction update(state: IState): IState {\n  const address = {\n    // TSLint error when we enable this rule\n    ...state.address,\n    foo: \"bar\"\n  }\n  return {\n    ...state,\n    address\n  }\n}\n```\n\n### `react-memo-requires-custom-compare`\n\nWhen using [`React.memo()`](https://reactjs.org/docs/react-api.html#reactmemo) or [`extends React.PureComponent`](https://reactjs.org/docs/react-api.html#reactpurecomponent) the default\ncomparsions are shallow which means they will always render for complex props\nlike `Date`'s, `Array`s, or `Object`s, even if the underlying values are equivalent.\n\nIn the cases of these complex props, this lint will warn you and recommend\npassing a custom compare function `React.memo()` or defining a custom\n`shouldComponentUpdate` and extending `React.Component`.\n\n**Caveat:** If an object is passed as a prop and isn't copied/changed, i.e., no\n`{...x}` then referential integrity is retained and the shallow compare of\n`React.memo()` and `PureComponent` will correctly prevent a render. So if you\nare using something like\n[Immutable-js](https://github.com/immutable-js/immutable-js) where shallow\nequals is maintained then this lint might be less helpful.\n\n```typescript\ninterface IOkayProps {\n  name: string\n  accountAge: number | string\n  admin: boolean\n}\n\nconst Okay = React.memo((props: IOkayProps) =\u003e (\n  \u003cp\u003e\n    {props.name} ({props.accountAge})\n  \u003c/p\u003e\n))\n\ninterface IBadProps {\n  user: {\n    name: string\n  }\n}\n\n// TSLint raises error\nconst Bad = React.memo((props: IBadProps) =\u003e \u003cp\u003e{props.user.name} \u003c/p\u003e)\n\n// TSLint raises error\nclass BadPure extends React.PureComponent\u003cIBadProps\u003e {\n  render() {\n    return \u003cp\u003e{props.user.name} \u003c/p\u003e\n  }\n}\n```\n\n### `no-implicit-to-string`\n\nChecks for cases where `null`, `undefined`, or `object` are converted to\n`string`.\n\n```typescript\nconst userName: string | null | Date = null\n// all of the following error\nconst foo = `hello ${userName}`\nconst bar = String(userName)\nconst blah = \"hello \" + userName\n```\n\n## Dev\n\n```shell\nyarn build\n\nyarn test\n\nyarn lint\n\nyarn fmt\n\nyarn publish\n```\n\n## TODO\n\n- add fixers\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsbdchd%2Ftslint-cake","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsbdchd%2Ftslint-cake","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsbdchd%2Ftslint-cake/lists"}