Skip to content

Migrating from tslint to eslint and from request to got (part 1)

Last month was unexpectedly busy, update-wise, as 2 major dependencies I use for work-related projects suddenly announced, barely a few days apart, that they were being discontinued. The first was tslint, which probably every TypeScript developer has at least heard of once (~3.4M weekly downloads at the moment). The second was request, which probably every Node developer who’s ever had to make an HTTP request has already heard of.

Both projects had become pretty quiet, update-wise, so I probably could have seen this coming earlier had I paid attention more carefully. Anyway, replacements were easy to fine: tsling clearly suggested eslint, request didn’t suggest a particular replacement, but got and a few other seemed obivously widespread.

Part 1: From tslint to eslint

I installed eslint globally. Which isn’t recommended for some reason, but works perfectly fine for me. You’ll most like also need a couple of plugins for TypeScript, namely:
npm i -g eslint @typescript-eslint/eslint-plugin @typescript-eslint/parser

A key tool in the migration was tslint-to-eslint-config. Running it is as simple as npx tslint-to-eslint-config (after, if you don’t have it already, npm install -g npx). It provides an .eslintrc.js file that’s a good basis for you new eslint rules. Except that it’s in JavaScript and I definitely wanted JSON. I also created a base config file from eslint itself, via the eslint --init mentioned in getting started. And then I basically combined those.

Finally I browsed my code with the new rules, either fixing the new violations or adding rules for them. In the end, I couldn’t match my tsling rules perfectly, but I had just about 20 lines (on a ~30k lines codebase) that required a change that I was unhappy with, mostly about indenting and typecasting.

Last but not least, here are my config files:

Old tslint.json:

  "defaultSeverity": "warning",
  "extends": [
  "jsRules": {},
  "rules": {
    "array-type": [true, "array"],
    "arrow-parens": [true, "ban-single-arg-parens"],
    "curly": [true, "ignore-same-line"],
    "eofline": true,
    "max-classes-per-file": [true, 3],
    "max-line-length": [true, {"limit": 140, "ignore-pattern": "^import |^export {(.*?)}|class [a-zA-Z] implements |//"}],
    "no-angle-bracket-type-assertion": false,
    "no-consecutive-blank-lines": [true, 2],
    "no-console": false,
    "no-empty": false,
    "no-shadowed-variable": false,
    "no-string-literal": true,
    "no-string-throw": true,
    "no-trailing-whitespace": true,
    "object-literal-key-quotes": [true, "as-needed"],
    "object-literal-shorthand": [true, "never"],
    "object-literal-sort-keys": false,
    "one-line": [true, "check-catch", "check-finally", "check-else", "check-open-brace"],
    "only-arrow-functions": [true, "allow-named-functions"],
    "prefer-const": false,
    "quotemark": [true, "single", "avoid-escape"],
    "semicolon": [true, "always"],
    "trailing-comma": [true, {"multiline": "never", "singleline": "never"}],
    "triple-equals": [true, "allow-null-check", "allow-undefined-check"],
    "variable-name": [true, "ban-keywords", "check-format", "allow-leading-underscore"],
    "whitespace": [true, "check-branch", "check-decl", "check-operator", "check-module",
      "check-separator", "check-rest-spread", "check-type", "check-type-operator","check-preblock"]
  "rulesDirectory": []

New .eslintrc.json (yes, it’s a hell lot bigger):

  "env": {
    "es6": true,
    "node": true
  "extends": [
  "globals": {
    "Atomics": "readonly",
    "SharedArrayBuffer": "readonly"
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaVersion": 2018,
    "sourceType": "module"
  "plugins": [
  "rules": {
    "@typescript-eslint/adjacent-overload-signatures": "warn",
    "@typescript-eslint/array-type": "warn",
    "@typescript-eslint/ban-types": "warn",
    "@typescript-eslint/camelcase": ["warn",
        "properties": "always",
        "ignoreDestructuring": false,
        "ignoreImports": true,
        "genericType": "never",
        "allow": ["child_process"]
    "@typescript-eslint/class-name-casing": "warn",
    "@typescript-eslint/consistent-type-assertions": "off",
    "@typescript-eslint/interface-name-prefix": ["warn", { "prefixWithI": "always" }],
    "@typescript-eslint/member-delimiter-style": [
          "multiline": {
              "delimiter": "semi",
              "requireLast": true
          "singleline": {
              "delimiter": "semi",
              "requireLast": false
    "@typescript-eslint/member-ordering": "warn",
    "@typescript-eslint/no-empty-function": "off",
    "@typescript-eslint/no-empty-interface": "warn",
    "@typescript-eslint/no-explicit-any": "off",
    "@typescript-eslint/no-extra-parens": "off",
    "@typescript-eslint/no-misused-new": "warn",
    "@typescript-eslint/no-namespace": "warn",
    "@typescript-eslint/no-parameter-properties": "off",
    "@typescript-eslint/no-use-before-define": "off",
    "@typescript-eslint/no-useless-constructor": "warn",
    "@typescript-eslint/no-var-requires": "warn",
    "@typescript-eslint/prefer-for-of": "warn",
    "@typescript-eslint/prefer-function-type": "warn",
    "@typescript-eslint/prefer-namespace-keyword": "warn",
    "@typescript-eslint/quotes": [
          "avoidEscape": true
    "@typescript-eslint/semi": ["warn", "always", {"omitLastInOneLineBlock": true}],
    "@typescript-eslint/triple-slash-reference": "warn",
    "@typescript-eslint/unified-signatures": "warn",
    "array-bracket-spacing": ["warn", "never"],
    "arrow-parens": ["warn", "as-needed"],
    "arrow-spacing": ["warn", {"after": true, "before": true}],
    "brace-style": ["warn", "1tbs", {"allowSingleLine": true}],
    "camelcase": "off",
    "comma-dangle": ["warn", "never"],
    "comma-spacing": ["warn", {"before": false, "after": true}],
    "complexity": "off",
    "computed-property-spacing": ["warn", "never", { "enforceForClassMembers": true }],
    "comma-style": ["warn", "last"],
    "consistent-this": ["error", "self"],
    "constructor-super": "warn",
    "curly": ["warn", "multi-line", "consistent"],
    "dot-notation": "warn",
    "eol-last": "warn",
    "eqeqeq": ["warn", "always"],
    "func-call-spacing": ["warn", "never"],
    "guard-for-in": "off",
    "id-blacklist": [
    "id-match": "warn",
    "indent": [
        "SwitchCase": 1,
        "MemberExpression": "off",
        "FunctionDeclaration": {
          "parameters": 1,
          "body": 1
        "FunctionExpression": {
          "parameters": 1,
          "body": 1
        "CallExpression": {"arguments": "first"},
        "ArrayExpression": "off",
        "ObjectExpression": 1,
        "flatTernaryExpressions": true,
        "ignoredNodes": []
    "keyword-spacing": ["warn", {"after": true, "before": true}],
    "linebreak-style": ["warn", "unix"],
    "max-classes-per-file": ["warn", 3],
    "max-len": ["warn", {"code": 140, "ignoreComments": true}],
    "new-parens": "warn",
    "no-bitwise": "warn",
    "no-caller": "warn",
    "no-cond-assign": "warn",
    "no-console": "off",
    "no-constant-condition": "off",
    "no-debugger": "warn",
    "no-empty": "off",
    "no-eval": "warn",
    "no-fallthrough": "off",
    "no-invalid-this": "off",
    "no-multi-spaces": "warn",
    "no-multiple-empty-lines": ["warn", {"max": 2}],
    "no-new-wrappers": "warn",
    "no-shadow": ["off", {"hoist": "all"}],
    "no-throw-literal": "off",
    "no-trailing-spaces": "warn",
    "no-undef-init": "warn",
    "no-underscore-dangle": "off",
    "no-unsafe-finally": "warn",
    "no-unused-expressions": "warn",
    "no-unused-labels": "warn",
    "no-unused-vars": "off",
    "no-useless-constructor": "off",
    "no-useless-rename": "warn",
    "no-var": "warn",
    "no-whitespace-before-property": "warn",
    "object-curly-spacing": ["warn", "never"],
    "object-shorthand": ["warn", "never"],
    "one-var": ["warn", "never"],
    "prefer-arrow-callback": "warn",
    "prefer-const": "off",
    "quote-props": ["warn", "as-needed"],
    "quotes": ["warn", "single", {"avoidEscape": true, "allowTemplateLiterals": true}],
    "radix": "warn",
    "semi": "off",
    "semi-spacing": ["warn", {"before": false, "after": true}],
    "space-before-blocks": ["warn", { "functions": "always", "keywords": "always", "classes": "always" }],
    "space-before-function-paren": ["warn", {"anonymous": "never", "named": "never", "asyncArrow": "always"}],
    "space-in-parens": ["warn", "never"],
    "space-infix-ops": ["warn", { "int32Hint": false }],
    "space-unary-ops": [2, {"words": true, "nonwords": false, "overrides": {}}],
    "spaced-comment": "off",
    "switch-colon-spacing": ["error", {"after": true, "before": false}],
    "unicode-bom": ["warn", "never"],
    "use-isnan": "warn",
    "valid-typeof": "off"

Note that some ESLint rules have been enhanced with TypeScript support in typescript-eslint. In case you want to use the typescript-eslint version, you should set the eslint version to “off” to avoid conflicts.
List of ESLint rules:
List of typescript-eslint rules:

Part 2: From request to got

To be continued in another post, as this one got huge enough already!

Posted in JavaScript / TypeScript / Node.js, programming, web development.

0 Responses

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.

Some HTML is OK

or, reply to this post via trackback.

Sorry about the CAPTCHA that requires JS. If you really don't want to enable JS and still want to comment, you can send me your comment via e-mail and I'll post it for you.

Please solve the CAPTCHA below in order to fight spamWordPress CAPTCHA