{"_id":"@arnaud-barre/type-lint","_rev":"6-75e7b3cb57f7831a6e606f115a992186","name":"@arnaud-barre/type-lint","dist-tags":{"latest":"0.0.5"},"versions":{"0.0.1":{"name":"@arnaud-barre/type-lint","version":"0.0.1","author":"Arnaud Barré (https://github.com/ArnaudBarre)","license":"MIT","_id":"@arnaud-barre/type-lint@0.0.1","maintainers":[{"name":"arnaud-barre","email":"arnaud.barre72@gmail.com"}],"bin":{"type-lint":"cli.js"},"dist":{"shasum":"62b7ecf62949c9f4b3dd4cb16b137d000103d160","tarball":"https://registry.npmjs.org/@arnaud-barre/type-lint/-/type-lint-0.0.1.tgz","fileCount":46,"integrity":"sha512-yqfRqyehfXpbKMrb5urWnUowYZc0J9KgUnegglGm8mNLHk7MoaOT+XwpXEtl6uyjo22i4+2AKZ5Gpbt51WvFYQ==","signatures":[{"sig":"MEQCIF6v/xmCkw57kcoPDbZnhHi3+XASzfiH1R4K0caORExiAiBfmYxx1lVPDyUf53QHUX3sgzqSl91Zr1r6O4fwUSMMpw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":393934},"type":"module","shasum":"62b7ecf62949c9f4b3dd4cb16b137d000103d160","exports":{".":{"types":"./index.d.ts","import":"./index.js"},"./rules":{"types":"./rules.d.ts","import":"./rules.js"},"./plugin":{"import":"./plugin.js"},"./allRules":{"types":"./allRules.d.ts","import":"./allRules.js"}},"_npmUser":{"name":"arnaud-barre","email":"arnaud.barre72@gmail.com"},"_integrity":"sha512-yqfRqyehfXpbKMrb5urWnUowYZc0J9KgUnegglGm8mNLHk7MoaOT+XwpXEtl6uyjo22i4+2AKZ5Gpbt51WvFYQ==","repository":"github:ArnaudBarre/type-lint","_npmVersion":"10.8.3","description":"Modern TypeScript linter","directories":{},"_nodeVersion":"22.6.0","dependencies":{"esbuild":"^0.24.0","ts-api-utils":"^1.3.0"},"_hasShrinkwrap":false,"peerDependencies":{"typescript":"^5.6.3"},"_npmOperationalInternal":{"tmp":"tmp/type-lint_0.0.1_1733091841862_0.01742479480232384","host":"s3://npm-registry-packages"},"deprecated":"Moved to tsl"},"0.0.2":{"name":"@arnaud-barre/type-lint","version":"0.0.2","author":"Arnaud Barré (https://github.com/ArnaudBarre)","license":"MIT","_id":"@arnaud-barre/type-lint@0.0.2","maintainers":[{"name":"arnaud-barre","email":"arnaud.barre72@gmail.com"}],"bin":{"type-lint":"cli.js"},"dist":{"shasum":"b864c29dfde9ad2c1c89cd2bdd3b3eab2e0d0db3","tarball":"https://registry.npmjs.org/@arnaud-barre/type-lint/-/type-lint-0.0.2.tgz","fileCount":51,"integrity":"sha512-ic+6nXFQGyTn3CBrzJ69HUKCJogOSbZEgc4L+8CaqA3uyNDd3gs6FC1brYXgcPw2i17p7chXNhS8vWxy0RL4bw==","signatures":[{"sig":"MEUCIC5K6nW9xbnUtJZdDVK/RzsK32tCQE0FzHcGfeeN5RtrAiEAtA8vx422SvsbQlbzL9uzSeRnHESQ1mXC0F9MrASVbAI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":398731},"type":"module","shasum":"b864c29dfde9ad2c1c89cd2bdd3b3eab2e0d0db3","exports":{".":{"types":"./index.d.ts","import":"./index.js"},"./rules":{"types":"./rules.d.ts","import":"./rules.js"},"./plugin":{"import":"./plugin.js"},"./allRules":{"types":"./allRules.d.ts","import":"./allRules.js"},"./ruleTester":{"types":"./ruleTester.d.ts","import":"./ruleTester.js"}},"_npmUser":{"name":"arnaud-barre","email":"arnaud.barre72@gmail.com"},"_integrity":"sha512-ic+6nXFQGyTn3CBrzJ69HUKCJogOSbZEgc4L+8CaqA3uyNDd3gs6FC1brYXgcPw2i17p7chXNhS8vWxy0RL4bw==","repository":"github:ArnaudBarre/type-lint","_npmVersion":"10.8.3","description":"Modern TypeScript linter","directories":{},"_nodeVersion":"22.6.0","dependencies":{"esbuild":"^0.24.2","ts-api-utils":"^2.0.0"},"_hasShrinkwrap":false,"peerDependencies":{"typescript":"^5.6.3"},"_npmOperationalInternal":{"tmp":"tmp/type-lint_0.0.2_1740347262623_0.1286981465270256","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Moved to tsl"},"0.0.3":{"name":"@arnaud-barre/type-lint","version":"0.0.3","author":{"url":"https://github.com/ArnaudBarre","name":"Arnaud Barré"},"license":"MIT","_id":"@arnaud-barre/type-lint@0.0.3","maintainers":[{"name":"arnaud-barre","email":"arnaud.barre72@gmail.com"}],"homepage":"https://github.com/ArnaudBarre/type-lint#readme","bugs":{"url":"https://github.com/ArnaudBarre/type-lint/issues"},"bin":{"type-lint":"cli-entrypoint.js"},"dist":{"shasum":"9fef2e6038eb13663ecf094daec3b3005ade37fa","tarball":"https://registry.npmjs.org/@arnaud-barre/type-lint/-/type-lint-0.0.3.tgz","fileCount":12,"integrity":"sha512-LPwLrG3y+gSwF5xPrFmLZbx2tNzdlRSX/klTyyf9/2vWBsUm5PXI4hirw9Oe1AYCnOkSgJ/axGcVeKLF+NK8yw==","signatures":[{"sig":"MEQCIFJhO50XT+odYwiI/gLiT6mwtPe/hNbJu9cEAuwwb45/AiBUe2RwpVgHemH7O1ScYcYJp4uzWSlECJika9AHqfZkjA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":433143},"type":"module","types":"./index.d.ts","exports":{".":{"types":"./index.d.ts","import":"./index.js"},"./ruleTester":{"types":"./ruleTester.d.ts","import":"./ruleTester.js"}},"gitHead":"565d2f25bad17202c903e30dff7ec15b688d1697","_npmUser":{"name":"arnaud-barre","email":"arnaud.barre72@gmail.com"},"repository":{"url":"git+https://github.com/ArnaudBarre/type-lint.git","type":"git"},"_npmVersion":"10.9.2","description":"Modern TypeScript linter","directories":{},"_nodeVersion":"23.11.0","dependencies":{"esbuild":"^0.25.4","ts-api-utils":"^2.1.0"},"_hasShrinkwrap":false,"peerDependencies":{"typescript":"~5.8.3"},"_npmOperationalInternal":{"tmp":"tmp/type-lint_0.0.3_1748827306185_0.7365601655658442","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Moved to tsl"},"0.0.4":{"name":"@arnaud-barre/type-lint","version":"0.0.4","author":"Arnaud Barré (https://github.com/ArnaudBarre)","license":"MIT","_id":"@arnaud-barre/type-lint@0.0.4","maintainers":[{"name":"arnaud-barre","email":"arnaud.barre72@gmail.com"}],"bin":{"type-lint":"cli-entrypoint.js"},"dist":{"shasum":"6bd3f3e7927ec51ded39643bdd2f1c119d5c473f","tarball":"https://registry.npmjs.org/@arnaud-barre/type-lint/-/type-lint-0.0.4.tgz","fileCount":14,"integrity":"sha512-EP7RqlIrka8CAv9Dw0Kwjvo/9gZ824W6oeZxIEX88mQI90yBNVbwzHu83Hkt9/+JVuGSLNU+asjxFxYyrfYdDw==","signatures":[{"sig":"MEQCIBAWrG5QZxMArGzkWA5iIIb2uNMgYthWBK2gOsCaCR57AiB3fEVpImJG7XiuJmqhFkf8klyiupVSLIbz9pLcW15VDA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":646215},"type":"module","shasum":"6bd3f3e7927ec51ded39643bdd2f1c119d5c473f","exports":{".":{"types":"./index.d.ts","import":"./index.js"},"./ruleTester":{"types":"./ruleTester.d.ts","import":"./ruleTester.js"}},"_npmUser":{"name":"arnaud-barre","email":"arnaud.barre72@gmail.com"},"_integrity":"sha512-EP7RqlIrka8CAv9Dw0Kwjvo/9gZ824W6oeZxIEX88mQI90yBNVbwzHu83Hkt9/+JVuGSLNU+asjxFxYyrfYdDw==","repository":"github:ArnaudBarre/type-lint","_npmVersion":"10.8.3","description":"Modern TypeScript linter","directories":{},"_nodeVersion":"22.6.0","dependencies":{"esbuild":"^0.25.4","ts-api-utils":"^2.1.0"},"_hasShrinkwrap":false,"peerDependencies":{"typescript":"~5.8.3"},"_npmOperationalInternal":{"tmp":"tmp/type-lint_0.0.4_1750037856127_0.8207288735021612","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Moved to tsl"},"0.0.5":{"name":"@arnaud-barre/type-lint","version":"0.0.5","author":{"url":"https://github.com/ArnaudBarre","name":"Arnaud Barré"},"license":"MIT","_id":"@arnaud-barre/type-lint@0.0.5","maintainers":[{"name":"arnaud-barre","email":"arnaud.barre72@gmail.com"}],"homepage":"https://github.com/ArnaudBarre/type-lint#readme","bugs":{"url":"https://github.com/ArnaudBarre/type-lint/issues"},"bin":{"type-lint":"cli-entrypoint.js"},"dist":{"shasum":"5e17f22f584064896863160158804319a7781634","tarball":"https://registry.npmjs.org/@arnaud-barre/type-lint/-/type-lint-0.0.5.tgz","fileCount":17,"integrity":"sha512-1oBG/9Iu/tCM3//1fk7LsMfxstrRPgBgA7zQ1qPAkZ4dJt5Magrx0N60F6d5XMM8T3z3aEiv0wB2EM5ZdTIAFg==","signatures":[{"sig":"MEQCICGQHbiWro6e4ByHy4W+2IFZDWgKHNd28DH7SlI+gB7UAiB1YvO982zSTWABMGDaADXZUJ3BV+5dh9wdgRBcLo5PLQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":649425},"type":"module","types":"./index.d.ts","exports":{".":{"types":"./index.d.ts","import":"./index.js"},"./patches":{"types":"./patches/all.d.ts"},"./patches/*":{"types":"./patches/*.d.ts"},"./ruleTester":{"types":"./ruleTester.d.ts","import":"./ruleTester.js"}},"gitHead":"fd6243701c262b8f0c29c627299f44f97756201b","_npmUser":{"name":"arnaud-barre","actor":{"name":"arnaud-barre","type":"user","email":"arnaud.barre72@gmail.com"},"email":"arnaud.barre72@gmail.com"},"repository":{"url":"git+https://github.com/ArnaudBarre/type-lint.git","type":"git"},"_npmVersion":"10.9.2","description":"Modern TypeScript linter","directories":{},"_nodeVersion":"23.11.0","dependencies":{"esbuild":"^0.25.4","ts-api-utils":"^2.1.0"},"_hasShrinkwrap":false,"peerDependencies":{"typescript":"~5.8.3"},"_npmOperationalInternal":{"tmp":"tmp/type-lint_0.0.5_1750202049515_0.24438931276791664","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Moved to tsl"}},"time":{"created":"2024-12-01T22:24:01.558Z","modified":"2025-06-29T08:16:39.599Z","0.0.1":"2024-12-01T22:24:02.068Z","0.0.2":"2025-02-23T21:47:42.842Z","0.0.3":"2025-06-02T01:21:46.423Z","0.0.4":"2025-06-16T01:37:36.321Z","0.0.5":"2025-06-17T23:14:09.798Z"},"bugs":{"url":"https://github.com/ArnaudBarre/type-lint/issues"},"author":{"url":"https://github.com/ArnaudBarre","name":"Arnaud Barré"},"license":"MIT","homepage":"https://github.com/ArnaudBarre/type-lint#readme","repository":{"url":"git+https://github.com/ArnaudBarre/type-lint.git","type":"git"},"description":"Modern TypeScript linter","maintainers":[{"name":"arnaud-barre","email":"arnaud.barre72@gmail.com"}],"readme":"# type-lint [![npm](https://img.shields.io/npm/v/@arnaud-barre/type-lint)](https://www.npmjs.com/package/@arnaud-barre/type-lint)\n\nOngoing work for a modern TypeScript linter\n\n## Features\n\n- Run type-aware rules faster than ESLint\n- Type safe config with custom rules in TypeScript\n- No [IDE caching issue](https://typescript-eslint.io/troubleshooting/faqs/general/#changes-to-one-file-are-not-reflected-when-linting-other-files-in-my-ide)\n\n## Type-aware linting\n\nMost lint rules only rely on the structure (AST) to detect issues. On top of that, type-aware linting leverage type information, such as the types of variables or return values, to detect new issues. Here are two of the most common issues that type-aware linting can detect:\n\n```ts\nasync function foo() {\n  try {\n    doAsyncWork(); // <- Missing await\n  } catch {\n    // This will never run, even if doAsyncWork() throws\n    return getDefault();\n  }\n}\n```\n\n```ts\ntype User = { firstName: string | null };\n\nfunction greet(user: User) {\n  console.log(`Hello ${user.firstName}`);\n}\n\ngreet({ firstName: null }); // logs \"Hello null\"\n```\n\nTo know if you are using type-aware linting in ESLint, see if `@typescript-eslint/parser` is installed and if you have `project` or `projectService` in `languageOptions.parserOptions` in your config.\n\n## Philosophy\n\ntype-lint is like an extension of tsc, where you can create rules that generates reports that are displayed alongside the built-in type errors. No file resolution, no files/includes glob, no parsing: simply take all the files from a TS config and run the rules on the AST generated by TS.\n\nThis means that editor support can be provided though a compiler plugin and that, like TS errors, there is no auto-fix and only suggestions are available.\n\nI don't think this scale well to hundreds of rules. Most lint rules don't require type information and tools written in Rust like [Oxlint](https://oxc.rs/docs/guide/usage/linter.html) or [Biome](https://biomejs.dev/) are really well suited for this.\n\n### What about [tsgo](https://github.com/microsoft/typescript-go)?\n\nI still have to investigate how the language service integration will work with inter-process communication. Probably most of the main rules will have to be ported to Go to be efficient, but this having them ported to the TS AST is still a necessary useful step.\n\n### What about [Biome](https://biomejs.dev/)?\n\nBiome 2 introduced a first type-aware lint rule (noFloatingPromises) without using the TypeScript compiler. While I agree that detecting if a function returns a promise doens't require full type information, various type aware rules require to know if a variable is nullable or not (restrictedTemplateExpressions, restrictedPlusOperands) and this will be hard to implement without the TypeScript compiler for web codebases where most types come from the DB and are resolved though this kind of types\n\n<details>\n\n<summary>Extract of Prisma types</summary>\n\n```ts\nexport declare type GetPayloadResult<\n  Base extends Record<any, any>,\n  R extends InternalArgs[\"result\"][string],\n> = Omit<Base, GetPayloadResultExtensionKeys<R>>\n  & GetPayloadResultExtensionObject<R>;\n\nexport declare type GetPayloadResultExtensionKeys<\n  R extends InternalArgs[\"result\"][string],\n  KR extends keyof R = string extends keyof R ? never : keyof R,\n> = KR;\n\nexport declare type GetPayloadResultExtensionObject<\n  R extends InternalArgs[\"result\"][string],\n> = {\n  [K in GetPayloadResultExtensionKeys<R>]: R[K] extends () => {\n    compute: (...args: any) => infer C;\n  }\n    ? C\n    : never;\n};\n\nexport type $UserPayload<\n  ExtArgs extends $Extensions.InternalArgs = $Extensions.DefaultArgs,\n> = {\n  name: \"User\";\n  objects: {\n    analytics: Prisma.$AnalyticsEventPayload<ExtArgs>[];\n  };\n  scalars: $Extensions.GetPayloadResult<\n    {\n      uuid: string;\n      createdAt: Date;\n      updatedAt: Date;\n      username: string;\n      firstName: string | null;\n      lastName: string | null;\n    },\n    ExtArgs[\"result\"][\"user\"]\n  >;\n  composites: {};\n};\n\ntype UserGetPayload<S extends boolean | null | undefined | UserDefaultArgs> =\n  $Result.GetResult<Prisma.$UserPayload, S>;\n\n// prettier-ignore\nexport interface UserDelegate<ExtArgs extends $Extensions.InternalArgs = $Extensions.DefaultArgs, ClientOptions = {}> {\n  [K: symbol]: { types: Prisma.TypeMap<ExtArgs>['model']['User'], meta: { name: 'User' } }\n  findUnique<T extends UserFindUniqueArgs>(args: SelectSubset<T, UserFindUniqueArgs<ExtArgs>>): Prisma__UserClient<$Result.GetResult<Prisma.$UserPayload<ExtArgs>, T,\"findUnique\", ClientOptions> | null, null, ExtArgs, ClientOptions>\n  findUniqueOrThrow<T extends UserFindUniqueOrThrowArgs>(args: SelectSubset<T, UserFindUniqueOrThrowArgs<ExtArgs>>): Prisma__UserClient<$Result.GetResult<Prisma.$UserPayload<ExtArgs>, T, \"findUniqueOrThrow\", ClientOptions>, never, ExtArgs, ClientOptions>\n  findFirst<T extends UserFindFirstArgs>(args?: SelectSubset<T, UserFindFirstArgs<ExtArgs>>): Prisma__UserClient<$Result.GetResult<Prisma.$UserPayload<ExtArgs>, T,\"findFirst\", ClientOptions> | null, null, ExtArgs, ClientOptions>\n  findFirstOrThrow<T extends UserFindFirstOrThrowArgs>(args?: SelectSubset<T, UserFindFirstOrThrowArgs<ExtArgs>>): Prisma__UserClient<$Result.GetResult<Prisma.$UserPayload<ExtArgs>, T, \"findFirstOrThrow\", ClientOptions>, never, ExtArgs, ClientOptions>\n  findMany<T extends UserFindManyArgs>(args?: SelectSubset<T, UserFindManyArgs<ExtArgs>>): Prisma.PrismaPromise<$Result.GetResult<Prisma.$UserPayload<ExtArgs>, T,\"findMany\", ClientOptions>>\n  create<T extends UserCreateArgs>(args: SelectSubset<T, UserCreateArgs<ExtArgs>>): Prisma__UserClient<$Result.GetResult<Prisma.$UserPayload<ExtArgs>, T, \"create\", ClientOptions>, never, ExtArgs, ClientOptions>\n  createMany<T extends UserCreateManyArgs>(args?: SelectSubset<T, UserCreateManyArgs<ExtArgs>>): Prisma.PrismaPromise<BatchPayload>\n  // More methods...\n}\n```\n\n</details>\n\n## Installation\n\n```bash\nbun add @arnaud-barre/type-lint\n```\n\n### Add a configuration\n\nAdd a `type-lint.config.ts` file to your project root. If you don't have one, all core rules are enabled.\n\nYou can either enable all core rules and disable some of them or update options if needed,\n\n```ts\nimport { core, defineConfig } from \"@arnaud-barre/type-lint\";\n\nexport default defineConfig({\n  rules: [\n    ...core.all(),\n    core.noUnnecessaryBooleanLiteralCompare(\"off\"),\n    core.switchExhaustivenessCheck({\n      considerDefaultExhaustiveForUnions: true,\n    }),\n  ],\n});\n```\n\nor pick only the rules you want to enable.\n\n```ts\nimport { core, defineConfig } from \"@arnaud-barre/type-lint\";\n\nexport default defineConfig({\n  rules: [\n    core.noFloatingPromises(),\n    core.noForInArray(),\n    core.preferOptionalChain(),\n    core.switchExhaustivenessCheck({\n      considerDefaultExhaustiveForUnions: true,\n    }),\n  ],\n});\n```\n\n### Add the TypeScript plugin\n\nInstead of developing multiple plugins for each IDE, type-lint provides a compiler plugin that can be used to display diagnostics in the editor and provide suggestions.\n\nIn your `tsconfig.json` add the following:\n\n```json\n{\n  \"compilerOptions\": {\n    \"plugins\": [{ \"name\": \"@arnaud-barre/type-lint/plugin\" }]\n  }\n}\n```\n\n> [!IMPORTANT]\n> If you use VS Code, you need to run the \"TypeScript: Select TypeScript Version\" command and choose \"Use Workspace Version\".\n\n### Add the patches\n\nRules like [prefer-promise-reject-errors](https://typescript-eslint.io/rules/prefer-promise-reject-errors/) and [use-unknown-in-catch-callback-variable](https://typescript-eslint.io/rules/use-unknown-in-catch-callback-variable/) in TS-ESLint are implemented with patches to override the builtin types. To do so, add the following to your `tsconfig.json`:\n\n```json\n{\n  \"compilerOptions\": {\n    \"types\": [\"@arnaud-barre/type-lint/patches\"]\n  }\n}\n```\n\nThey can also be added individually with `@arnaud-barre/type-lint/patches/promiseRejectError` or `@arnaud-barre/type-lint/patches/unknowninCatchCallbacks`.\n\n### Update your scripts to run the linter\n\nTo avoid parsing and typechecking twice your codebase, type-lint run first tsc via the javascript API and then run rules on the TS AST.\n\n```diff\n{\n  \"scripts\": {\n-    \"typecheck\": \"tsc\"\n+    \"typecheck\": \"type-lint\"\n  }\n}\n```\n\n#### Can I use it without the tsc wrapper?\n\nYes, you can run `type-lint --lint-only` directly. But if you use rules that request type information for a lot of nodes, this can be very inefficient with you run one after the other. As an example, running on my work codebase (~1k TS files), `tsc` take 17s, `type-lint --lint-only` take 17s, and `type-lint` (doing both) take 21s.\n\n### Update your CI\n\n```diff\n-  - run: bun tsc\n+  - run: bun type-lint\n```\n\n### Run for another tsconfig\n\n```sh\ntype-lint -p tsconfig.test.json\ntype-lint --project tsconfig.test.json\n```\n\n## Configuration\n\n### ignore\n\nEverything in node_modules is ignored by default. If you want to ignore generated code, you can use the `ignore` option. The current implementation is simply `ignore.some((i) => path.includes(i))`. By design, files that are excluded in the TS config will not be linted.\n\n```ts\ndefineConfig({\n  ignore: [\"src/generated/\"],\n});\n```\n\n### diagnosticCategory\n\nTo differentiate type-lint reports from TS errors, type-lint diagnostics are reported by default as warnings. If you prefer having only red squiggles, you can set the `diagnosticCategory` option to `\"error\"`.\n\n```ts\ndefineConfig({\n  diagnosticCategory: \"error\",\n});\n```\n\n### overrides\n\nIf for some subset of files you want to enable or disable rules, you can use the `overrides` option like this:\n\n```ts\ndefineConfig({\n  overrides: [\n    {\n      files: [\".server.ts\"],\n      rules: [\n        core.dotNotation(\"off\"),\n        core.switchExhaustivenessCheck({ requireDefaultForNonUnion: true }),\n      ],\n    },\n  ],\n});\n```\n\nLike the ignore option, the `files` option test for inclusion against the file path.\n\nRedeclared rules (identical name) completely replace the \"base\" rule, there is no merging of options.\n\n## Custom rules\n\nWriting custom rules is part of the core value of type-lint.\n\nRules run on the TS AST, which is less known than ESTree but allows to query type information for a given node with `context.checker.getTypeAtLocation(node)`. Use [ast-explorer.dev](https://ast-explorer.dev/#eNo9zEEKwjAQheGrxLdSKG5ctV5AcO9qNiEdQmSYCUlVpPTuTSl0+72fN0Mw4O2/voaS8oQOucH0z3xAaBBMqwlfxeKZ8GARcz8rMp4Ilztpy6xlM6lzBPEaPz7yi0tNpoTB9X23b/vtM+m48Y10wbIChCAraw==) to explore the AST.\n\nBy default, the TS AST is, funny enough, poorly typed. That's why the linter ships rewritten AST types that you can import and allow for type narrowing and exhaustive switches.\n\nTo help build rules, a few common utils are available on `context.utils`, and `context.checker` is overridden with some type changes. If you need to pass the checker to another library that expect the builtin TypeChecker type, like [ts-api-utils](https://github.com/JoshuaKGoldberg/ts-api-utils), you can use `context.rawChecker`.\n\n```ts\nimport { type AST, core, defineConfig } from \"@arnaud-barre/type-lint\";\nimport { SyntaxKind } from \"typescript\";\n\nexport default defineConfig({\n  rules: [\n    ...core.all(),\n    {\n      name: \"org/useLogger\",\n      visitor: {\n        CallExpression(node, context) {\n          if (\n            node.expression.kind === SyntaxKind.PropertyAccessExpression\n            && node.expression.expression.kind === SyntaxKind.Identifier\n            && node.expression.expression.text === \"console\"\n          ) {\n            node.expression.expression satisfies AST.Identifier;\n            context.report({ message: \"Use logger instead\", node });\n          }\n        },\n      },\n    },\n  ],\n});\n```\n\n## Core rules\n\nCurrently, the list of core rules are the type-aware lint rules I use from TS-ESLint. If you think more rules should be added, please open an issue, but to reduce the surface, only non-styling type-aware rules will be accepted. Here is the list of [TS-ESLint type aware rules](https://typescript-eslint.io/rules/?=typeInformation) with their status:\n\n- await-thenable: ✅ Implemented\n- consistent-return: 🛑 Implementation not planned, you can use `noImplicitReturns` compilerOption\n- consistent-type-exports: 🛑 Implementation not planned, you can use `verbatimModuleSyntax` compilerOption\n- dot-notation: ✅ Implemented without options\n- naming-convention: Styling is out of core\n- no-array-delete: ✅ Implemented\n- no-base-to-string: ✅ Implemented, only `String()`, `.to(Locale)String()` and `.join()` are checked, see `restrict-plus-operands` and `restrict-template-expressions` for other checks\n- no-confusing-void-expression: ✅ Implemented\n- no-deprecated: ❌ Not implemented\n- no-duplicate-type-constituents: 🗑️ Merged with `no-redundant-type-constituents`\n- no-floating-promises: ✅ Implemented, allowList is named based only\n- no-for-in-array: ✅ Implemented\n- no-implied-eval: ✅ Implemented, do not check for global shadowing\n- no-meaningless-void-operator: ✅ Implemented\n- no-misused-promises: ✅ Implemented\n- no-misused-spread: ✅ Implemented, no allow option\n- no-mixed-enums: 🛑 TS only concepts are out of core\n- no-redundant-type-constituents: ✅ Implemented and smarter thanks to `checker.isTypeAssignableTo`\n- no-unnecessary-boolean-literal-compare: ✅ Implemented\n- no-unnecessary-condition: ✅ Implemented\n- no-unnecessary-qualifier: TS only concepts are out of core\n- no-unnecessary-template-expression: ✅ Implemented\n- no-unnecessary-type-arguments: ✅ Implemented\n- no-unnecessary-type-assertion: ✅ Implemented\n- no-unnecessary-type-conversion: ✅ Implemented\n- no-unnecessary-type-parameters: ❌ Not implemented, IMO report too many legitimate cases\n- no-unsafe-argument: ❌ Not implemented, too noisy for me\n- no-unsafe-assignment: ❌ Not implemented, too noisy for me\n- no-unsafe-call: ❌ Not implemented, too noisy for me\n- no-unsafe-enum-comparison: 🛑 TS only concepts are out of core\n- no-unsafe-member-access: ❌ Not implemented, too noisy for me\n- no-unsafe-return: ❌ Not implemented, too noisy for me\n- no-unsafe-type-assertion: ❌ Not implemented, too noisy for me\n- no-unsafe-unary-minus: ✅ Implemented\n- non-nullable-type-assertion-style: ✅ Implemented\n- only-throw-error: ✅ Implemented, allow options is named based only\n- prefer-destructuring: Styling is out of core\n- prefer-find: ✅ Implemented\n- prefer-includes: ✅ Implemented, without `/baz/.test(a)`, it requires regex parsing and can be achieved without type information\n- prefer-nullish-coalescing: ✅ Implemented\n- prefer-optional-chain: ✅ Implemented\n- prefer-promise-reject-errors: 🛑 See [Add the patches](#add-the-patches)\n- prefer-readonly: ❌ Not implemented, too OOP for me\n- prefer-readonly-parameter-types: 🛑 Implementation not planned, it would better to check that function parameters are never mutated instead\n- prefer-reduce-type-parameter: ✅ Implemented,\n- prefer-regexp-exec: 🛑 Small runtime optimization are out of core\n- prefer-return-this-type: ✅ Implemented\n- prefer-string-starts-ends-with: ✅ Implemented\n- promise-function-async: ❌ Not implemented\n- related-getter-setter-pairs: ❌ Not implemented, too OOP for me\n- require-array-sort-compare: ❌ Not implemented\n- require-await: 🛑 Implementation not planned, type information to handle async generators, which is a niche case\n- restrict-plus-operands: ✅ Implemented with stricter defaults, always lint assignment\n- restrict-template-expressions: ✅ Implemented, with stricter defaults\n- return-await: ✅ Implemented, only support always, remove unneeded await handled by await-thenable\n- strict-boolean-expressions: ❌ Not implemented\n- switch-exhaustiveness-check: ✅ Implemented, missing no default comment #10218\n- unbound-method: ❌ Not implemented, too OOP for me\n- use-unknown-in-catch-callback-variable: 🛑 See [Add the patches](#add-the-patches)\n\n## Create sharable rules\n\nThis is a work in progress, but if you are interested into publishing rules, you can use `defineRule` and `createRulesSet` that are used for the core rules.\n","readmeFilename":"README.md"}