Skip to content
This repository was archived by the owner on Jan 19, 2019. It is now read-only.

[FEAT] [no-semantic-errors] Add rule (Requires Type Info) #245

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ This guarantees 100% compatibility between the plugin and the parser.
| [`typescript/no-non-null-assertion`](./docs/rules/no-non-null-assertion.md) | Disallows non-null assertions using the `!` postfix operator (`no-non-null-assertion` from TSLint) | | |
| [`typescript/no-object-literal-type-assertion`](./docs/rules/no-object-literal-type-assertion.md) | Forbids an object literal to appear in a type assertion expression (`no-object-literal-type-assertion` from TSLint) | | |
| [`typescript/no-parameter-properties`](./docs/rules/no-parameter-properties.md) | Disallow the use of parameter properties in class constructors. (`no-parameter-properties` from TSLint) | | |
| [`typescript/no-semantic-errors`](./docs/rules/no-semantic-errors.md) | Enforces that there is no semantic and syntactic errors | | |
| [`typescript/no-this-alias`](./docs/rules/no-this-alias.md) | Disallow aliasing `this` (`no-this-assignment` from TSLint) | | |
| [`typescript/no-triple-slash-reference`](./docs/rules/no-triple-slash-reference.md) | Disallow `/// <reference path="" />` comments (`no-reference` from TSLint) | | |
| [`typescript/no-type-alias`](./docs/rules/no-type-alias.md) | Disallow the use of type aliases (`interface-over-type-literal` from TSLint) | | |
Expand Down
31 changes: 31 additions & 0 deletions docs/rules/no-semantic-errors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Enforces that there is no semantic and syntactic errors (no-semantic-errors)

This rule reports all semantic and type errors provided by diagnostics from typescript.

## Rule Details

Examples of **incorrect** code for this rule:

```ts
interface Foo {
hello: string;
}
const foo: string = ({ hello: 2 } as Foo)!.foo;
```

Examples of **correct** code for this rule:

```ts
interface Foo {
hello: string;
}
const foo: string = ({ hello: "Bar" } as Foo).hello;
```

### Options

```json
{
"typescript/no-this-alias": "no-semantic-errors"
}
```
56 changes: 56 additions & 0 deletions lib/rules/no-semantic-errors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* @fileoverview Enforces that there is no semantic and syntactic errors
* @author Armano <https://github.com/armano2>
*/
"use strict";

const util = require("../util");

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = {
meta: {
type: "problem",
docs: {
description:
"Enforces that there is no semantic and syntactic errors",
category: "TypeScript",
url: util.metaDocsUrl("no-semantic-errors"),
},
schema: [],
},

create(context) {
const sourceCode = context.getSourceCode();

const program = util.getParserServices(context).program;

return {
Program(node) {
const semantic = program.getSemanticDiagnostics() || [];
const syntactic = program.getSyntacticDiagnostics() || [];

const errors = semantic
.concat(syntactic)
// DiagnosticCategory.Error = 1,
.filter(error => error.category === 1);

for (const error of errors) {
const errorNode = error.start
? sourceCode.getNodeByRangeIndex(error.start)
: node;

context.report({
node: errorNode,
message:
typeof error.messageText === "object"
? error.messageText.messageText
: error.messageText,
});
}
},
};
},
};
18 changes: 18 additions & 0 deletions lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,24 @@ exports.metaDocsUrl = name =>
*/
exports.isTypescript = fileName => /\.tsx?$/.test(fileName);

/**
* Try to retrieve typescript parser service from context
* @param {RuleContext} context Rule context
* @returns {{esTreeNodeToTSNodeMap}|{program}|Object|*} parserServices
*/
exports.getParserServices = context => {
if (
!context.parserServices ||
!context.parserServices.program ||
!context.parserServices.esTreeNodeToTSNodeMap
) {
throw new Error(
"This rule requires you to use `typescript-eslint-parser`."
);
}
return context.parserServices;
};

/**
* Pure function - doesn't mutate either parameter!
* Merges two objects together deeply, overwriting the properties in first with the properties in second
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
},
"dependencies": {
"requireindex": "^1.2.0",
"typescript-eslint-parser": "21.0.2"
"typescript-eslint-parser": "git+https://github.com/uniqueiniquity/typescript-eslint-parser.git#0625f29a9721c6f10a7522de6c9d236a671bd1ac"
},
"devDependencies": {
"eslint": "^5.9.0",
Expand Down
Empty file.
3 changes: 3 additions & 0 deletions tests/lib/fixtures/empty/import.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default interface Foo {
name: string
}
9 changes: 9 additions & 0 deletions tests/lib/fixtures/empty/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"lib": ["es2015", "es2017"]
}
}
132 changes: 132 additions & 0 deletions tests/lib/rules/no-semantic-errors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/**
* @fileoverview Enforces that there is no semantic and syntactic errors
* @author Armano <https://github.com/armano2>
*/
"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const path = require("path");

const rule = require("../../../lib/rules/no-semantic-errors"),
RuleTester = require("eslint").RuleTester;

//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------

const rootPath = path.join(process.cwd(), "tests/lib/fixtures/empty");
// valid filePath is required to get access to lib
// @see https://github.com/JamesHenry/typescript-estree/issues/50
const filePath = path.join(rootPath, "empty.ts");

const ruleTester = new RuleTester({
parser: "typescript-eslint-parser",
parserOptions: {
generateServices: true,
tsconfigRootDir: rootPath,
project: "./tsconfig.json",
},
});

ruleTester.run("no-errors", rule, {
valid: [
{
filename: filePath,
code: `
import Foo from './import';
var foo: Foo = {
name: 'test'
};
`,
},
{
filename: filePath,
code: `
interface Foo {
hello: string;
}
const foo: string = ({ hello: 'Bar' } as Foo).hello
`,
},
{
filename: filePath,
code: `var foo: number = parseInt("5.5", 10) + 10;`,
},
],
invalid: [
{
filename: filePath,
code: `var foo: string = parseInt("5.5", 10) + 10;`,
errors: [
{
message:
"Type 'number' is not assignable to type 'string'.",
line: 1,
column: 5,
type: "Identifier",
},
],
},
{
filename: filePath,
code: `
import Foo from './import';
var foo: Foo = {
name: 2
};
`,
errors: [
{
message:
"Type 'number' is not assignable to type 'string'.",
line: 4,
column: 5,
type: "Identifier",
},
],
},
{
filename: filePath,
code: `
import Foo from './not-found';
var foo: Foo = {
name: 2
};
`,
errors: [
{
message: "Cannot find module './not-found'.",
line: 2,
column: 17,
type: "Literal",
},
],
},
{
filename: filePath,
code: `
interface Foo {
hello: string;
}
const foo: string = ({ hello: 2 } as Foo)!.foo
`,
errors: [
{
message:
"Conversion of type '{ hello: number; }' to type 'Foo' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.",
line: 5,
column: 22,
type: "ObjectExpression",
},
{
message: "Property 'foo' does not exist on type 'Foo'.",
line: 5,
column: 44,
type: "Identifier",
},
],
},
],
});
17 changes: 8 additions & 9 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2996,15 +2996,6 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=

[email protected]:
version "21.0.2"
resolved "https://registry.yarnpkg.com/typescript-eslint-parser/-/typescript-eslint-parser-21.0.2.tgz#270af10e4724528677fbcf34ea495284bec3a894"
integrity sha512-u+pj4RVJBr4eTzj0n5npoXD/oRthvfUCjSKndhNI714MG0mQq2DJw5WP7qmonRNIFgmZuvdDOH3BHm9iOjIAfg==
dependencies:
eslint-scope "^4.0.0"
eslint-visitor-keys "^1.0.0"
typescript-estree "5.3.0"

typescript-eslint-parser@^16.0.0:
version "16.0.1"
resolved "https://registry.yarnpkg.com/typescript-eslint-parser/-/typescript-eslint-parser-16.0.1.tgz#b40681c7043b222b9772748b700a000b241c031b"
Expand All @@ -3013,6 +3004,14 @@ typescript-eslint-parser@^16.0.0:
lodash.unescape "4.0.1"
semver "5.5.0"

"typescript-eslint-parser@git+https://github.com/uniqueiniquity/typescript-eslint-parser.git#0625f29a9721c6f10a7522de6c9d236a671bd1ac":
version "21.0.2"
resolved "git+https://github.com/uniqueiniquity/typescript-eslint-parser.git#0625f29a9721c6f10a7522de6c9d236a671bd1ac"
dependencies:
eslint-scope "^4.0.0"
eslint-visitor-keys "^1.0.0"
typescript-estree "5.3.0"

[email protected]:
version "5.3.0"
resolved "https://registry.yarnpkg.com/typescript-estree/-/typescript-estree-5.3.0.tgz#fb6c977b5e21073eb16cbdc0338a7f752da99ff5"
Expand Down