ljosberinn/eslint-config-galex

View on GitHub
src/overrides/typescript.ts

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
import { type Linter } from 'eslint';
import { rules as allPrettierRules } from 'eslint-config-prettier';

import {
  type Dependencies,
  type OverrideCreator,
  type OverrideESLintConfig,
  type OverrideInternalOverride,
  type RulesCreator,
} from '../types';
import { uniqueArrayEntries } from '../utils/array';
import { tsOverrideType } from '../utils/overrideType';
import { fulfillsVersionRequirement } from '../utils/version';
import { remixRunRoutesOverrideFiles } from './react';

const prettierTypeScriptRules = Object.fromEntries(
  Object.entries(allPrettierRules).filter(([key]) =>
    key.startsWith('@typescript-eslint')
  )
);

export const files = ['**/*.ts?(x)'];
const parser = '@typescript-eslint/parser';
const defaultParserOptions = {
  ecmaFeatures: {
    jsx: false,
  },
  project: './tsconfig.json',
  warnOnUnsupportedTypeScriptVersion: true,
};

const plugins = ['@typescript-eslint', 'etc'];
const defaultSettings = {
  react: {
    version: 'detect',
  },
};

export const createTypeScriptOverride: OverrideCreator = ({
  rules: customRules,
  files: customFiles,
  parserOptions: customParserOptions,
  settings: customSettings,
  env: customEnv,
  excludedFiles: customExcludedFiles,
  globals: customGlobals,
  plugins: customPlugins,
  extends: customExtends,
  overrides: customOverrides,
  ...dependencies
}) => {
  if (!dependencies.typescript.hasTypeScript) {
    return null;
  }

  const rules = {
    ...createTypeScriptRules(dependencies),
    ...createNestJsRules(dependencies),
    ...createEtcRules(dependencies),
    ...prettierTypeScriptRules,
    ...customRules,
  };

  const finalFiles = customFiles ?? files;
  const finalEnv = customEnv;
  const finalExcludedFiles = customExcludedFiles;
  const finalGlobals = customGlobals;
  const finalPlugins = uniqueArrayEntries([
    ...plugins,
    ...(customPlugins ?? []),
  ]);
  const finalExtends = customExtends;
  const finalOverrides = createOverrides(dependencies, customOverrides);

  const parserOptions: OverrideESLintConfig['parserOptions'] = {
    ...defaultParserOptions,
    ...customParserOptions,
    ecmaFeatures: {
      ...defaultParserOptions.ecmaFeatures,
      ...customParserOptions?.ecmaFeatures,
      jsx: dependencies.react.hasReact,
    },
  };

  const settings: OverrideESLintConfig['settings'] = {
    ...defaultSettings,
    ...customSettings,
    react: {
      ...defaultSettings.react,
      ...customSettings?.react,
    },
  };

  return {
    files: finalFiles,
    parser,
    parserOptions,
    plugins: finalPlugins,
    settings,
    rules,
    overrideType: tsOverrideType,
    env: finalEnv,
    excludedFiles: finalExcludedFiles,
    globals: finalGlobals,
    extends: finalExtends,
    overrides: finalOverrides,
  };
};

const createOverrides = (
  dependencies: Dependencies,
  customOverrides: OverrideESLintConfig['overrides'] = []
): OverrideESLintConfig['overrides'] => {
  const remixOverride = createRemixTypeScriptOverride(dependencies);

  return [remixOverride, ...customOverrides].filter(
    (dataset): dataset is Linter.ConfigOverride =>
      dataset !== null && typeof dataset === 'object'
  );
};

/**
 * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/README.md
 *
 */
export const createTypeScriptRules: RulesCreator = ({
  typescript: { version, hasTypeScript, config },
  react: { isCreateReactApp, hasReact },
}) => {
  if (!hasTypeScript || !version || !config) {
    return null;
  }

  return {
    '@typescript-eslint/adjacent-overload-signatures': 'error',
    /**
     * prefer using `T[]` over `Array<T>`
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/array-type.md
     */
    '@typescript-eslint/array-type': ['warn', { default: 'array' }],

    /**
     * disallows awaiting a value that is not thenable
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/await-thenable.md
     */
    '@typescript-eslint/await-thenable': 'error',

    /**
     * disallows // @ts-<directive> comments except for -expect-error with
     * description
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/ban-ts-comment.md
     */
    '@typescript-eslint/ban-ts-comment': [
      'error',
      {
        'ts-expect-error': isCreateReactApp ? true : 'allow-with-description',
        'ts-ignore': true,
        'ts-nocheck': true,
      },
    ],

    /**
     * TSLint is dead
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/ban-tslint-comment.md
     */
    ...(isCreateReactApp
      ? null
      : { '@typescript-eslint/ban-tslint-comment': 'error' }),

    /**
     * warn about ambiguous types that might not work as you'd think
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/ban-types.md
     */
    '@typescript-eslint/ban-types': 'warn',

    /**
     * off because prettier
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/brace-style.md
     */
    '@typescript-eslint/brace-style': 'off',

    /**
     * off because deprecated
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/camelcase.md
     */
    '@typescript-eslint/camelcase': 'off',

    /**
     * off because too opinionated
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/class-literal-property-style.md
     */
    '@typescript-eslint/class-literal-property-style': 'off',

    /**
     * off because prettier takes care of that
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/comma-dangle.md
     */
    '@typescript-eslint/comma-dangle': 'off',

    /**
     * off because prettier takes care of that
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/comma-spacing.md
     */
    '@typescript-eslint/comma-spacing': 'off',

    /**
     * disallow function creation in loops
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-loop-func.md
     * @see https://eslint.org/docs/rules/no-loop-func
     */
    ...(isCreateReactApp
      ? null
      : { '@typescript-eslint/no-loop-func': 'error' }),

    /**
     * streamlines assertions to use `as` over `<type>`
     * expects objects to be defined by `const foo: Type` over `as Type`
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/consistent-type-assertions.md
     */
    ...(fulfillsVersionRequirement({
      given: version,
      expected: '^3.4.0',
    })
      ? {
          '@typescript-eslint/consistent-type-assertions': [
            'error',
            {
              assertionStyle: 'as',
              objectLiteralTypeAssertions: 'never',
            },
          ],
        }
      : null),

    /**
     * prefer using types since interfaces:
     * - implicitly merged given multiple declarations of the same name
     * - aren't entirely as flexible as types
     * - may have confusing syntax for functions
     *
     * also consistency
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/consistent-type-definitions.md
     */
    '@typescript-eslint/consistent-type-definitions': ['error', 'type'],

    /**
     *  enforces type-only imports when possible
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/consistent-type-imports.md
     */
    ...(fulfillsVersionRequirement({ given: version, expected: '^3.8.0' })
      ? {
          '@typescript-eslint/consistent-type-imports': [
            'warn',
            {
              disallowTypeAnnotations: true,
              prefer: 'type-imports',
            },
          ],
        }
      : null),

    /**
     * prevents having optional params before default
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/default-param-last.md
     * @see default-params-last
     */
    '@typescript-eslint/default-param-last': 'error',

    /**
     * prefer using the object.dot notation over key access
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/dot-notation.md
     */
    '@typescript-eslint/dot-notation': 'error',

    /**
     * off because it is preferred to let inference work where possible
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/explicit-function-return-type.md
     */
    '@typescript-eslint/explicit-function-return-type': 'off',

    /**
     * ensures explicitness about member accessibility
     *
     * __EXPERIMENTAL__
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/explicit-member-accessibility.md
     */
    '@typescript-eslint/explicit-member-accessibility': 'error',

    /**
     * ensures exported functions are strongly typed
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/explicit-module-boundary-types.md
     */
    '@typescript-eslint/explicit-module-boundary-types': 'error',

    /**
     * off because prettier takes care of that
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/func-call-spacing.md
     * @see func-call-spacing
     */
    '@typescript-eslint/func-call-spacing': 'off',

    /**
     * off because prettier takes care of that
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/indent.md
     */
    '@typescript-eslint/indent': 'off',

    /**
     * off because required to escape scope
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/init-declarations.md
     * @see init-declarations
     */
    '@typescript-eslint/init-declarations': 'off',

    /**
     * off because prettier takes care of that
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/keyword-spacing.md
     * @see keyword-spacing
     */
    '@typescript-eslint/keyword-spacing': 'off',

    /**
     * formatting; prettier handles it.
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/lines-around-comment.md
     * @see lines-around-comment
     */
    '@typescript-eslint/lines-around-comment': 'off',

    /**
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/lines-between-class-members.md
     * @see lines-between-class-members
     */
    '@typescript-eslint/lines-between-class-members': 'warn',

    /**
     * enforces consistent member delimiter style in interfaces & types
     *
     * off because prettier takes care of that
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/member-delimiter-style.md
     */
    '@typescript-eslint/member-delimiter-style': 'off',

    /**
     * ensures consistend ordering of class internals
     *
     * __EXPERIMENTAL__
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/member-ordering.md
     */
    '@typescript-eslint/member-ordering': 'warn',

    /**
     * ensures consistent method signature declaration on interfaces
     *
     * off since interfaces are forbidden anyways
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/method-signature-style.md
     */
    '@typescript-eslint/method-signature-style': 'off',

    /**
     * off because too opinionated
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/naming-convention.md
     */
    '@typescript-eslint/naming-convention': 'off',

    /**
     * disallows `new Array()`
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-array-constructor.md
     * @see no-array-constructor
     */
    '@typescript-eslint/no-array-constructor': 'error',

    /**
     * avoids calling `.toString()` on variables that don't contain
     * meainingful info
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-base-to-string.md
     */
    '@typescript-eslint/no-base-to-string': 'error',

    /**
     * disallows confusing use of `!`
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-confusing-non-null-assertion.md
     */
    ...(isCreateReactApp
      ? null
      : { '@typescript-eslint/no-confusing-non-null-assertion': 'error' }),

    /**
     * off because opinionated
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/consistent-indexed-object-style.md
     */
    '@typescript-eslint/consistent-indexed-object-style': 'off',

    /**
     * disallows duplicate names in class members
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-dupe-class-members.md
     * @see no-dupe-class-members
     */
    '@typescript-eslint/no-dupe-class-members': 'error',

    /**
     * disallows defining an enum with multiple members initialized to the same value
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-duplicate-enum-values.md
     */
    '@typescript-eslint/no-duplicate-enum-values': 'error',

    /**
     * warns when trying to dynamically delete a property
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-dynamic-delete.md
     */
    '@typescript-eslint/no-dynamic-delete': 'warn',

    /**
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-empty-function.md
     * @see no-empty-function
     */
    '@typescript-eslint/no-empty-function': 'error',

    /**
     * disallows empty interfaces
     *
     * off because interfaces are forbidden anyways
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-empty-interface.md
     */
    '@typescript-eslint/no-empty-interface': 'off',

    /**
     * disallows explicit any. escalated to error because `warn` would be
     * silenced already, and you usually don't want to have `any`s long time
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-explicit-any.md
     */
    '@typescript-eslint/no-explicit-any': 'error',

    /**
     * prevents e.g. `!!!`
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-extra-non-null-assertion.md
     */
    '@typescript-eslint/no-extra-non-null-assertion': 'error',

    /**
     * off because prettier takes care of that
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-extra-parens.md
     */
    '@typescript-eslint/no-extra-parens': 'off',

    /**
     * off because prettier takes care of that
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-extra-semi.md
     */
    '@typescript-eslint/no-extra-semi': 'off',

    /**
     * prevents use of class as namespace. use an object instead
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-extraneous-class.md
     */
    '@typescript-eslint/no-extraneous-class': 'error',

    /**
     * prevents unhandled promises
     *
     * can be enabled in CRA once they support ESLint v8
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-floating-promises.md
     * @see promise/catch-or-return
     */
    ...(isCreateReactApp
      ? null
      : { '@typescript-eslint/no-floating-promises': 'error' }),

    /**
     * prevents potentially unintended loops
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-for-in-array.md
     */
    '@typescript-eslint/no-for-in-array': 'warn',

    /**
     * warns implicit-any catches
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-implicit-any-catch.md
     */
    ...(fulfillsVersionRequirement({ given: version, expected: '^4.0.0' })
      ? { '@typescript-eslint/no-implicit-any-catch': 'off' }
      : null),

    /**
     * prevents odd cases of implied eval. probably intentional, given those
     * examples, but fine to disable it there then.
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-implied-eval.md
     */
    '@typescript-eslint/no-implied-eval': 'error',

    /**
     * prevents unecessary types
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-inferrable-types.md
     */
    '@typescript-eslint/no-inferrable-types': 'warn',

    /**
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-invalid-this.md
     * @see no-invalid-this
     */
    '@typescript-eslint/no-invalid-this': 'error',

    /**
     * disallows illogical void type annotations, e.g. in unions
     *
     * __EXPERIMENTAL__
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-invalid-void-type.md
     */
    '@typescript-eslint/no-invalid-void-type': 'error',

    /**
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-loss-of-precision.md
     * @see no-loss-of-precision
     */
    ...(isCreateReactApp
      ? null
      : { '@typescript-eslint/no-loss-of-precision': 'error' }),

    /**
     * off because it expects literally every number to be declared as variable
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-magic-numbers.md
     * @see no-magic-numbers
     */
    '@typescript-eslint/no-magic-numbers': 'off',

    /**
     * disallows defining constructors in interfaces, use `new` instead
     *
     * off because interfaces are forbidden anyways
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-misused-new.md
     */
    '@typescript-eslint/no-misused-new': 'off',

    /**
     * prevents most likely improperly handled promises
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-misused-promises.md
     */
    '@typescript-eslint/no-misused-promises': [
      'warn',
      {
        checksConditionals: true,
        checksVoidReturn: true,
      },
    ],

    /**
     * warns when using module/namespace syntax - use import/export instead
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-namespace.md
     */
    '@typescript-eslint/no-namespace': 'warn',

    /**
     * prevents non-null casting during optional chaining
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-non-null-asserted-optional-chain.md
     */
    ...(fulfillsVersionRequirement({ given: version, expected: '^3.7.0' })
      ? { '@typescript-eslint/no-non-null-asserted-optional-chain': 'error' }
      : null),

    /**
     * prevents casting something as non-null that probably is null
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-non-null-assertion.md
     */
    '@typescript-eslint/no-non-null-assertion': 'error',

    /**
     * off because opinionated
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-parameter-properties.md
     */
    '@typescript-eslint/no-parameter-properties': 'off',
    /**
     * prevents partially subtle var redeclaring issues
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-redeclare.md
     */
    '@typescript-eslint/no-redeclare': 'warn',

    /**
     * prevents redundant code
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-redundant-type-constituents.md
     */
    '@typescript-eslint/no-redundant-type-constituents': 'warn',

    /**
     * use import instead of require
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-require-imports.md
     */
    '@typescript-eslint/no-require-imports': 'error',

    /**
     * disallows assigning `this` to a variable.
     *
     * destructuring from a class is still allowed.
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-this-alias.md
     */
    '@typescript-eslint/no-this-alias': [
      'error',
      {
        allowDestructuring: true,
      },
    ],

    /**
     * disallows throwing anything else than an error
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-throw-literal.md
     */
    '@typescript-eslint/no-throw-literal': 'error',

    /**
     * off because aliasing is fine and this rule does not allow to exclusively
     * forbid aliasing e.g. `type MyString = string`
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-type-alias.md
     */
    '@typescript-eslint/no-type-alias': 'off',

    /**
     * prevents unecessary comparison
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unnecessary-boolean-literal-compare.md
     */
    '@typescript-eslint/no-unnecessary-boolean-literal-compare':
      isCreateReactApp
        ? 'warn'
        : [
            'warn',
            {
              allowComparingNullableBooleansToFalse: false,
              allowComparingNullableBooleansToTrue: false,
            },
          ],

    /**
     * prevents unecessary checks
     *
     * off, as its bugged
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unnecessary-condition.md
     */
    '@typescript-eslint/no-unnecessary-condition': 'off',
    // config?.compilerOptions?.strictNullChecks ||
    // config?.compilerOptions?.strict
    //   ? 'warn'
    //   : 'off',

    /**
     * prevents using unmecessary qualifier
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unnecessary-qualifier.md
     */
    '@typescript-eslint/no-unnecessary-qualifier': 'error',

    /**
     * prevents unnecessary type arguments
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unnecessary-type-arguments.md
     */
    '@typescript-eslint/no-unnecessary-type-arguments': 'error',

    /**
     * prevents unnecessary type assertions
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unnecessary-type-assertion.md
     */
    '@typescript-eslint/no-unnecessary-type-assertion': 'warn',

    /**
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unnecessary-type-constraint.md
     */
    '@typescript-eslint/no-unnecessary-type-constraint': 'warn',

    /**
     * prevents accidental any leaking into your code
     *
     * off because lots of false positives
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unsafe-argument.md
     */
    '@typescript-eslint/no-unsafe-argument': 'off',

    /**
     * off because already handled
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unsafe-assignment.md
     * @see @typescript-eslint/no-explicit-any
     */
    '@typescript-eslint/no-unsafe-assignment': 'off',

    /**
     * off because already handled
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unsafe-call.md
     * @see @typescript-eslint/no-explicit-any
     */
    '@typescript-eslint/no-unsafe-call': 'off',

    /**
     * off because already handled
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unsafe-member-access.md
     * @see @typescript-eslint/no-explicit-any
     */
    '@typescript-eslint/no-unsafe-member-access': 'off',

    /**
     * off because already handled
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unsafe-return.md
     * @see @typescript-eslint/no-explicit-any
     */
    '@typescript-eslint/no-unsafe-return': 'off',

    /**
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unused-expressions.md
     * @see no-unused-expressions
     */
    '@typescript-eslint/no-unused-expressions': 'error',

    /**
     * adds support for TS features, e.g. types
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unused-vars.md
     */
    '@typescript-eslint/no-unused-vars': 'warn',

    /**
     * blocked because false positives
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-use-before-define.md
     * @see https://github.com/typescript-eslint/typescript-eslint/issues/2453
     * @see https://github.com/typescript-eslint/typescript-eslint/issues/2452
     * @see https://github.com/typescript-eslint/typescript-eslint/issues/2451
     * @see https://github.com/typescript-eslint/typescript-eslint/issues/2449
     */
    '@typescript-eslint/no-use-before-define': 'off',

    /**
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-useless-constructor.md
     * @see no-useless-constructor
     */
    '@typescript-eslint/no-useless-constructor': 'error',

    /**
     * prevents redundant code
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-useless-empty-export.md
     */
    '@typescript-eslint/no-useless-empty-export': 'error',

    /**
     * off because already handled
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-var-requires.md
     * @see @typescript-eslint/no-namespace
     */
    '@typescript-eslint/no-var-requires': 'off',

    /**
     * detects when `as` casts can be simplified to `!`
     *
     * off because it conflicts in some cases with `@typescript-eslint/no-non-null-assertion` which has priority
     * @see https://github.com/ljosberinn/eslint-config-galex/issues/235
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/non-nullable-type-assertion-style.md
     */
    '@typescript-eslint/non-nullable-type-assertion-style': 'off',

    /**
     * off because handled by prettier
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/object-curly-spacing.md
     * @see object-curly-spacing
     */
    '@typescript-elint/object-curly-spacing': 'off',

    /**
     * sorts members of type union/intersection are sorted alphabetically
     *
     * off because opinionated
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/sort-type-union-intersection-members.md
     */
    '@typescript-eslint/sort-type-union-intersection-members': 'off',

    /**
     * off because the use of modern language features, esp. if already present
     * in other languages, should be encouraged
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-parameter-properties.md
     */
    '@typescript-eslint/parameter-properties': [
      'warn',
      {
        prefer: 'parameter-property',
      },
    ],

    /**
     * prefer using `as const` syntax over casting
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/prefer-as-const.md
     */
    ...(fulfillsVersionRequirement({ given: version, expected: '^3.4.0' })
      ? { '@typescript-eslint/prefer-as-const': 'error' }
      : null),

    /**
     * be explicit about enum members
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/prefer-enum-initializers.md
     */
    ...(isCreateReactApp
      ? null
      : { '@typescript-eslint/prefer-enum-initializers': 'error' }),

    /**
     * prefer using `for...of` when only iterating the index
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/prefer-for-of.md
     */
    '@typescript-eslint/prefer-for-of': 'warn',

    /**
     * off because opinonated
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/prefer-function-type.md
     */
    '@typescript-eslint/prefer-function-type': 'off',

    /**
     * prefer using array.includes over older alternatives
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/prefer-includes.md
     */
    '@typescript-eslint/prefer-includes': 'warn',

    /**
     * enforces enum members to be literal values
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/prefer-literal-enum-member.md
     */
    ...(isCreateReactApp
      ? null
      : { '@typescript-eslint/prefer-literal-enum-member': 'error' }),

    /**
     * use `namespace` over `module`
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/prefer-namespace-keyword.md
     */
    '@typescript-eslint/prefer-namespace-keyword': 'error',

    /**
     * prefer ?? over || where needed
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/prefer-nullish-coalescing.md
     */
    ...(config?.compilerOptions?.strictNullChecks &&
    fulfillsVersionRequirement({ given: version, expected: '^3.7.0' })
      ? { '@typescript-eslint/prefer-nullish-coalescing': 'error' }
      : null),

    /**
     * prefer `foo?.bar?.baz` over `foo && foo.bar && foo.bar.baz`
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/prefer-optional-chain.md
     */
    ...(fulfillsVersionRequirement({ given: version, expected: '^3.7.0' })
      ? { '@typescript-eslint/prefer-optional-chain': 'error' }
      : null),

    /**
     * prefer having all function parameters to be readonly
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/prefer-readonly.md
     */
    '@typescript-eslint/prefer-readonly': 'error',

    /**
     * prefer all function parameters to be readonly
     *
     * off because of false positives that never go away :cry:
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/prefer-readonly-parameter-types.md
     */
    '@typescript-eslint/prefer-readonly-parameter-types': 'off',

    /**
     * use `[].reduce<Type>` over `[].reduce(...) as Type`
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/prefer-reduce-type-parameter.md
     */
    '@typescript-eslint/prefer-reduce-type-parameter': 'error',

    /**
     * use RegExp#exec over String#match because performance
     *
     * warn because autofixeable since 4.22.0
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/prefer-regexp-exec.md
     */
    '@typescript-eslint/prefer-regexp-exec': 'warn',

    /**
     * use String.startsWith/.endsWith over String.indexOf or regex
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/prefer-string-starts-ends-with.md
     */
    '@typescript-eslint/prefer-string-starts-ends-with': 'error',

    /**
     * prefer using `// @ts-expect-error` as its better than `// @ts-ignore`
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/prefer-ts-expect-error.md
     */
    ...(fulfillsVersionRequirement({ given: version, expected: '^3.9.0' })
      ? { '@typescript-eslint/prefer-ts-expect-error': 'error' }
      : null),

    /**
     * returning void may lead to confusing code
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-confusing-void-expression.md
     */
    '@typescript-eslint/no-confusing-void-expression': 'warn',

    /**
     * off because nonsensical
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/promise-function-async.md
     */
    '@typescript-eslint/promise-function-async': 'off',

    /**
     * off because handled by prettier
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/quotes.md
     */
    '@typescript-eslint/quotes': 'off',

    /**
     * prevents using `[].sort()` without compare function
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/require-array-sort-compare.md
     */
    '@typescript-eslint/require-array-sort-compare': 'warn',

    /**
     * enforces both sides of an addition to be of the same type
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/restrict-plus-operands.md
     */
    '@typescript-eslint/restrict-plus-operands': 'warn',

    /**
     * enforces template literal expressions to be of type string
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/restrict-template-expressions.md
     */
    '@typescript-eslint/restrict-template-expressions': [
      'warn',
      {
        allowBoolean: true,
        allowNumber: true,
      },
    ],

    /**
     * prevents using `async` without `await`
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/require-await.md
     */
    '@typescript-eslint/return-await': 'error',

    /**
     * off because handled by prettier
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/semi.md
     * @see semi
     */
    '@typescript-eslint/semi': 'off',

    /**
     * off because handled by prettier
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/space-before-blocks.md
     */
    '@typescript-eslint/space-before-blocks': 'off',

    /**
     * off because handled by prettier
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/space-before-function-paren.md
     */
    '@typescript-eslint/space-before-function-paren': 'off',

    /**
     * off because handled by prettier
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/space-infix-ops.md
     * @see space-infix-ops
     */
    '@typescript-eslint/space-infix-ops': 'off',

    /**
     * off because opinionated. unless you have odd types going on, this shouldn't
     * be an issue following best practices.
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/strict-boolean-expressions.md
     */
    '@typescript-eslint/strict-boolean-expressions': 'off',

    /**
     * enforces switch statements cover all cases
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/switch-exhaustiveness-check.md
     */
    '@typescript-eslint/switch-exhaustiveness-check': 'warn',

    /**
     * prefer import over triple slash references
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/triple-slash-reference.md
     */
    '@typescript-eslint/triple-slash-reference': 'warn',

    /**
     * off because handled by prettier
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/type-annotation-spacing.md
     */
    '@typescript-eslint/type-annotation-spacing': 'off',

    /**
     * off because inference works
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/typedef.md
     */
    '@typescript-eslint/typedef': 'off',

    /**
     * enforces unbound methods are called with their expected scope
     *
     * off in react because it reports plenty of false positives with hooks
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/unbound-method.md
     */
    '@typescript-eslint/unbound-method': hasReact ? 'off' : 'warn',

    /**
     * prefer unions for function parameters instead of separate overloads
     *
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/unified-signatures.md
     */
    '@typescript-eslint/unified-signatures': 'warn',
  };
};

/**
 * @see https://github.com/nestjs/typescript-starter/blob/master/.eslintrc.js
 */
export const createNestJsRules: RulesCreator = ({ hasNest }) => {
  if (!hasNest) {
    return null;
  }

  return {
    '@typescript-eslint/interface-name-prefix': 'off',
    '@typescript-eslint/explicit-function-return-type': 'off',
    '@typescript-eslint/explicit-module-boundary-types': 'off',
    '@typescript-eslint/no-explicit-any': 'off',
    '@typescript-eslint/no-extraneous-class': 'off',
  };
};

/**
 * @see https://github.com/cartant/eslint-plugin-etc
 */
export const createEtcRules: RulesCreator = ({
  typescript: { hasTypeScript },
}) => {
  if (!hasTypeScript) {
    return null;
  }
  return {
    /**
     * forbids the assignment of returned, mutated arrays.
     *
     * @see https://github.com/cartant/eslint-plugin-etc/blob/main/docs/rules/no-assign-mutated-array.md
     */
    'etc/no-assign-mutated-array': 'error',

    /**
     * forbids commented-out code.
     *
     * @see https://github.com/cartant/eslint-plugin-etc/blob/main/docs/rules/no-commented-out-code.md
     */
    'etc/no-commented-out-code': 'off',

    /**
     * forbids the use of deprecated APIs. TypeScript already seems to be doing this
     *
     * @see https://github.com/cartant/eslint-plugin-etc/blob/main/docs/rules/no-deprecated.md
     */
    // 'etc/no-deprecated': 'off',

    /**
     * forbids the use of internal APIs.
     *
     * @see https://github.com/cartant/eslint-plugin-etc/blob/main/docs/rules/no-internal.md
     */
    'etc/no-internal': 'warn',

    /**
     * forbids type parameters without inference sites and type parameters that don't add type safety to declarations.
     *
     * @see https://github.com/cartant/eslint-plugin-etc/blob/main/docs/rules/no-misused-generics.md
     */
    'etc/no-misused-generics': 'warn',

    /**
     * forbids internal APIs that are not prefixed with underscores.
     *
     * @see https://github.com/cartant/eslint-plugin-etc/blob/main/docs/rules/underscore-internal.md
     */
    'etc/underscore-internal': 'warn',
  };
};

export const createRemixTypeScriptOverride: OverrideInternalOverride =
  dependencies => {
    if (!dependencies.react.isRemix || !dependencies.typescript.hasTypeScript) {
      return null;
    }

    return {
      files: remixRunRoutesOverrideFiles,
      rules: {
        // recommended practice is to `trow new Response` in e.g. `LoaderFunctions`
        '@typescript-eslint/no-throw-literal': 'off',
      },
    };
  };