Created
April 28, 2024 22:11
-
-
Save tbjgolden/9fdfc826973b3aaa11cc8282b8144603 to your computer and use it in GitHub Desktop.
eslint config (wip)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import eslintJs from "@eslint/js"; | |
import typescriptEslintPlugin from "@typescript-eslint/eslint-plugin"; | |
import typescriptEslintParser from "@typescript-eslint/parser"; | |
import disableRulesThatClashWithPrettierConfig from "eslint-config-prettier"; | |
import eslintConfigXo from "eslint-config-xo"; | |
import eslintPluginReact from "eslint-plugin-react"; | |
import reactRecommended from "eslint-plugin-react/configs/recommended.js"; | |
import eslintPluginReactHooks from "eslint-plugin-react-hooks"; | |
import simpleImportSort from "eslint-plugin-simple-import-sort"; | |
import eslintPluginUnicorn from "eslint-plugin-unicorn"; | |
const tsSourceFiles = ["**/*.ts", "**/*.cts", "**/*.mts", "**/*.tsx"]; | |
const allSourceFiles = ["**/*.js", "**/*.cjs", "**/*.mjs", ...tsSourceFiles]; | |
/** @type {import('eslint').Linter.FlatConfig[]} */ | |
const config = [ | |
{ | |
name: "eslint recommended defaults", | |
files: allSourceFiles, | |
rules: eslintJs.configs.recommended.rules, | |
}, | |
{ | |
name: "@typescript-eslint defaults, for JavaScript-like files", | |
files: allSourceFiles, | |
linterOptions: { | |
reportUnusedDisableDirectives: true, | |
}, | |
rules: { | |
"constructor-super": "error", | |
"for-direction": "error", | |
"getter-return": "error", | |
"no-async-promise-executor": "error", | |
"no-case-declarations": "error", | |
"no-class-assign": "error", | |
"no-compare-neg-zero": "error", | |
"no-cond-assign": "error", | |
"no-const-assign": "error", | |
"no-constant-binary-expression": "error", | |
"no-constant-condition": "error", | |
"no-control-regex": "error", | |
"no-debugger": "error", | |
"no-delete-var": "error", | |
"no-dupe-args": "error", | |
"no-dupe-class-members": "error", | |
"no-dupe-else-if": "error", | |
"no-dupe-keys": "error", | |
"no-duplicate-case": "error", | |
"no-empty": "error", | |
"no-empty-character-class": "error", | |
"no-empty-pattern": "error", | |
"no-empty-static-block": "error", | |
"no-ex-assign": "error", | |
"no-extra-boolean-cast": "error", | |
"no-fallthrough": "error", | |
"no-func-assign": "error", | |
"no-global-assign": "error", | |
"no-import-assign": "error", | |
"no-invalid-regexp": "error", | |
"no-irregular-whitespace": "error", | |
"no-loss-of-precision": "error", | |
"no-misleading-character-class": "error", | |
"no-new-native-nonconstructor": "error", | |
"no-nonoctal-decimal-escape": "error", | |
"no-obj-calls": "error", | |
"no-octal": "error", | |
"no-prototype-builtins": "error", | |
"no-redeclare": "error", | |
"no-regex-spaces": "error", | |
"no-self-assign": "error", | |
"no-setter-return": "error", | |
"no-shadow-restricted-names": "error", | |
"no-sparse-arrays": "error", | |
"no-this-before-super": "error", | |
"no-undef": "error", | |
"no-unexpected-multiline": "error", | |
"no-unreachable": "error", | |
"no-unsafe-finally": "error", | |
"no-unsafe-negation": "error", | |
"no-unsafe-optional-chaining": "error", | |
"no-unused-labels": "error", | |
"no-unused-private-class-members": "error", | |
"no-unused-vars": "error", | |
"no-useless-backreference": "error", | |
"no-useless-catch": "error", | |
"no-useless-escape": "error", | |
"no-with": "error", | |
"require-yield": "error", | |
"use-isnan": "error", | |
"valid-typeof": "error", | |
}, | |
}, | |
{ | |
name: "xo defaults", | |
files: allSourceFiles, | |
rules: eslintConfigXo.rules, | |
}, | |
{ | |
name: "@typescript-eslint defaults, for TypeScript-like files", | |
files: tsSourceFiles, | |
languageOptions: { | |
parser: typescriptEslintParser, | |
parserOptions: { | |
warnOnUnsupportedTypeScriptVersion: false, | |
sourceType: "module", | |
project: "tsconfig.json", | |
ecmaFeatures: { jsx: true }, | |
}, | |
sourceType: "module", | |
}, | |
plugins: { "@typescript-eslint": typescriptEslintPlugin }, | |
rules: { | |
"constructor-super": "off", | |
"getter-return": "off", | |
"no-const-assign": "off", | |
"no-dupe-args": "off", | |
"no-dupe-class-members": "off", | |
"no-dupe-keys": "off", | |
"no-func-assign": "off", | |
"no-import-assign": "off", | |
"no-new-symbol": "off", | |
"no-new-native-nonconstructor": "off", | |
"no-obj-calls": "off", | |
"no-redeclare": "off", | |
"no-setter-return": "off", | |
"no-this-before-super": "off", | |
"no-undef": "off", | |
"no-unreachable": "off", | |
"no-unsafe-negation": "off", | |
"no-var": "error", | |
"prefer-const": "error", | |
"prefer-rest-params": "error", | |
"prefer-spread": "error", | |
"@typescript-eslint/ban-ts-comment": "error", | |
"@typescript-eslint/ban-types": "error", | |
"no-array-constructor": "off", | |
"@typescript-eslint/no-array-constructor": "error", | |
"@typescript-eslint/no-duplicate-enum-values": "error", | |
"@typescript-eslint/no-explicit-any": "error", | |
"@typescript-eslint/no-extra-non-null-assertion": "error", | |
"no-loss-of-precision": "off", | |
"@typescript-eslint/no-loss-of-precision": "error", | |
"@typescript-eslint/no-misused-new": "error", | |
"@typescript-eslint/no-namespace": "error", | |
"@typescript-eslint/no-non-null-asserted-optional-chain": "error", | |
"@typescript-eslint/no-this-alias": "error", | |
"@typescript-eslint/no-unnecessary-type-constraint": "error", | |
"@typescript-eslint/no-unsafe-declaration-merging": "error", | |
"no-unused-vars": "off", | |
"@typescript-eslint/no-unused-vars": "error", | |
"@typescript-eslint/no-var-requires": "error", | |
"@typescript-eslint/prefer-as-const": "error", | |
"@typescript-eslint/triple-slash-reference": "error", | |
}, | |
}, | |
{ | |
name: "react defaults", | |
files: ["**/*.tsx"], | |
plugins: { react: eslintPluginReact }, | |
rules: reactRecommended.rules, | |
}, | |
{ | |
name: "react-hooks defaults", | |
files: ["**/*.tsx"], | |
plugins: { "react-hooks": eslintPluginReactHooks }, | |
rules: { | |
"react-hooks/exhaustive-deps": "error", | |
"react-hooks/rules-of-hooks": "error", | |
}, | |
}, | |
{ | |
name: "xo-typescript defaults", | |
files: tsSourceFiles, | |
// Inlined as it's not exported in a flat config friendly format | |
rules: { | |
"@typescript-eslint/adjacent-overload-signatures": "error", | |
"@typescript-eslint/array-type": [ | |
"error", | |
{ | |
default: "array-simple", | |
}, | |
], | |
"@typescript-eslint/await-thenable": "error", | |
"@typescript-eslint/ban-ts-comment": [ | |
"error", | |
{ | |
"ts-expect-error": "allow-with-description", | |
minimumDescriptionLength: 4, | |
}, | |
], | |
"@typescript-eslint/ban-tslint-comment": "error", | |
"@typescript-eslint/ban-types": [ | |
"error", | |
{ | |
extendDefaults: false, | |
types: { | |
String: { | |
message: "Use `string` instead.", | |
fixWith: "string", | |
}, | |
Number: { | |
message: "Use `number` instead.", | |
fixWith: "number", | |
}, | |
Boolean: { | |
message: "Use `boolean` instead.", | |
fixWith: "boolean", | |
}, | |
Symbol: { | |
message: "Use `symbol` instead.", | |
fixWith: "symbol", | |
}, | |
BigInt: { | |
message: "Use `bigint` instead.", | |
fixWith: "bigint", | |
}, | |
Object: { | |
message: | |
"The `Object` type is mostly the same as `unknown`. You probably want `Record<string, unknown>` instead. See https://github.com/typescript-eslint/typescript-eslint/pull/848", | |
fixWith: "Record<string, unknown>", | |
}, | |
"{}": { | |
message: | |
"The `{}` type is mostly the same as `unknown`. You probably want `Record<string, unknown>` instead.", | |
fixWith: "Record<string, unknown>", | |
}, | |
object: { | |
message: | |
"The `object` type is hard to use. Use `Record<string, unknown>` instead. See: https://github.com/typescript-eslint/typescript-eslint/pull/848", | |
fixWith: "Record<string, unknown>", | |
}, | |
Function: "Use a specific function type instead, like `() => void`.", | |
null: { | |
message: | |
"Use `undefined` instead. See: https://github.com/sindresorhus/meta/issues/7", | |
fixWith: "undefined", | |
}, | |
Buffer: { | |
message: | |
"Use Uint8Array instead. See: https://sindresorhus.com/blog/goodbye-nodejs-buffer", | |
suggest: ["Uint8Array"], | |
}, | |
"[]": "Don't use the empty array type `[]`. It only allows empty arrays. Use `SomeType[]` instead.", | |
"[[]]": | |
"Don't use `[[]]`. It only allows an array with a single element which is an empty array. Use `SomeType[][]` instead.", | |
"[[[]]]": "Don't use `[[[]]]`. Use `SomeType[][][]` instead.", | |
"[[[[]]]]": "ur drunk 🤡", | |
"[[[[[]]]]]": "🦄💥", | |
}, | |
}, | |
], | |
"@typescript-eslint/class-literal-property-style": ["error", "getters"], | |
"@typescript-eslint/consistent-generic-constructors": ["error", "constructor"], | |
"@typescript-eslint/consistent-indexed-object-style": "error", | |
"brace-style": "off", | |
"@typescript-eslint/brace-style": [ | |
"error", | |
"1tbs", | |
{ | |
allowSingleLine: false, | |
}, | |
], | |
"comma-dangle": "off", | |
"@typescript-eslint/comma-dangle": ["error", "always-multiline"], | |
"comma-spacing": "off", | |
"@typescript-eslint/comma-spacing": [ | |
"error", | |
{ | |
before: false, | |
after: true, | |
}, | |
], | |
"default-param-last": "off", | |
"@typescript-eslint/default-param-last": "error", | |
"dot-notation": "off", | |
"@typescript-eslint/dot-notation": "error", | |
"@typescript-eslint/consistent-type-assertions": [ | |
"error", | |
{ | |
assertionStyle: "as", | |
objectLiteralTypeAssertions: "allow-as-parameter", | |
}, | |
], | |
"@typescript-eslint/consistent-type-definitions": ["error", "type"], | |
"@typescript-eslint/consistent-type-exports": [ | |
"error", | |
{ | |
fixMixedExportsWithInlineTypeSpecifier: true, | |
}, | |
], | |
"@typescript-eslint/consistent-type-imports": [ | |
"error", | |
{ | |
fixStyle: "inline-type-imports", | |
}, | |
], | |
// Disabled because it's too annoying. Enable it when it's more mature, smarter, and more flexible. | |
// https://github.com/typescript-eslint/typescript-eslint/search?q=%22explicit-function-return-type%22&state=open&type=Issues | |
// '@typescript-eslint/explicit-function-return-type': [ | |
// 'error', | |
// { | |
// allowExpressions: true, | |
// allowTypedFunctionExpressions: true, | |
// allowHigherOrderFunctions: true, | |
// allowConciseArrowFunctionExpressionsStartingWithVoid: false, | |
// allowIIFE: true | |
// } | |
// ], | |
// TODO: This rule should be removed if/when we enable `@typescript-eslint/explicit-function-return-type`. | |
// Disabled for now as it has too many false-positives. | |
// https://github.com/typescript-eslint/typescript-eslint/search?q=%22explicit-module-boundary-types%22&state=open&type=Issues | |
// '@typescript-eslint/explicit-module-boundary-types': [ | |
// 'error', | |
// { | |
// allowTypedFunctionExpressions: true, | |
// allowHigherOrderFunctions: true, | |
// allowDirectConstAssertionInArrowFunctions: true, | |
// shouldTrackReferences: true | |
// } | |
// ], | |
"func-call-spacing": "off", | |
"@typescript-eslint/func-call-spacing": ["error", "never"], | |
indent: "off", | |
"@typescript-eslint/indent": [ | |
"error", | |
"tab", | |
{ | |
SwitchCase: 1, | |
}, | |
], | |
"keyword-spacing": "off", | |
"@typescript-eslint/keyword-spacing": "error", | |
"lines-between-class-members": "off", | |
"@typescript-eslint/lines-between-class-members": [ | |
"error", | |
"always", | |
{ | |
// Workaround to allow class fields to not have lines between them. | |
// TODO: Get ESLint to add an option to ignore class fields. | |
exceptAfterSingleLine: true, | |
}, | |
], | |
"@typescript-eslint/member-delimiter-style": [ | |
"error", | |
{ | |
multiline: { | |
delimiter: "semi", | |
requireLast: true, | |
}, | |
singleline: { | |
delimiter: "semi", | |
requireLast: false, | |
}, | |
}, | |
], | |
"@typescript-eslint/member-ordering": [ | |
"error", | |
{ | |
default: [ | |
"signature", | |
"public-static-field", | |
"public-static-method", | |
"protected-static-field", | |
"protected-static-method", | |
"private-static-field", | |
"private-static-method", | |
"static-field", | |
"static-method", | |
"public-decorated-field", | |
"public-instance-field", | |
"public-abstract-field", | |
"public-field", | |
"protected-decorated-field", | |
"protected-instance-field", | |
"protected-abstract-field", | |
"protected-field", | |
"private-decorated-field", | |
"private-instance-field", | |
"private-field", | |
"instance-field", | |
"abstract-field", | |
"decorated-field", | |
"field", | |
"public-constructor", | |
"protected-constructor", | |
"private-constructor", | |
"constructor", | |
"public-decorated-method", | |
"public-instance-method", | |
"public-abstract-method", | |
"public-method", | |
"protected-decorated-method", | |
"protected-instance-method", | |
"protected-abstract-method", | |
"protected-method", | |
"private-decorated-method", | |
"private-instance-method", | |
"private-method", | |
"instance-method", | |
"abstract-method", | |
"decorated-method", | |
"method", | |
], | |
}, | |
], | |
// Disabled for now as it causes too many weird TypeScript issues. I'm not sure whether the problems are caused by bugs in TS or problems in my types. | |
// TODO: Try to re-enable this again in 2026. | |
// '@typescript-eslint/method-signature-style': 'error', | |
// We use `@typescript-eslint/naming-convention` in favor of `camelcase`. | |
camelcase: "off", | |
// Known issues: | |
// - https://github.com/typescript-eslint/typescript-eslint/issues/1485 | |
// - https://github.com/typescript-eslint/typescript-eslint/issues/1484 | |
// TODO: Prevent `_` prefix on private fields when TypeScript 3.8 is out. | |
...getNamingConventionRule({ isTsx: false }), | |
"@typescript-eslint/no-base-to-string": "error", | |
"no-array-constructor": "off", | |
"@typescript-eslint/no-array-constructor": "error", | |
"@typescript-eslint/no-array-delete": "error", | |
"no-dupe-class-members": "off", | |
"@typescript-eslint/no-dupe-class-members": "error", | |
"@typescript-eslint/no-confusing-void-expression": "error", | |
"@typescript-eslint/no-duplicate-enum-values": "error", | |
"@typescript-eslint/no-duplicate-type-constituents": "error", | |
"@typescript-eslint/no-dynamic-delete": "error", | |
"no-empty-function": "off", | |
"@typescript-eslint/no-empty-function": "error", | |
"@typescript-eslint/no-empty-interface": [ | |
"error", | |
{ | |
allowSingleExtends: true, | |
}, | |
], | |
// TODO: Try to enable this again in 2025. | |
// Disabled for now. This is a great rule. It's just that TypeScript is not good enough yet to not use `any` in many places. | |
// For example: https://github.com/sindresorhus/refined-github/pull/2391#discussion_r318995182 | |
// '@typescript-eslint/no-explicit-any': [ | |
// 'error', | |
// { | |
// fixToUnknown: true, | |
// ignoreRestArgs: true | |
// } | |
// ], | |
"@typescript-eslint/no-extra-non-null-assertion": "error", | |
// Disabled because it's buggy. It transforms `...(personalToken ? {Authorization: `token ${personalToken}`} : {})` into `...personalToken ? {Authorization: `token ${personalToken}`} : {}` which is not valid. | |
// https://github.com/typescript-eslint/typescript-eslint/search?q=%22no-extra-parens%22&state=open&type=Issues | |
"no-extra-parens": "off", | |
// '@typescript-eslint/no-extra-parens': [ | |
// 'error', | |
// 'all', | |
// { | |
// conditionalAssign: false, | |
// nestedBinaryExpressions: false, | |
// ignoreJSX: 'multi-line' | |
// } | |
// ], | |
"no-extra-semi": "off", | |
"@typescript-eslint/no-extra-semi": "error", | |
"no-loop-func": "off", | |
"@typescript-eslint/no-loop-func": "error", | |
"no-loss-of-precision": "off", | |
"@typescript-eslint/no-loss-of-precision": "error", | |
"@typescript-eslint/no-extraneous-class": [ | |
"error", | |
{ | |
allowConstructorOnly: false, | |
allowEmpty: false, | |
allowStaticOnly: false, | |
allowWithDecorator: true, | |
}, | |
], | |
"no-void": [ | |
"error", | |
{ | |
allowAsStatement: true, // To allow `ignoreVoid` in `@typescript-eslint/no-floating-promises` | |
}, | |
], | |
"@typescript-eslint/no-floating-promises": [ | |
"error", | |
{ | |
ignoreVoid: true, // Prepend a function call with `void` to mark it as not needing to be await'ed, which silences this rule. | |
ignoreIIFE: true, | |
}, | |
], | |
"@typescript-eslint/no-for-in-array": "error", | |
"@typescript-eslint/no-inferrable-types": "error", | |
// Disabled for now as it has too many false-positives. | |
// '@typescript-eslint/no-invalid-void-type': 'error', | |
"@typescript-eslint/no-meaningless-void-operator": "error", | |
"@typescript-eslint/no-misused-new": "error", | |
"@typescript-eslint/no-misused-promises": [ | |
"error", | |
{ | |
checksConditionals: true, | |
// TODO: I really want this to be `true`, but it makes it inconvenient to use | |
// async functions as event handlers... I need to find a good way to handle that. | |
// https://github.com/sindresorhus/refined-github/pull/2391#discussion_r318990466 | |
checksVoidReturn: false, | |
}, | |
], | |
"@typescript-eslint/no-namespace": "error", | |
"@typescript-eslint/no-non-null-asserted-nullish-coalescing": "error", | |
"@typescript-eslint/no-non-null-asserted-optional-chain": "error", | |
// Disabled for now. There are just too many places where you need to use it because of incorrect types, for example, the Node.js types. | |
// TODO: Try to enable this again in 2023. | |
// '@typescript-eslint/no-non-null-assertion': 'error', | |
"no-redeclare": "off", | |
"@typescript-eslint/no-redeclare": "error", | |
"no-restricted-imports": "off", | |
"@typescript-eslint/no-restricted-imports": [ | |
"error", | |
{ | |
paths: [ | |
"error", | |
"domain", | |
"freelist", | |
"smalloc", | |
"punycode", | |
"sys", | |
"querystring", | |
"colors", | |
], | |
}, | |
], | |
// The rule is buggy and keeps inferring `any` for types that are not `any`. Just a lot of false-positives. | |
// '@typescript-eslint/no-redundant-type-constituents': 'error', | |
"@typescript-eslint/no-require-imports": "error", | |
"@typescript-eslint/no-this-alias": [ | |
"error", | |
{ | |
allowDestructuring: true, | |
}, | |
], | |
"no-throw-literal": "off", | |
"@typescript-eslint/no-throw-literal": [ | |
"error", | |
{ | |
// This should ideally be `false`, but it makes rethrowing errors inconvenient. There should be a separate `allowRethrowingUnknown` option. | |
allowThrowingUnknown: true, | |
allowThrowingAny: false, | |
}, | |
], | |
"@typescript-eslint/no-unnecessary-boolean-literal-compare": "error", | |
// `no-unnecessary-condition` is essentially a stricter version of `no-constant-condition`, but that isn't currently enabled | |
"no-constant-condition": "error", | |
// TODO: Try to enable this again in 2025 *if* the following are resolved: | |
// - https://github.com/microsoft/TypeScript/issues/36393 | |
// - The rule needs a way to ignore runtime type-checks: https://github.com/sindresorhus/refined-github/pull/3168 | |
// - Run the rule on https://github.com/sindresorhus/refined-github and ensure there are no false-positives | |
// | |
// Also related: https://github.com/typescript-eslint/typescript-eslint/issues/1798 | |
// Also disable `no-constant-condition` when this is enabled | |
// '@typescript-eslint/no-unnecessary-condition': 'error', | |
"@typescript-eslint/no-unnecessary-qualifier": "error", | |
"@typescript-eslint/no-unnecessary-type-arguments": "error", | |
"@typescript-eslint/no-unnecessary-type-assertion": "error", | |
"@typescript-eslint/no-unnecessary-type-constraint": "error", | |
"@typescript-eslint/no-unsafe-argument": "error", | |
"@typescript-eslint/no-unsafe-assignment": "error", | |
"@typescript-eslint/no-unsafe-call": "error", | |
"@typescript-eslint/no-unsafe-declaration-merging": "error", | |
"@typescript-eslint/no-unsafe-enum-comparison": "error", | |
// Disabled until TypeScrpt supports the `node:` protocol. | |
// '@typescript-eslint/no-unsafe-member-access': 'error', | |
"@typescript-eslint/no-unsafe-return": "error", | |
"@typescript-eslint/no-useless-empty-export": "error", | |
"no-unused-expressions": "off", | |
"@typescript-eslint/no-unused-expressions": "error", | |
"no-unused-vars": "off", | |
// NOTE: TypeScript already catches unused variables. Let us know if there's something this rule catches that TypeScript does not. | |
// '@typescript-eslint/no-unused-vars': [ | |
// 'error', | |
// { | |
// vars: 'all', | |
// args: 'after-used', | |
// ignoreRestSiblings: true, | |
// argsIgnorePattern: /^_/.source, | |
// caughtErrors: 'all', | |
// caughtErrorsIgnorePattern: /^_$/.source | |
// } | |
// ], | |
"no-useless-constructor": "off", | |
"@typescript-eslint/no-useless-constructor": "error", | |
"object-curly-spacing": "off", | |
"@typescript-eslint/object-curly-spacing": ["error", "never"], | |
"padding-line-between-statements": "off", | |
"@typescript-eslint/padding-line-between-statements": [ | |
"error", | |
{ | |
blankLine: "always", | |
prev: "multiline-block-like", | |
next: "*", | |
}, | |
], | |
"@typescript-eslint/no-var-requires": "error", | |
"@typescript-eslint/non-nullable-type-assertion-style": "error", | |
"@typescript-eslint/parameter-properties": [ | |
"error", | |
{ | |
prefer: "parameter-property", | |
}, | |
], | |
"@typescript-eslint/prefer-as-const": "error", | |
"@typescript-eslint/prefer-find": "error", | |
"@typescript-eslint/prefer-for-of": "error", | |
"@typescript-eslint/prefer-function-type": "error", | |
"@typescript-eslint/prefer-includes": "error", | |
"@typescript-eslint/prefer-literal-enum-member": "error", | |
"@typescript-eslint/prefer-namespace-keyword": "error", | |
"@typescript-eslint/prefer-nullish-coalescing": [ | |
"error", | |
{ | |
ignoreTernaryTests: false, | |
ignoreConditionalTests: false, | |
ignoreMixedLogicalExpressions: false, | |
}, | |
], | |
"@typescript-eslint/prefer-optional-chain": "error", | |
"prefer-promise-reject-errors": "off", | |
"@typescript-eslint/prefer-promise-reject-errors": "error", | |
"@typescript-eslint/prefer-readonly": "error", | |
// TODO: Try to enable this again in 2023. | |
// Disabled for now as it's too annoying and will cause too much churn. It also has bugs: https://github.com/typescript-eslint/typescript-eslint/search?q=%22prefer-readonly-parameter-types%22+is:issue&state=open&type=issues | |
// '@typescript-eslint/prefer-readonly-parameter-types': [ | |
// 'error', | |
// { | |
// checkParameterProperties: true, | |
// ignoreInferredTypes: true | |
// } | |
// ], | |
"@typescript-eslint/prefer-reduce-type-parameter": "error", | |
"@typescript-eslint/prefer-string-starts-ends-with": "error", | |
"@typescript-eslint/prefer-ts-expect-error": "error", | |
"@typescript-eslint/promise-function-async": "error", | |
quotes: "off", | |
"@typescript-eslint/quotes": ["error", "single"], | |
"@typescript-eslint/restrict-plus-operands": [ | |
"error", | |
{ | |
allowAny: false, | |
}, | |
], | |
"@typescript-eslint/restrict-template-expressions": [ | |
"error", | |
{ | |
allowNumber: true, | |
}, | |
], | |
"@typescript-eslint/return-await": "error", | |
"@typescript-eslint/require-array-sort-compare": [ | |
"error", | |
{ | |
ignoreStringArrays: true, | |
}, | |
], | |
// Disabled for now. It's too buggy. It fails to detect when try/catch is used, await inside blocks, etc. It's also common to have async functions without await for various reasons. | |
// 'require-await': 'off', | |
// '@typescript-eslint/require-await': 'error', | |
"space-before-function-paren": "off", | |
"@typescript-eslint/space-before-function-paren": [ | |
"error", | |
{ | |
anonymous: "always", | |
named: "never", | |
asyncArrow: "always", | |
}, | |
], | |
"space-infix-ops": "off", | |
"@typescript-eslint/space-infix-ops": "error", | |
semi: "off", | |
"@typescript-eslint/semi": ["error", "always"], | |
"space-before-blocks": "off", | |
"@typescript-eslint/space-before-blocks": ["error", "always"], | |
// TODO: Reconsider enabling it again in 2023. | |
// NOTE: The rule was complete redone in typescript-eslint v3, so this config needs to be changed before this is enabled. | |
// Disabled for now as it's too strict. | |
// Relevant discussion: https://github.com/sindresorhus/refined-github/pull/2521#discussion_r343013852 | |
// '@typescript-eslint/strict-boolean-expressions': [ | |
// 'error', | |
// { | |
// allowNullable: true, | |
// allowSafe: true | |
// } | |
// ], | |
"default-case": "off", // It conflicts with `@typescript-eslint/switch-exhaustiveness-check`. It would still be nice to have this rule for non-exhaustive switches though. | |
"@typescript-eslint/switch-exhaustiveness-check": [ | |
"error", | |
{ | |
allowDefaultCaseForExhaustiveSwitch: false, | |
requireDefaultForNonUnion: true, | |
}, | |
], | |
"@typescript-eslint/triple-slash-reference": [ | |
"error", | |
{ | |
path: "never", | |
types: "never", | |
lib: "never", | |
}, | |
], | |
"@typescript-eslint/type-annotation-spacing": "error", | |
// Disabled as it crashes on most code. | |
// https://github.com/typescript-eslint/typescript-eslint/search?q=%22unbound-method%22&state=open&type=Issues | |
// '@typescript-eslint/unbound-method': [ | |
// 'error', | |
// { | |
// ignoreStatic: true | |
// } | |
// ], | |
"@typescript-eslint/prefer-regexp-exec": "error", | |
"@typescript-eslint/prefer-return-this-type": "error", | |
"@typescript-eslint/unified-signatures": [ | |
"error", | |
{ | |
ignoreDifferentlyNamedParameters: true, | |
}, | |
], | |
// Disabled per typescript-eslint recommendation: https://github.com/typescript-eslint/typescript-eslint/blob/e26e43ffba96f6d46198b22f1c8dd5c814db2652/docs/getting-started/linting/FAQ.md#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors | |
"no-undef": "off", | |
// TypeScript might have features not supported in a specific Node.js version. | |
"node/no-unsupported-features/es-syntax": "off", | |
"node/no-unsupported-features/es-builtins": "off", | |
// Even though we already use `@typescript-eslint/ban-types`, `unicorn/no-null` is useful for catching literal usage. | |
// https://github.com/xojs/eslint-config-xo-typescript/issues/69 | |
// 'unicorn/no-null': 'off', | |
// The rule is buggy with TS and it's not needed as TS already enforces valid imports and references at compile-time. | |
"import/namespace": "off", | |
// TypeScript already does a better job at this. | |
"import/named": "off", | |
// `import/no-duplicates` works better with TypeScript. | |
"no-duplicate-imports": "off", | |
}, | |
}, | |
{ | |
name: "xo-typescript override .d.ts files", | |
files: ["**/*.d.ts", "**/*.d.mts", "**/*.d.cts"], | |
rules: { | |
"@typescript-eslint/no-unused-vars": "off", | |
}, | |
}, | |
{ | |
name: "xo-typescript override .d.ts files", | |
files: ["**/*.d.ts", "**/*.d.mts", "**/*.d.cts"], | |
rules: { | |
"@typescript-eslint/no-unused-vars": "off", | |
}, | |
}, | |
{ | |
name: "xo-typescript override .tsx files", | |
files: ["**/*.tsx"], | |
rules: { | |
...getNamingConventionRule({ isTsx: true }), | |
}, | |
}, | |
{ | |
name: "simple-import-sort", | |
plugins: { | |
"simple-import-sort": simpleImportSort, | |
}, | |
rules: { | |
"simple-import-sort/imports": "error", | |
"simple-import-sort/exports": "error", | |
}, | |
}, | |
eslintPluginUnicorn.configs["flat/recommended"], | |
{ | |
name: "project specific overrides", | |
files: allSourceFiles, | |
rules: { | |
"arrow-body-style": "off", | |
// "no-console": "error", | |
"no-empty": ["error", { allowEmptyCatch: true }], | |
"@typescript-eslint/no-unused-vars": [ | |
"error", | |
{ | |
vars: "all", | |
args: "after-used", | |
ignoreRestSiblings: false, | |
varsIgnorePattern: "^_", | |
argsIgnorePattern: "^_", | |
destructuredArrayIgnorePattern: "^_", | |
caughtErrorsIgnorePattern: "^(_|error$)", | |
}, | |
], | |
"unicorn/filename-case": "off", | |
"unicorn/no-null": "off", | |
"unicorn/prefer-switch": ["error", { minimumCases: 5 }], | |
"unicorn/no-new-array": "off", | |
"unicorn/no-await-expression-member": "off", | |
"unicorn/prevent-abbreviations": [ | |
"warn", | |
{ | |
extendDefaultReplacements: false, | |
replacements: { | |
cur: { current: true }, | |
curr: { current: true }, | |
dest: { destination: true }, | |
dir: { direction: true, directory: true }, | |
dist: { distance: true, distribution: true }, | |
dst: { daylightSavingTime: true, destination: true }, | |
e: { error: true, event: true }, | |
elem: { el: true }, | |
err: { error: true }, | |
ev: { event: true }, | |
evt: { event: true }, | |
fn: { func: true }, | |
idx: { index: true }, | |
rel: { related: true, relationship: true, relative: true }, | |
res: { resource: true, response: true, result: true }, | |
ret: { returnValue: true }, | |
retval: { returnValue: true }, | |
tmp: { temp: true }, | |
}, | |
}, | |
], | |
}, | |
}, | |
disableRulesThatClashWithPrettierConfig, | |
]; | |
export default config; | |
// For xo-typescript | |
function getNamingConventionRule({ isTsx }) { | |
return { | |
"@typescript-eslint/naming-convention": [ | |
"error", | |
{ | |
/// selector: ['variableLike', 'memberLike', 'property', 'method'], | |
// Note: Leaving out `parameter` and `typeProperty` because of the mentioned known issues. | |
// Note: We are intentionally leaving out `enumMember` as it's usually pascal-case or upper-snake-case. | |
selector: [ | |
"variable", | |
"function", | |
"classProperty", | |
"objectLiteralProperty", | |
"parameterProperty", | |
"classMethod", | |
"objectLiteralMethod", | |
"typeMethod", | |
"accessor", | |
], | |
format: ["strictCamelCase", isTsx && "StrictPascalCase"].filter(Boolean), | |
// We allow double underscore because of GraphQL type names and some React names. | |
leadingUnderscore: "allowSingleOrDouble", | |
trailingUnderscore: "allow", | |
// Ignore `{'Retry-After': retryAfter}` type properties. | |
filter: { | |
regex: "[- ]", | |
match: false, | |
}, | |
}, | |
{ | |
selector: "typeLike", | |
format: ["StrictPascalCase"], | |
}, | |
{ | |
selector: "variable", | |
types: ["boolean"], | |
format: ["StrictPascalCase"], | |
prefix: ["is", "has", "can", "should", "will", "did"], | |
}, | |
{ | |
// Interface name should not be prefixed with `I`. | |
selector: "interface", | |
filter: /^(?!I)[A-Z]/.source, | |
format: ["StrictPascalCase"], | |
}, | |
{ | |
// Type parameter name should either be `T` or a descriptive name. | |
selector: "typeParameter", | |
filter: /^T$|^[A-Z][A-Za-z]+$/.source, | |
format: ["StrictPascalCase"], | |
}, | |
// Allow these in non-camel-case when quoted. | |
{ | |
selector: ["classProperty", "objectLiteralProperty"], | |
format: null, | |
modifiers: ["requiresQuotes"], | |
}, | |
], | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment