diff --git a/package.json b/package.json
index a3ffa851516..e01a0ed0c89 100644
--- a/package.json
+++ b/package.json
@@ -76,7 +76,7 @@
"cspell": "^4.0.61",
"cz-conventional-changelog": "^3.2.0",
"downlevel-dts": "^0.4.0",
- "eslint": "^7.0.0",
+ "eslint": "^7.2.0",
"eslint-plugin-eslint-comments": "^3.1.2",
"eslint-plugin-eslint-plugin": "^2.2.1",
"eslint-plugin-import": "^2.20.2",
diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md
index 53b8cbb9412..cc2bbc07899 100644
--- a/packages/eslint-plugin/README.md
+++ b/packages/eslint-plugin/README.md
@@ -191,6 +191,7 @@ In these cases, we create what we call an extension rule; a rule within our plug
| [`@typescript-eslint/no-extra-parens`](./docs/rules/no-extra-parens.md) | Disallow unnecessary parentheses | | :wrench: | |
| [`@typescript-eslint/no-extra-semi`](./docs/rules/no-extra-semi.md) | Disallow unnecessary semicolons | :heavy_check_mark: | :wrench: | |
| [`@typescript-eslint/no-invalid-this`](./docs/rules/no-invalid-this.md) | disallow `this` keywords outside of classes or class-like objects | | | |
+| [`@typescript-eslint/no-loss-of-precision`](./docs/rules/no-loss-of-precision.md) | Disallow literal numbers that lose precision | | | |
| [`@typescript-eslint/no-magic-numbers`](./docs/rules/no-magic-numbers.md) | Disallow magic numbers | | | |
| [`@typescript-eslint/no-unused-expressions`](./docs/rules/no-unused-expressions.md) | Disallow unused expressions | | | |
| [`@typescript-eslint/no-unused-vars`](./docs/rules/no-unused-vars.md) | Disallow unused variables | :heavy_check_mark: | | |
diff --git a/packages/eslint-plugin/docs/rules/no-loss-of-precision.md b/packages/eslint-plugin/docs/rules/no-loss-of-precision.md
new file mode 100644
index 00000000000..d6cf7b71c14
--- /dev/null
+++ b/packages/eslint-plugin/docs/rules/no-loss-of-precision.md
@@ -0,0 +1,19 @@
+# Disallow literal numbers that lose precision (`no-loss-of-precision`)
+
+## Rule Details
+
+This rule extends the base [`eslint/no-loss-of-precision`](https://eslint.org/docs/rules/no-loss-of-precision) rule.
+It adds support for [numeric separators](https://github.com/tc39/proposal-numeric-separator).
+Note that this rule requires ESLint v7.
+
+## How to use
+
+```jsonc
+{
+ // note you must disable the base rule as it can report incorrect errors
+ "no-loss-of-precision": "off",
+ "@typescript-eslint/no-loss-of-precision": ["error"]
+}
+```
+
+Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-loss-of-precision.md)
diff --git a/packages/eslint-plugin/src/configs/all.ts b/packages/eslint-plugin/src/configs/all.ts
index 25001233883..642d6f736d0 100644
--- a/packages/eslint-plugin/src/configs/all.ts
+++ b/packages/eslint-plugin/src/configs/all.ts
@@ -59,6 +59,8 @@ export = {
'no-invalid-this': 'off',
'@typescript-eslint/no-invalid-this': 'error',
'@typescript-eslint/no-invalid-void-type': 'error',
+ 'no-loss-of-precision': 'off',
+ '@typescript-eslint/no-loss-of-precision': 'error',
'no-magic-numbers': 'off',
'@typescript-eslint/no-magic-numbers': 'error',
'@typescript-eslint/no-misused-new': 'error',
diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts
index 05016d5705f..37ad1da4806 100644
--- a/packages/eslint-plugin/src/rules/index.ts
+++ b/packages/eslint-plugin/src/rules/index.ts
@@ -96,6 +96,7 @@ import typedef from './typedef';
import unboundMethod from './unbound-method';
import unifiedSignatures from './unified-signatures';
import linesBetweenClassMembers from './lines-between-class-members';
+import noLossOfPrecision from './no-loss-of-precision';
export default {
'adjacent-overload-signatures': adjacentOverloadSignatures,
@@ -196,4 +197,5 @@ export default {
'unbound-method': unboundMethod,
'unified-signatures': unifiedSignatures,
'lines-between-class-members': linesBetweenClassMembers,
+ 'no-loss-of-precision': noLossOfPrecision,
};
diff --git a/packages/eslint-plugin/src/rules/no-loss-of-precision.ts b/packages/eslint-plugin/src/rules/no-loss-of-precision.ts
new file mode 100644
index 00000000000..47ab5b1a74f
--- /dev/null
+++ b/packages/eslint-plugin/src/rules/no-loss-of-precision.ts
@@ -0,0 +1,53 @@
+import { TSESTree } from '@typescript-eslint/experimental-utils';
+import BaseRule from 'eslint/lib/rules/no-loss-of-precision';
+import * as util from '../util';
+
+const baseRule = ((): typeof BaseRule | null => {
+ try {
+ return require('eslint/lib/rules/no-loss-of-precision');
+ } catch {
+ /* istanbul ignore next */
+ return null;
+ }
+})();
+
+type Options = util.InferOptionsTypeFromRule;
+type MessageIds = util.InferMessageIdsTypeFromRule;
+
+export default util.createRule({
+ name: 'no-loss-of-precision',
+ meta: {
+ type: 'problem',
+ docs: {
+ description: 'Disallow literal numbers that lose precision',
+ category: 'Possible Errors',
+ recommended: false,
+ extendsBaseRule: true,
+ },
+ schema: [],
+ messages: baseRule?.meta.messages ?? { noLossOfPrecision: '' },
+ },
+ defaultOptions: [],
+ create(context) {
+ /* istanbul ignore if */ if (baseRule === null) {
+ throw new Error(
+ '@typescript-eslint/no-loss-of-precision requires at least ESLint v7.1.0',
+ );
+ }
+
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
+ const rules = baseRule!.create(context);
+
+ function isSeperatedNumeric(node: TSESTree.Literal): boolean {
+ return typeof node.value === 'number' && node.raw.includes('_');
+ }
+ return {
+ Literal(node: TSESTree.Literal): void {
+ rules.Literal({
+ ...node,
+ raw: isSeperatedNumeric(node) ? node.raw.replace(/_/g, '') : node.raw,
+ });
+ },
+ };
+ },
+});
diff --git a/packages/eslint-plugin/tests/rules/no-loss-of-precision.test.ts b/packages/eslint-plugin/tests/rules/no-loss-of-precision.test.ts
new file mode 100644
index 00000000000..f2405bacdb6
--- /dev/null
+++ b/packages/eslint-plugin/tests/rules/no-loss-of-precision.test.ts
@@ -0,0 +1,36 @@
+import rule from '../../src/rules/no-loss-of-precision';
+import { RuleTester } from '../RuleTester';
+
+const ruleTester = new RuleTester({
+ parser: '@typescript-eslint/parser',
+});
+
+ruleTester.run('no-loss-of-precision', rule, {
+ valid: [
+ 'const x = 12345;',
+ 'const x = 123.456;',
+ 'const x = -123.456;',
+ 'const x = 123_456;',
+ 'const x = 123_00_000_000_000_000_000_000_000;',
+ 'const x = 123.000_000_000_000_000_000_000_0;',
+ ],
+ invalid: [
+ {
+ code: 'const x = 9007199254740993;',
+ errors: [{ messageId: 'noLossOfPrecision' }],
+ },
+ {
+ code: 'const x = 9_007_199_254_740_993;',
+ errors: [{ messageId: 'noLossOfPrecision' }],
+ },
+ {
+ code: 'const x = 9_007_199_254_740.993e3;',
+ errors: [{ messageId: 'noLossOfPrecision' }],
+ },
+ {
+ code:
+ 'const x = 0b100_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_001;',
+ errors: [{ messageId: 'noLossOfPrecision' }],
+ },
+ ],
+});
diff --git a/packages/eslint-plugin/typings/eslint-rules.d.ts b/packages/eslint-plugin/typings/eslint-rules.d.ts
index 31a752878b8..add5af1c1bb 100644
--- a/packages/eslint-plugin/typings/eslint-rules.d.ts
+++ b/packages/eslint-plugin/typings/eslint-rules.d.ts
@@ -702,3 +702,16 @@ declare module 'eslint/lib/rules/dot-notation' {
>;
export = rule;
}
+
+declare module 'eslint/lib/rules/no-loss-of-precision' {
+ import { TSESLint, TSESTree } from '@typescript-eslint/experimental-utils';
+
+ const rule: TSESLint.RuleModule<
+ 'noLossOfPrecision',
+ [],
+ {
+ Literal(node: TSESTree.Literal): void;
+ }
+ >;
+ export = rule;
+}
diff --git a/yarn.lock b/yarn.lock
index 9b6fedc38d8..337037083ad 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1750,7 +1750,7 @@ acorn@^6.0.1:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474"
integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==
-acorn@^7.1.0, acorn@^7.1.1:
+acorn@^7.1.0, acorn@^7.2.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.2.0.tgz#17ea7e40d7c8640ff54a694c889c26f31704effe"
integrity sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ==
@@ -3614,6 +3614,14 @@ eslint-scope@^5.0.0:
esrecurse "^4.1.0"
estraverse "^4.1.1"
+eslint-scope@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.0.tgz#d0f971dfe59c69e0cada684b23d49dbf82600ce5"
+ integrity sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==
+ dependencies:
+ esrecurse "^4.1.0"
+ estraverse "^4.1.1"
+
eslint-utils@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.0.0.tgz#7be1cc70f27a72a76cd14aa698bcabed6890e1cd"
@@ -3626,10 +3634,15 @@ eslint-visitor-keys@^1.1.0:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2"
integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==
-eslint@^7.0.0:
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.0.0.tgz#c35dfd04a4372110bd78c69a8d79864273919a08"
- integrity sha512-qY1cwdOxMONHJfGqw52UOpZDeqXy8xmD0u8CT6jIstil72jkhURC704W8CFyTPDPllz4z4lu0Ql1+07PG/XdIg==
+eslint-visitor-keys@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.2.0.tgz#74415ac884874495f78ec2a97349525344c981fa"
+ integrity sha512-WFb4ihckKil6hu3Dp798xdzSfddwKKU3+nGniKF6HfeW6OLd2OUDEPP7TcHtB5+QXOKg2s6B2DaMPE1Nn/kxKQ==
+
+eslint@^7.2.0:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.2.0.tgz#d41b2e47804b30dbabb093a967fb283d560082e6"
+ integrity sha512-B3BtEyaDKC5MlfDa2Ha8/D6DsS4fju95zs0hjS3HdGazw+LNayai38A25qMppK37wWGWNYSPOR6oYzlz5MHsRQ==
dependencies:
"@babel/code-frame" "^7.0.0"
ajv "^6.10.0"
@@ -3637,10 +3650,10 @@ eslint@^7.0.0:
cross-spawn "^7.0.2"
debug "^4.0.1"
doctrine "^3.0.0"
- eslint-scope "^5.0.0"
+ eslint-scope "^5.1.0"
eslint-utils "^2.0.0"
- eslint-visitor-keys "^1.1.0"
- espree "^7.0.0"
+ eslint-visitor-keys "^1.2.0"
+ espree "^7.1.0"
esquery "^1.2.0"
esutils "^2.0.2"
file-entry-cache "^5.0.1"
@@ -3668,14 +3681,14 @@ eslint@^7.0.0:
text-table "^0.2.0"
v8-compile-cache "^2.0.3"
-espree@^7.0.0:
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/espree/-/espree-7.0.0.tgz#8a7a60f218e69f120a842dc24c5a88aa7748a74e"
- integrity sha512-/r2XEx5Mw4pgKdyb7GNLQNsu++asx/dltf/CI8RFi9oGHxmQFgvLbc5Op4U6i8Oaj+kdslhJtVlEZeAqH5qOTw==
+espree@^7.1.0:
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/espree/-/espree-7.1.0.tgz#a9c7f18a752056735bf1ba14cb1b70adc3a5ce1c"
+ integrity sha512-dcorZSyfmm4WTuTnE5Y7MEN1DyoPYy1ZR783QW1FJoenn7RailyWFsq/UL6ZAAA7uXurN9FIpYyUs3OfiIW+Qw==
dependencies:
- acorn "^7.1.1"
+ acorn "^7.2.0"
acorn-jsx "^5.2.0"
- eslint-visitor-keys "^1.1.0"
+ eslint-visitor-keys "^1.2.0"
esprima@^2.7.0:
version "2.7.3"