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": [ "tslint:recommended" ], "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": [ "eslint:recommended", "plugin:@typescript-eslint/eslint-recommended" ], "globals": { "Atomics": "readonly", "SharedArrayBuffer": "readonly" }, "parser": "@typescript-eslint/parser", "parserOptions": { "ecmaVersion": 2018, "sourceType": "module" }, "plugins": [ "@typescript-eslint" ], "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": [ "warn", { "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": [ "warn", "single", { "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": [ "warn", "any", "Number", "number", "String", "string", "Boolean", "boolean", "Undefined" ], "id-match": "warn", "indent": [ "warn", 2, { "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: https://eslint.org/docs/rules/
List of typescript-eslint rules: https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin/docs/rules
Part 2: From request to got
To be continued in another post, as this one got huge enough already!
0 Responses
Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.