diff --git a/.eslintrc.json b/.eslintrc.json index 40a5182652a..ce78adc0b64 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -30,7 +30,7 @@ { "files": [ "packages/eslint-plugin-tslint/tests/**/*.ts", - "packages/eslint-plugin/tests/**/*.js", + "packages/eslint-plugin/tests/**/*.test.ts", "packages/parser/tests/**/*.ts", "packages/typescript-estree/tests/**/*.ts" ], @@ -53,22 +53,9 @@ } }, { - "files": ["packages/eslint-plugin/**/*.js"], + "files": ["packages/eslint-plugin/test/**/*.ts"], "rules": { - "eslint-plugin/fixer-return": "error", - "eslint-plugin/no-identical-tests": "error", - "eslint-plugin/no-missing-placeholders": "error", - "eslint-plugin/no-unused-placeholders": "error", - "eslint-plugin/no-useless-token-range": "error", - "eslint-plugin/require-meta-fixable": "error", - "eslint-plugin/prefer-placeholders": "error", - "eslint-plugin/prefer-replace-text": "error", - "eslint-plugin/no-deprecated-report-api": "error", - "eslint-plugin/report-message-format": ["error", "^[A-Z'{].*[\\.}]$"], - "eslint-plugin/no-deprecated-context-methods": "error", - "eslint-plugin/prefer-output-null": "error", - "eslint-plugin/test-case-shorthand-strings": "error", - "eslint-plugin/require-meta-type": "error" + "eslint-plugin/no-identical-tests": "error" } } ] diff --git a/.gitignore b/.gitignore index 41847c7fd60..55599f0c184 100644 --- a/.gitignore +++ b/.gitignore @@ -36,9 +36,6 @@ build/Release node_modules/ jspm_packages/ -# TypeScript v1 declaration files -typings/ - # Optional npm cache directory .npm diff --git a/.vscode/launch.json b/.vscode/launch.json index 4ac7bc13cdd..15c6b1bbb3d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -11,9 +11,10 @@ "cwd": "${workspaceFolder}/packages/eslint-plugin/", "program": "${workspaceFolder}/node_modules/jest/bin/jest.js", "args": [ - "--colors", - "tests/lib/rules/${fileBasename}" + "--runInBand", + "tests/rules/${fileBasenameNoExtension}" ], + "sourceMaps": true, "console": "integratedTerminal", "internalConsoleOptions": "neverOpen" } diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 805b5c0a1ee..eee318c9eb8 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -17,6 +17,12 @@ jobs: # bootstrap yarn --ignore-engines --frozen-lockfile + - script: | + # Note that this command *also* typechecks tests/tools, + # whereas the build only checks src files + yarn typecheck + displayName: 'Typecheck all packages' + - script: | yarn check-format displayName: 'Check code formatting' diff --git a/package.json b/package.json index 301ec05cfdf..f2185043436 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "contributors": [ "James Henry ", "Nicholas C. Zakas", - "Brad Zacher", + "Brad Zacher ", "armano2", "Jed Fox" ], @@ -21,6 +21,7 @@ "test": "lerna run test --parallel", "build": "lerna run build", "clean": "lerna clean && lerna run clean", + "typecheck": "lerna run typecheck", "lint": "eslint . --ext .js,.ts", "lint-fix": "eslint . --ext .js,.ts --fix", "cz": "git-cz", diff --git a/packages/eslint-plugin-tslint/package.json b/packages/eslint-plugin-tslint/package.json index c69652e9c65..651b858a004 100644 --- a/packages/eslint-plugin-tslint/package.json +++ b/packages/eslint-plugin-tslint/package.json @@ -21,8 +21,9 @@ "scripts": { "test": "jest --coverage", "prebuild": "npm run clean", - "build": "tsc", - "clean": "rimraf dist/" + "build": "tsc -p tsconfig.build.json", + "clean": "rimraf dist/", + "typecheck": "tsc --noEmit" }, "dependencies": { "lodash.memoize": "^4.1.2" diff --git a/packages/eslint-plugin-tslint/tsconfig.build.json b/packages/eslint-plugin-tslint/tsconfig.build.json new file mode 100644 index 00000000000..b0fced27d72 --- /dev/null +++ b/packages/eslint-plugin-tslint/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist" + }, + "include": ["src"] +} diff --git a/packages/eslint-plugin-tslint/tsconfig.json b/packages/eslint-plugin-tslint/tsconfig.json index d74357950e9..7db2d0520ff 100644 --- a/packages/eslint-plugin-tslint/tsconfig.json +++ b/packages/eslint-plugin-tslint/tsconfig.json @@ -1,8 +1,5 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "declaration": true, - "outDir": "./dist" - }, - "include": ["src"] + "extends": "./tsconfig.build.json", + "include": ["src", "tests"], + "exclude": ["tests/test-project", "tests/test-tslint-rules-directory"] } diff --git a/packages/eslint-plugin/.npmignore b/packages/eslint-plugin/.npmignore deleted file mode 100644 index 3d63d33280c..00000000000 --- a/packages/eslint-plugin/.npmignore +++ /dev/null @@ -1,6 +0,0 @@ -**/* -!lib/**/* -!docs/**/* -!package.json -!parser.js -!README.md diff --git a/packages/eslint-plugin/jest.config.js b/packages/eslint-plugin/jest.config.js index 8c1bc93c7f0..2210a4dcb89 100644 --- a/packages/eslint-plugin/jest.config.js +++ b/packages/eslint-plugin/jest.config.js @@ -5,9 +5,9 @@ module.exports = { transform: { '^.+\\.tsx?$': 'ts-jest' }, - testRegex: './tests/lib/.+\\.js$', + testRegex: './tests/.+\\.test\\.ts$', collectCoverage: false, - collectCoverageFrom: ['lib/**/*.{js,jsx,ts,tsx}'], + collectCoverageFrom: ['src/**/*.{js,jsx,ts,tsx}'], moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], coverageReporters: ['text-summary', 'lcov'] }; diff --git a/packages/eslint-plugin/lib/index.js b/packages/eslint-plugin/lib/index.js deleted file mode 100644 index 1dcee891d94..00000000000 --- a/packages/eslint-plugin/lib/index.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * @fileoverview TypeScript plugin for ESLint - * @author Nicholas C. Zakas - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const requireIndex = require('requireindex'); -const path = require('path'); - -//------------------------------------------------------------------------------ -// Plugin Definition -//------------------------------------------------------------------------------ - -// import all rules in lib/rules -module.exports = { - rules: requireIndex(path.join(__dirname, 'rules')), - configs: { - // eslint-disable-next-line node/no-unpublished-require - recommended: require('./configs/recommended') - } -}; diff --git a/packages/eslint-plugin/lib/rules/adjacent-overload-signatures.js b/packages/eslint-plugin/lib/rules/adjacent-overload-signatures.js deleted file mode 100644 index 0c53769c0e8..00000000000 --- a/packages/eslint-plugin/lib/rules/adjacent-overload-signatures.js +++ /dev/null @@ -1,138 +0,0 @@ -/** - * @fileoverview Enforces member overloads to be consecutive. - * @author Patricio Trevino - */ -'use strict'; - -const util = require('../util'); - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -module.exports = { - meta: { - type: 'suggestion', - docs: { - description: 'Require that member overloads be consecutive', - category: 'TypeScript', - extraDescription: [util.tslintRule('adjacent-overload-signatures')], - url: util.metaDocsUrl('adjacent-overload-signatures'), - recommended: 'error' - }, - schema: [], - messages: { - adjacentSignature: "All '{{name}}' signatures should be adjacent." - } - }, - - create(context) { - //---------------------------------------------------------------------- - // Helpers - //---------------------------------------------------------------------- - - /** - * Gets the name of the member being processed. - * @param {ASTNode} member the member being processed. - * @returns {string|null} the name of the member or null if it's a member not relevant to the rule. - * @private - */ - function getMemberName(member) { - if (!member) return null; - - switch (member.type) { - case 'ExportDefaultDeclaration': - case 'ExportNamedDeclaration': { - // export statements (e.g. export { a };) - // have no declarations, so ignore them - return member.declaration ? getMemberName(member.declaration) : null; - } - case 'TSDeclareFunction': - case 'FunctionDeclaration': - case 'TSNamespaceFunctionDeclaration': { - return member.id && member.id.name; - } - case 'TSMethodSignature': { - return ( - (member.key && (member.key.name || member.key.value)) || - (member.name && (member.name.name || member.name.value)) - ); - } - case 'TSCallSignatureDeclaration': { - return 'call'; - } - case 'TSConstructSignatureDeclaration': { - return 'new'; - } - case 'MethodDefinition': { - return member.key.name || member.key.value; - } - default: { - return null; - } - } - } - - /** - * Determine whether two methods are the same or not - * @param {{ name: string; static: boolean }} method1 a method to compare - * @param {{ name: string; static: boolean }} method2 another method to compare with - * @returns {boolean} true if two methods are the same - * @private - */ - function isSameMethod(method1, method2) { - return method1.name === method2.name && method1.static === method2.static; - } - - /** - * Check the body for overload methods. - * @param {ASTNode} node the body to be inspected. - * @returns {void} - * @private - */ - function checkBodyForOverloadMethods(node) { - const members = node.body || node.members; - - if (members) { - let lastMethod; - const seenMethods = []; - - members.forEach(member => { - const name = getMemberName(member); - const method = { - name, - static: member.static - }; - - const index = seenMethods.findIndex(seenMethod => - isSameMethod(method, seenMethod) - ); - if (index > -1 && !isSameMethod(method, lastMethod)) { - context.report({ - node: member, - messageId: 'adjacentSignature', - data: { - name: (method.static ? 'static ' : '') + method.name - } - }); - } else if (name && index === -1) { - seenMethods.push(method); - } - - lastMethod = method; - }); - } - } - - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- - return { - TSModuleBlock: checkBodyForOverloadMethods, - TSTypeLiteral: checkBodyForOverloadMethods, - TSInterfaceBody: checkBodyForOverloadMethods, - ClassBody: checkBodyForOverloadMethods, - Program: checkBodyForOverloadMethods - }; - } -}; diff --git a/packages/eslint-plugin/lib/rules/class-name-casing.js b/packages/eslint-plugin/lib/rules/class-name-casing.js deleted file mode 100644 index 3b8db844efa..00000000000 --- a/packages/eslint-plugin/lib/rules/class-name-casing.js +++ /dev/null @@ -1,96 +0,0 @@ -/** - * @fileoverview Enforces PascalCased class and interface names. - * @author Jed Fox - * @author Armano - */ -'use strict'; - -const util = require('../util'); - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -module.exports = { - meta: { - type: 'suggestion', - docs: { - description: 'Require PascalCased class and interface names', - extraDescription: [util.tslintRule('class-name')], - category: 'Best Practices', - url: util.metaDocsUrl('class-name-casing'), - recommended: 'error' - }, - schema: [] - }, - - create(context) { - // variables should be defined here - - //---------------------------------------------------------------------- - // Helpers - //---------------------------------------------------------------------- - - /** - * Determine if the identifier name is PascalCased - * @param {string} name The identifier name - * @returns {boolean} Is the name PascalCased? - */ - function isPascalCase(name) { - return /^[A-Z][0-9A-Za-z]*$/.test(name); - } - - /** - * Report a class declaration as invalid - * @param {Node} decl The declaration - * @param {Node} [id=classDecl.id] The name of the declaration - * @returns {undefined} - */ - function report(decl, id) { - const resolvedId = id || decl.id; - - let friendlyName; - - switch (decl.type) { - case 'ClassDeclaration': - case 'ClassExpression': - friendlyName = decl.abstract ? 'Abstract class' : 'Class'; - break; - case 'TSInterfaceDeclaration': - friendlyName = 'Interface'; - break; - default: - friendlyName = decl.type; - } - - context.report({ - node: resolvedId, - message: "{{friendlyName}} '{{name}}' must be PascalCased.", - data: { - friendlyName, - name: resolvedId.name - } - }); - } - - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- - - return { - 'ClassDeclaration, TSInterfaceDeclaration, ClassExpression'(node) { - // class expressions (i.e. export default class {}) are OK - if (node.id && !isPascalCase(node.id.name)) { - report(node); - } - }, - "VariableDeclarator[init.type='ClassExpression']"(node) { - const id = node.id; - - if (id && !node.init.id && !isPascalCase(id.name)) { - report(node.init, id); - } - } - }; - } -}; diff --git a/packages/eslint-plugin/lib/rules/explicit-function-return-type.js b/packages/eslint-plugin/lib/rules/explicit-function-return-type.js deleted file mode 100644 index 0253d80d3c9..00000000000 --- a/packages/eslint-plugin/lib/rules/explicit-function-return-type.js +++ /dev/null @@ -1,118 +0,0 @@ -/** - * @fileoverview Enforces explicit return type for functions - * @author Scott O'Hara - */ -'use strict'; - -const util = require('../util'); - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -const defaultOptions = [ - { - allowExpressions: true - } -]; - -module.exports = { - meta: { - type: 'problem', - docs: { - description: - 'Require explicit return types on functions and class methods', - category: 'TypeScript', - url: util.metaDocsUrl('explicit-function-return-type'), - recommended: 'warn' - }, - schema: [ - { - type: 'object', - properties: { - allowExpressions: { - type: 'boolean' - } - }, - additionalProperties: false - } - ] - }, - - create(context) { - const options = util.applyDefault(defaultOptions, context.options)[0]; - - //---------------------------------------------------------------------- - // Helpers - //---------------------------------------------------------------------- - - /** - * Checks if the parent of a function expression is a constructor. - * @param {ASTNode} parent The parent of a function expression node - * @returns {boolean} `true` if the parent is a constructor - * @private - */ - function isConstructor(parent) { - return ( - parent.type === 'MethodDefinition' && parent.kind === 'constructor' - ); - } - - /** - * Checks if the parent of a function expression is a setter. - * @param {ASTNode} parent The parent of a function expression node - * @returns {boolean} `true` if the parent is a setter - * @private - */ - function isSetter(parent) { - return parent.type === 'MethodDefinition' && parent.kind === 'set'; - } - - /** - * Checks if a function declaration/expression has a return type. - * @param {ASTNode} node The node representing a function. - * @returns {void} - * @private - */ - function checkFunctionReturnType(node) { - if ( - !node.returnType && - !isConstructor(node.parent) && - !isSetter(node.parent) && - util.isTypescript(context.getFilename()) - ) { - context.report({ - node, - message: `Missing return type on function.` - }); - } - } - - /** - * Checks if a function declaration/expression has a return type. - * @param {ASTNode} node The node representing a function. - * @returns {void} - * @private - */ - function checkFunctionExpressionReturnType(node) { - if ( - options.allowExpressions && - node.parent.type !== 'VariableDeclarator' && - node.parent.type !== 'MethodDefinition' - ) { - return; - } - - checkFunctionReturnType(node); - } - - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- - return { - FunctionDeclaration: checkFunctionReturnType, - FunctionExpression: checkFunctionExpressionReturnType, - ArrowFunctionExpression: checkFunctionExpressionReturnType - }; - } -}; diff --git a/packages/eslint-plugin/lib/rules/explicit-member-accessibility.js b/packages/eslint-plugin/lib/rules/explicit-member-accessibility.js deleted file mode 100644 index e3f041ea2ff..00000000000 --- a/packages/eslint-plugin/lib/rules/explicit-member-accessibility.js +++ /dev/null @@ -1,83 +0,0 @@ -/** - * @fileoverview Enforces explicit accessibility modifier for class members - * @author Danny Fritz - */ -'use strict'; - -const util = require('../util'); - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -module.exports = { - meta: { - type: 'problem', - docs: { - description: - 'Require explicit accessibility modifiers on class properties and methods', - extraDescription: [util.tslintRule('member-access')], - category: 'TypeScript', - url: util.metaDocsUrl('explicit-member-accessibility'), - recommended: 'error' - }, - schema: [] - }, - - create(context) { - //---------------------------------------------------------------------- - // Helpers - //---------------------------------------------------------------------- - - /** - * Checks if a method declaration has an accessibility modifier. - * @param {ASTNode} methodDefinition The node representing a MethodDefinition. - * @returns {void} - * @private - */ - function checkMethodAccessibilityModifier(methodDefinition) { - if ( - !methodDefinition.accessibility && - util.isTypescript(context.getFilename()) - ) { - context.report({ - node: methodDefinition, - message: - 'Missing accessibility modifier on method definition {{name}}.', - data: { - name: methodDefinition.key.name - } - }); - } - } - - /** - * Checks if property has an accessibility modifier. - * @param {ASTNode} classProperty The node representing a ClassProperty. - * @returns {void} - * @private - */ - function checkPropertyAccessibilityModifier(classProperty) { - if ( - !classProperty.accessibility && - util.isTypescript(context.getFilename()) - ) { - context.report({ - node: classProperty, - message: 'Missing accessibility modifier on class property {{name}}.', - data: { - name: classProperty.key.name - } - }); - } - } - - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- - return { - ClassProperty: checkPropertyAccessibilityModifier, - MethodDefinition: checkMethodAccessibilityModifier - }; - } -}; diff --git a/packages/eslint-plugin/lib/rules/generic-type-naming.js b/packages/eslint-plugin/lib/rules/generic-type-naming.js deleted file mode 100644 index f14c8f50e89..00000000000 --- a/packages/eslint-plugin/lib/rules/generic-type-naming.js +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @fileoverview Enforces naming of generic type variables. - */ -'use strict'; - -const util = require('../util'); - -const defaultOptions = [ - // Matches: T , TA , TAbc , TA1Bca , T1 , T2 - '^T([A-Z0-9][a-zA-Z0-9]*){0,1}$' -]; - -module.exports = { - meta: { - type: 'suggestion', - docs: { - description: 'Enforces naming of generic type variables', - category: 'TypeScript', - url: util.metaDocsUrl('generic-type-naming') - }, - messages: { - paramNotMatchRule: 'Type parameter {{name}} does not match rule {{rule}}.' - }, - schema: [ - { - type: 'string' - } - ], - recommended: 'error' - }, - - create(context) { - const rule = util.applyDefault(defaultOptions, context.options)[0]; - const regex = new RegExp(rule); - - return { - TSTypeParameter(node) { - const name = - node.name && node.name.type === 'Identifier' ? node.name.name : null; - - if (name && !regex.test(name)) { - const data = { name, rule }; - - context.report({ - node, - messageId: 'paramNotMatchRule', - data - }); - } - } - }; - } -}; diff --git a/packages/eslint-plugin/lib/rules/indent.js b/packages/eslint-plugin/lib/rules/indent.js deleted file mode 100644 index 84144219faf..00000000000 --- a/packages/eslint-plugin/lib/rules/indent.js +++ /dev/null @@ -1,399 +0,0 @@ -/** - * @fileoverview Rule to flag non-camelcased identifiers - * @author Patricio Trevino - */ -'use strict'; - -const baseRule = require('eslint/lib/rules/indent'); -const util = require('../util'); - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -const KNOWN_NODES = new Set([ - // Class properties aren't yet supported by eslint... - 'ClassProperty', - - // ts keywords - 'TSAbstractKeyword', - 'TSAnyKeyword', - 'TSBooleanKeyword', - 'TSNeverKeyword', - 'TSNumberKeyword', - 'TSStringKeyword', - 'TSSymbolKeyword', - 'TSUndefinedKeyword', - 'TSUnknownKeyword', - 'TSVoidKeyword', - 'TSNullKeyword', - - // ts specific nodes we want to support - 'TSAbstractClassProperty', - 'TSAbstractMethodDefinition', - 'TSArrayType', - 'TSAsExpression', - 'TSCallSignatureDeclaration', - 'TSConditionalType', - 'TSConstructorType', - 'TSConstructSignatureDeclaration', - 'TSDeclareFunction', - 'TSEmptyBodyFunctionExpression', - 'TSEnumDeclaration', - 'TSEnumMember', - 'TSExportAssignment', - 'TSExternalModuleReference', - 'TSFunctionType', - 'TSImportType', - 'TSIndexedAccessType', - 'TSIndexSignature', - 'TSInferType', - 'TSInterfaceBody', - 'TSInterfaceDeclaration', - 'TSInterfaceHeritage', - 'TSIntersectionType', - 'TSImportEqualsDeclaration', - 'TSLiteralType', - 'TSMappedType', - 'TSMethodSignature', - 'TSMinusToken', - 'TSModuleBlock', - 'TSModuleDeclaration', - 'TSNonNullExpression', - 'TSParameterProperty', - 'TSParenthesizedType', - 'TSPlusToken', - 'TSPropertySignature', - 'TSQualifiedName', - 'TSQuestionToken', - 'TSRestType', - 'TSThisType', - 'TSTupleType', - 'TSTypeAnnotation', - 'TSTypeLiteral', - 'TSTypeOperator', - 'TSTypeParameter', - 'TSTypeParameterDeclaration', - 'TSTypeReference', - 'TSUnionType' -]); - -const defaultOptions = [ - // typescript docs and playground use 4 space indent - 4, - { - // typescript docs indent the case from the switch - // https://www.typescriptlang.org/docs/handbook/release-notes/typescript-1-8.html#example-4 - SwitchCase: 1, - flatTernaryExpressions: false, - ignoredNodes: [] - } -]; - -module.exports = Object.assign({}, baseRule, { - meta: { - type: 'layout', - docs: { - description: 'Enforce consistent indentation', - extraDescription: [util.tslintRule('indent')], - category: 'Stylistic Issues', - recommended: 'error', - url: util.metaDocsUrl('indent') - }, - fixable: 'whitespace', - schema: baseRule.meta.schema, - messages: baseRule.meta.messages - }, - - create(context) { - // because we extend the base rule, have to update opts on the context - // the context defines options as readonly though... - const contextWithDefaults = Object.create(context, { - options: { - writable: false, - configurable: false, - value: util.applyDefault(defaultOptions, context.options) - } - }); - - const rules = baseRule.create(contextWithDefaults); - - /** - * Converts from a TSPropertySignature to a Property - * @param {Object} node a TSPropertySignature node - * @param {string} [type] the type to give the new node - * @returns {Object} a Property node - */ - function TSPropertySignatureToProperty(node, type = 'Property') { - return { - type, - key: node.key, - value: node.value || node.typeAnnotation, - - // Property flags - computed: false, - method: false, - kind: 'init', - // this will stop eslint from interrogating the type literal - shorthand: true, - - // location data - parent: node.parent, - range: node.range, - loc: node.loc - }; - } - - return Object.assign({}, rules, { - // overwrite the base rule here so we can use our KNOWN_NODES list instead - '*:exit'(node) { - // For nodes we care about, skip the default handling, because it just marks the node as ignored... - if (!KNOWN_NODES.has(node.type)) { - rules['*:exit'](node); - } - }, - - TSAsExpression(node) { - // transform it to a BinaryExpression - return rules['BinaryExpression, LogicalExpression']({ - type: 'BinaryExpression', - operator: 'as', - left: node.expression, - // the first typeAnnotation includes the as token - right: node.typeAnnotation, - - // location data - parent: node.parent, - range: node.range, - loc: node.loc - }); - }, - - TSConditionalType(node) { - // transform it to a ConditionalExpression - return rules.ConditionalExpression({ - type: 'ConditionalExpression', - test: { - type: 'BinaryExpression', - operator: 'extends', - left: node.checkType, - right: node.extendsType, - - // location data - range: [node.checkType.range[0], node.extendsType.range[1]], - loc: { - start: node.checkType.loc.start, - end: node.extendsType.loc.end - } - }, - consequent: node.trueType, - alternate: node.falseType, - - // location data - parent: node.parent, - range: node.range, - loc: node.loc - }); - }, - - 'TSEnumDeclaration, TSTypeLiteral'(node) { - // transform it to an ObjectExpression - return rules['ObjectExpression, ObjectPattern']({ - type: 'ObjectExpression', - properties: node.members.map(TSPropertySignatureToProperty), - - // location data - parent: node.parent, - range: node.range, - loc: node.loc - }); - }, - - TSImportEqualsDeclaration(node) { - // transform it to an VariableDeclaration - // use VariableDeclaration instead of ImportDeclaration because it's essentially the same thing - const { id, moduleReference } = node; - - return rules.VariableDeclaration({ - type: 'VariableDeclaration', - declarations: [ - { - type: 'VariableDeclarator', - range: [id.range[0], moduleReference.range[1]], - loc: { - start: id.loc.start, - end: moduleReference.loc.end - }, - id: id, - init: { - type: 'CallExpression', - callee: { - type: 'Identifier', - name: 'require', - range: [ - moduleReference.range[0], - moduleReference.range[0] + 'require'.length - ], - loc: { - start: moduleReference.loc.start, - end: { - line: moduleReference.loc.end.line, - column: moduleReference.loc.start + 'require'.length - } - } - }, - arguments: [moduleReference.expression], - - // location data - range: moduleReference.range, - loc: moduleReference.loc - } - } - ], - - // location data - parent: node.parent, - range: node.range, - loc: node.loc - }); - }, - - TSIndexedAccessType(node) { - // convert to a MemberExpression - return rules['MemberExpression, JSXMemberExpression, MetaProperty']({ - type: 'MemberExpression', - object: node.objectType, - property: node.indexType, - - // location data - parent: node.parent, - range: node.range, - loc: node.loc - }); - }, - - TSInterfaceBody(node) { - // transform it to an ClassBody - return rules['BlockStatement, ClassBody']({ - type: 'ClassBody', - body: node.body.map(p => - TSPropertySignatureToProperty(p, 'ClassProperty') - ), - - // location data - parent: node.parent, - range: node.range, - loc: node.loc - }); - }, - - 'TSInterfaceDeclaration[extends.length > 0]'(node) { - // transform it to a ClassDeclaration - return rules[ - 'ClassDeclaration[superClass], ClassExpression[superClass]' - ]({ - type: 'ClassDeclaration', - body: node.body, - // TODO: This is invalid, there can be more than one extends in interface - superClass: node.extends[0].expression, - - // location data - parent: node.parent, - range: node.range, - loc: node.loc - }); - }, - - TSMappedType(node) { - const sourceCode = context.getSourceCode(); - const squareBracketStart = sourceCode.getTokenBefore( - node.typeParameter - ); - - // transform it to an ObjectExpression - return rules['ObjectExpression, ObjectPattern']({ - type: 'ObjectExpression', - properties: [ - { - type: 'Property', - key: node.typeParameter, - value: node.typeAnnotation, - - // location data - range: [ - squareBracketStart.range[0], - node.typeAnnotation.range[1] - ], - loc: { - start: squareBracketStart.loc.start, - end: node.typeAnnotation.loc.end - } - } - ], - - // location data - parent: node.parent, - range: node.range, - loc: node.loc - }); - }, - - TSModuleBlock(node) { - // transform it to a BlockStatement - return rules['BlockStatement, ClassBody']({ - type: 'BlockStatement', - body: node.body, - - // location data - parent: node.parent, - range: node.range, - loc: node.loc - }); - }, - - TSQualifiedName(node) { - return rules['MemberExpression, JSXMemberExpression, MetaProperty']({ - type: 'MemberExpression', - object: node.left, - property: node.right, - - // location data - parent: node.parent, - range: node.range, - loc: node.loc - }); - }, - - TSTupleType(node) { - // transform it to an ArrayExpression - return rules['ArrayExpression, ArrayPattern']({ - type: 'ArrayExpression', - elements: node.elementTypes, - - // location data - parent: node.parent, - range: node.range, - loc: node.loc - }); - }, - - TSTypeParameterDeclaration(node) { - const [name, ...attributes] = node.params; - - // JSX is about the closest we can get because the angle brackets - // it's not perfect but it works! - return rules.JSXOpeningElement({ - type: 'JSXOpeningElement', - selfClosing: false, - name, - attributes, - - // location data - parent: node.parent, - range: node.range, - loc: node.loc - }); - } - }); - } -}); diff --git a/packages/eslint-plugin/lib/rules/interface-name-prefix.js b/packages/eslint-plugin/lib/rules/interface-name-prefix.js deleted file mode 100644 index fc5d0da8229..00000000000 --- a/packages/eslint-plugin/lib/rules/interface-name-prefix.js +++ /dev/null @@ -1,85 +0,0 @@ -/** - * @fileoverview Enforces interface names are prefixed with "I". - * @author Danny Fritz - */ -'use strict'; - -const util = require('../util'); - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -const defaultOptions = ['never']; - -module.exports = { - meta: { - type: 'suggestion', - docs: { - description: 'Require that interface names be prefixed with `I`', - extraDescription: [util.tslintRule('interface-name')], - category: 'TypeScript', - url: util.metaDocsUrl('interface-name-prefix'), - recommended: 'error' - }, - schema: [ - { - enum: ['never', 'always'] - } - ] - }, - - create(context) { - const option = util.applyDefault(defaultOptions, context.options)[0]; - const never = option !== 'always'; - - //---------------------------------------------------------------------- - // Helpers - //---------------------------------------------------------------------- - - /** - * Checks if a string is prefixed with "I". - * @param {string} name The string to check - * @returns {boolean} true if it is prefixed with "I" - * @private - */ - function isPrefixedWithI(name) { - if (typeof name !== 'string') { - return false; - } - - return /^I[A-Z]/.test(name); - } - - /** - * Checks if interface is prefixed with "I". - * @param {ASTNode} interfaceNode The node representing an Interface. - * @returns {void} - * @private - */ - function checkInterfacePrefix(interfaceNode) { - if (never) { - if (isPrefixedWithI(interfaceNode.id.name)) { - context.report({ - node: interfaceNode.id, - message: 'Interface name must not be prefixed with "I".' - }); - } - } else { - if (!isPrefixedWithI(interfaceNode.id.name)) { - context.report({ - node: interfaceNode.id, - message: 'Interface name must be prefixed with "I".' - }); - } - } - } - - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- - return { - TSInterfaceDeclaration: checkInterfacePrefix - }; - } -}; diff --git a/packages/eslint-plugin/lib/rules/no-angle-bracket-type-assertion.js b/packages/eslint-plugin/lib/rules/no-angle-bracket-type-assertion.js deleted file mode 100644 index f030079c235..00000000000 --- a/packages/eslint-plugin/lib/rules/no-angle-bracket-type-assertion.js +++ /dev/null @@ -1,46 +0,0 @@ -/** - * @fileoverview Enforces the use of `as Type` assertions instead of `` assertions. - * @author Patricio Trevino - */ -'use strict'; - -const util = require('../util'); - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -module.exports = { - meta: { - type: 'problem', - docs: { - description: - 'Enforces the use of `as Type` assertions instead of `` assertions', - extraDescription: [util.tslintRule('no-angle-bracket-type-assertion')], - category: 'Style', - url: util.metaDocsUrl('no-angle-bracket-type-assertion'), - recommended: 'error' - }, - schema: [] - }, - - create(context) { - const sourceCode = context.getSourceCode(); - - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- - return { - TSTypeAssertion(node) { - context.report({ - node, - message: - "Prefer 'as {{cast}}' instead of '<{{cast}}>' when doing type assertions.", - data: { - cast: sourceCode.getText(node.typeAnnotation) - } - }); - } - }; - } -}; diff --git a/packages/eslint-plugin/lib/rules/no-empty-interface.js b/packages/eslint-plugin/lib/rules/no-empty-interface.js deleted file mode 100644 index 8caf42a9f62..00000000000 --- a/packages/eslint-plugin/lib/rules/no-empty-interface.js +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @fileoverview Disallows the declaration of empty interfaces. - * @author Patricio Trevino - */ -'use strict'; - -const util = require('../util'); - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -module.exports = { - meta: { - type: 'suggestion', - docs: { - description: 'Disallow the declaration of empty interfaces', - extraDescription: [util.tslintRule('no-empty-interface')], - category: 'TypeScript', - url: util.metaDocsUrl('no-empty-interface'), - recommended: 'error' - }, - schema: [] - }, - - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- - - create(context) { - return { - TSInterfaceDeclaration(node) { - if (node.body.body.length !== 0) { - return; - } - let message; - if (!node.extends || node.extends.length === 0) { - message = 'An empty interface is equivalent to `{}`.'; - } else if (node.extends.length === 1) { - message = - 'An interface declaring no members is equivalent to its supertype.'; - } - if (!message) { - return; - } - context.report({ - node: node.id, - message - }); - } - }; - } -}; diff --git a/packages/eslint-plugin/lib/rules/no-explicit-any.js b/packages/eslint-plugin/lib/rules/no-explicit-any.js deleted file mode 100644 index b8b77fe063b..00000000000 --- a/packages/eslint-plugin/lib/rules/no-explicit-any.js +++ /dev/null @@ -1,37 +0,0 @@ -/** - * @fileoverview Enforces the any type is not used. - * @author Danny Fritz - * @author Patricio Trevino - */ -'use strict'; - -const util = require('../util'); - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -module.exports = { - meta: { - type: 'suggestion', - docs: { - description: 'Disallow usage of the `any` type', - extraDescription: [util.tslintRule('no-any')], - category: 'TypeScript', - url: util.metaDocsUrl('no-explicit-any'), - recommended: 'warn' - }, - schema: [] - }, - - create(context) { - return { - TSAnyKeyword(node) { - context.report({ - node, - message: 'Unexpected any. Specify a different type.' - }); - } - }; - } -}; diff --git a/packages/eslint-plugin/lib/rules/no-inferrable-types.js b/packages/eslint-plugin/lib/rules/no-inferrable-types.js deleted file mode 100644 index a6318bf9bb5..00000000000 --- a/packages/eslint-plugin/lib/rules/no-inferrable-types.js +++ /dev/null @@ -1,191 +0,0 @@ -/** - * @fileoverview Disallows explicit type declarations for inferrable types - * @author James Garbutt - */ -'use strict'; - -const util = require('../util'); - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -const defaultOptions = [ - { - ignoreParameters: true, - ignoreProperties: true - } -]; - -module.exports = { - meta: { - type: 'suggestion', - docs: { - description: - 'Disallows explicit type declarations for variables or parameters initialized to a number, string, or boolean.', - extraDescription: [util.tslintRule('no-inferrable-types')], - category: 'TypeScript', - url: util.metaDocsUrl('no-inferrable-types'), - recommended: 'error' - }, - fixable: 'code', - schema: [ - { - type: 'object', - properties: { - ignoreParameters: { - type: 'boolean' - }, - ignoreProperties: { - type: 'boolean' - } - }, - additionalProperties: false - } - ] - }, - - create(context) { - const { ignoreParameters, ignoreProperties } = util.applyDefault( - defaultOptions, - context.options - )[0]; - - /** - * Returns whether a node has an inferrable value or not - * @param {ASTNode} node the node to check - * @param {ASTNode} init the initializer - * @returns {boolean} whether the node has an inferrable type - */ - function isInferrable(node, init) { - if (node.type !== 'TSTypeAnnotation' || !node.typeAnnotation) { - return false; - } - - if (!init) { - return false; - } - - const annotation = node.typeAnnotation; - - if (annotation.type === 'TSStringKeyword') { - return ( - (init.type === 'Literal' && typeof init.value === 'string') || - (init.type === 'TemplateElement' && - (!init.expressions || init.expressions.length === 0)) - ); - } - - if (annotation.type === 'TSBooleanKeyword') { - return init.type === 'Literal'; - } - - if (annotation.type === 'TSNumberKeyword') { - // Infinity is special - if ( - (init.type === 'UnaryExpression' && - init.operator === '-' && - init.argument.type === 'Identifier' && - init.argument.name === 'Infinity') || - (init.type === 'Identifier' && init.name === 'Infinity') - ) { - return true; - } - - return init.type === 'Literal' && typeof init.value === 'number'; - } - - return false; - } - - /** - * Reports an inferrable type declaration, if any - * @param {ASTNode} node the node being visited - * @param {ASTNode} typeNode the type annotation node - * @param {ASTNode} initNode the initializer node - * @returns {void} - */ - function reportInferrableType(node, typeNode, initNode) { - if (!typeNode || !initNode || !typeNode.typeAnnotation) { - return; - } - - if (!isInferrable(typeNode, initNode)) { - return; - } - - const typeMap = { - TSBooleanKeyword: 'boolean', - TSNumberKeyword: 'number', - TSStringKeyword: 'string' - }; - - const type = typeMap[typeNode.typeAnnotation.type]; - - context.report({ - node, - message: - 'Type {{type}} trivially inferred from a {{type}} literal, remove type annotation.', - data: { - type - }, - fix: fixer => fixer.remove(typeNode) - }); - } - - /** - * Visits variables - * @param {ASTNode} node the node to be visited - * @returns {void} - */ - function inferrableVariableVisitor(node) { - if (!node.id) { - return; - } - reportInferrableType(node, node.id.typeAnnotation, node.init); - } - - /** - * Visits parameters - * @param {ASTNode} node the node to be visited - * @returns {void} - */ - function inferrableParameterVisitor(node) { - if (ignoreParameters || !node.params) { - return; - } - node.params - .filter( - param => - param.type === 'AssignmentPattern' && param.left && param.right - ) - .forEach(param => { - reportInferrableType(param, param.left.typeAnnotation, param.right); - }); - } - - /** - * Visits properties - * @param {ASTNode} node the node to be visited - * @returns {void} - */ - function inferrablePropertyVisitor(node) { - // We ignore `readonly` because of Microsoft/TypeScript#14416 - // Essentially a readonly property without a type - // will result in its value being the type, leading to - // compile errors if the type is stripped. - if (ignoreProperties || node.readonly) { - return; - } - reportInferrableType(node, node.typeAnnotation, node.value); - } - - return { - VariableDeclarator: inferrableVariableVisitor, - FunctionExpression: inferrableParameterVisitor, - FunctionDeclaration: inferrableParameterVisitor, - ArrowFunctionExpression: inferrableParameterVisitor, - ClassProperty: inferrablePropertyVisitor - }; - } -}; diff --git a/packages/eslint-plugin/lib/rules/no-misused-new.js b/packages/eslint-plugin/lib/rules/no-misused-new.js deleted file mode 100644 index bb4132289c5..00000000000 --- a/packages/eslint-plugin/lib/rules/no-misused-new.js +++ /dev/null @@ -1,99 +0,0 @@ -/** - * @fileoverview Enforce valid definition of `new` and `constructor`. - * @author Armano - */ -'use strict'; - -const util = require('../util'); - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -module.exports = { - meta: { - type: 'problem', - docs: { - description: 'Enforce valid definition of `new` and `constructor`.', - extraDescription: [util.tslintRule('no-misused-new')], - category: 'TypeScript', - url: util.metaDocsUrl('no-misused-new'), - recommended: 'error' - }, - schema: [], - messages: { - errorMessageInterface: 'Interfaces cannot be constructed, only classes.', - errorMessageClass: 'Class cannon have method named `new`.' - } - }, - - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- - - create(context) { - /** - * @param {ASTNode} node type to be inspected. - * @returns {string|null} name of simple type or null - */ - function getTypeReferenceName(node) { - if (node) { - switch (node.type) { - case 'TSTypeAnnotation': - return getTypeReferenceName(node.typeAnnotation); - case 'TSTypeReference': - return getTypeReferenceName(node.typeName); - case 'Identifier': - return node.name; - default: - break; - } - } - return null; - } - - /** - * @param {ASTNode} parent parent node. - * @param {ASTNode} returnType type to be compared - * @returns {boolean} returns true if type is parent type - */ - function isMatchingParentType(parent, returnType) { - if (parent && parent.id && parent.id.type === 'Identifier') { - return getTypeReferenceName(returnType) === parent.id.name; - } - return false; - } - - return { - 'TSInterfaceBody > TSConstructSignatureDeclaration'(node) { - if (isMatchingParentType(node.parent.parent, node.returnType)) { - // constructor - context.report({ - node, - messageId: 'errorMessageInterface' - }); - } - }, - "TSMethodSignature[key.name='constructor']"(node) { - context.report({ - node, - messageId: 'errorMessageInterface' - }); - }, - "ClassBody > MethodDefinition[key.name='new']"(node) { - if ( - node.value && - (node.value.type === 'TSEmptyBodyFunctionExpression' || - (node.value.type === 'TSDeclareFunction' && !node.value.body)) - ) { - if (isMatchingParentType(node.parent.parent, node.value.returnType)) { - context.report({ - node, - messageId: 'errorMessageClass' - }); - } - } - } - }; - } -}; diff --git a/packages/eslint-plugin/lib/rules/no-non-null-assertion.js b/packages/eslint-plugin/lib/rules/no-non-null-assertion.js deleted file mode 100644 index e67b6b7675b..00000000000 --- a/packages/eslint-plugin/lib/rules/no-non-null-assertion.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @fileoverview Disallows non-null assertions using the `!` postfix operator. - * @author Macklin Underdown - */ -'use strict'; - -const util = require('../util'); - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -module.exports = { - meta: { - type: 'problem', - docs: { - description: - 'Disallows non-null assertions using the `!` postfix operator', - extraDescription: [util.tslintRule('no-non-null-assertion')], - category: 'TypeScript', - url: util.metaDocsUrl('no-non-null-assertion'), - recommended: 'error' - }, - schema: [] - }, - create(context) { - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- - - return { - TSNonNullExpression(node) { - context.report({ - node, - message: 'Forbidden non-null assertion.' - }); - } - }; - } -}; diff --git a/packages/eslint-plugin/lib/rules/no-object-literal-type-assertion.js b/packages/eslint-plugin/lib/rules/no-object-literal-type-assertion.js deleted file mode 100644 index 11258a99315..00000000000 --- a/packages/eslint-plugin/lib/rules/no-object-literal-type-assertion.js +++ /dev/null @@ -1,96 +0,0 @@ -/** - * @fileoverview Forbids an object literal to appear in a type assertion expression - * @author Armano - */ -'use strict'; - -const util = require('../util'); - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -const defaultOptions = [ - { - allowAsParameter: false - } -]; - -module.exports = { - meta: { - type: 'problem', - docs: { - description: - 'Forbids an object literal to appear in a type assertion expression', - extraDescription: [util.tslintRule('no-object-literal-type-assertion')], - category: 'TypeScript', - url: util.metaDocsUrl('no-object-literal-type-assertions'), - recommended: 'error' - }, - messages: { - unexpectedTypeAssertion: - 'Type assertion on object literals is forbidden, use a type annotation instead.' - }, - schema: [ - { - type: 'object', - additionalProperties: false, - properties: { - allowAsParameter: { - type: 'boolean' - } - } - } - ] - }, - create(context) { - const { allowAsParameter } = util.applyDefault( - defaultOptions, - context.options - )[0]; - - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- - - /** - * Check whatever node should be reported - * @param {ASTNode} node the node to be evaluated. - * @returns {*} true or false - */ - function checkType(node) { - if (node) { - switch (node.type) { - case 'TSAnyKeyword': - case 'TSUnknownKeyword': - return false; - default: - break; - } - } - return true; - } - - return { - 'TSTypeAssertion, TSAsExpression'(node) { - if ( - allowAsParameter && - (node.parent.type === 'NewExpression' || - node.parent.type === 'CallExpression') - ) { - return; - } - - if ( - checkType(node.typeAnnotation) && - node.expression.type === 'ObjectExpression' - ) { - context.report({ - node, - messageId: 'unexpectedTypeAssertion' - }); - } - } - }; - } -}; diff --git a/packages/eslint-plugin/lib/rules/no-parameter-properties.js b/packages/eslint-plugin/lib/rules/no-parameter-properties.js deleted file mode 100644 index d41066a8aa6..00000000000 --- a/packages/eslint-plugin/lib/rules/no-parameter-properties.js +++ /dev/null @@ -1,99 +0,0 @@ -/** - * @fileoverview Disallows parameter properties in class constructors. - * @author Patricio Trevino - */ -'use strict'; - -const util = require('../util'); - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -const defaultOptions = [ - { - allows: [] - } -]; - -module.exports = { - meta: { - type: 'problem', - docs: { - description: - 'Disallow the use of parameter properties in class constructors.', - extraDescription: [util.tslintRule('no-parameter-properties')], - category: 'TypeScript', - url: util.metaDocsUrl('no-parameter-properties'), - recommended: 'error' - }, - schema: [ - { - type: 'object', - properties: { - allows: { - type: 'array', - items: { - enum: [ - 'readonly', - 'private', - 'protected', - 'public', - 'private readonly', - 'protected readonly', - 'public readonly' - ] - }, - minItems: 1 - } - }, - additionalProperties: false - } - ] - }, - - create(context) { - const { allows } = util.applyDefault(defaultOptions, context.options)[0]; - - //---------------------------------------------------------------------- - // Helpers - //---------------------------------------------------------------------- - - /** - * Gets the modifiers of `node`. - * @param {ASTNode} node the node to be inspected. - * @returns {string} the modifiers of node - * @private - */ - function getModifiers(node) { - const modifiers = []; - - modifiers.push(node.accessibility); - if (node.readonly || node.isReadonly) { - modifiers.push('readonly'); - } - - return modifiers.filter(Boolean).join(' '); - } - - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- - return { - TSParameterProperty(node) { - const modifiers = getModifiers(node); - - if (allows.indexOf(modifiers) === -1) { - context.report({ - node, - message: - 'Property {{parameter}} cannot be declared in the constructor.', - data: { - parameter: node.parameter.name - } - }); - } - } - }; - } -}; diff --git a/packages/eslint-plugin/lib/rules/no-require-imports.js b/packages/eslint-plugin/lib/rules/no-require-imports.js deleted file mode 100644 index 82537a4dcda..00000000000 --- a/packages/eslint-plugin/lib/rules/no-require-imports.js +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @fileoverview Disallows invocation of `require()`. - * @author Kanitkorn Sujautra - */ -'use strict'; - -const util = require('../util'); - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -module.exports = { - meta: { - type: 'problem', - docs: { - description: 'Disallows invocation of `require()`.', - extraDescription: [util.tslintRule('no-require-imports')], - category: 'TypeScript', - url: util.metaDocsUrl('no-require-imports'), - recommended: 'error' - }, - schema: [], - messages: { - noRequireImports: 'A `require()` style import is forbidden.' - } - }, - create(context) { - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- - - return { - CallExpression(node) { - if (node.callee.name === 'require') { - context.report({ - node, - messageId: 'noRequireImports' - }); - } - }, - TSExternalModuleReference(node) { - context.report({ - node, - messageId: 'noRequireImports' - }); - } - }; - } -}; diff --git a/packages/eslint-plugin/lib/rules/no-this-alias.js b/packages/eslint-plugin/lib/rules/no-this-alias.js deleted file mode 100644 index fd247ebe654..00000000000 --- a/packages/eslint-plugin/lib/rules/no-this-alias.js +++ /dev/null @@ -1,77 +0,0 @@ -/** - * @fileoverview Disallow aliasing `this` - * @author Jed Fox - */ -'use strict'; - -const util = require('../util'); - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -const defaultOptions = [ - { - allowDestructuring: false, - allowedNames: [] - } -]; - -module.exports = { - meta: { - type: 'suggestion', - docs: { - description: 'Disallow aliasing `this`', - extraDescription: [util.tslintRule('no-this-assignment')], - category: 'Best Practices', - url: util.metaDocsUrl('no-this-alias'), - recommended: false - }, - fixable: null, - schema: [ - { - type: 'object', - additionalProperties: false, - properties: { - allowDestructuring: { - type: 'boolean' - }, - allowedNames: { - type: 'array', - items: { - type: 'string' - } - } - } - } - ], - messages: { - thisAssignment: "Unexpected aliasing of 'this' to local variable.", - thisDestructure: - "Unexpected aliasing of members of 'this' to local variables." - } - }, - - create(context) { - const { allowDestructuring, allowedNames } = util.applyDefault( - defaultOptions, - context.options - )[0]; - - return { - "VariableDeclarator[init.type='ThisExpression']"(node) { - const { id } = node; - - if (allowDestructuring && id.type !== 'Identifier') return; - - if (!allowedNames.includes(id.name)) { - context.report({ - node: id, - messageId: - id.type === 'Identifier' ? 'thisAssignment' : 'thisDestructure' - }); - } - } - }; - } -}; diff --git a/packages/eslint-plugin/lib/rules/no-triple-slash-reference.js b/packages/eslint-plugin/lib/rules/no-triple-slash-reference.js deleted file mode 100644 index 90086159057..00000000000 --- a/packages/eslint-plugin/lib/rules/no-triple-slash-reference.js +++ /dev/null @@ -1,66 +0,0 @@ -/** - * @fileoverview Enforces triple slash references are not used. - * @author Danny Fritz - */ -'use strict'; - -const util = require('../util'); - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -module.exports = { - meta: { - type: 'suggestion', - docs: { - description: 'Disallow `/// ` comments', - extraDescription: [util.tslintRule('no-reference')], - category: 'TypeScript', - url: util.metaDocsUrl('no-triple-slash-reference'), - recommended: 'error' - }, - schema: [], - messages: { - tripleSlashReference: 'Do not use a triple slash reference.' - } - }, - - create(context) { - const referenceRegExp = /^\/\s* { - if (comment.type !== 'Line') { - return; - } - if (referenceRegExp.test(comment.value)) { - context.report({ - node: comment, - messageId: 'tripleSlashReference' - }); - } - }); - } - - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- - return { - Program: checkTripleSlashReference - }; - } -}; diff --git a/packages/eslint-plugin/lib/rules/no-var-requires.js b/packages/eslint-plugin/lib/rules/no-var-requires.js deleted file mode 100644 index 40859f42d86..00000000000 --- a/packages/eslint-plugin/lib/rules/no-var-requires.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * @fileoverview Disallows the use of require statements except in import statements. - * @author Macklin Underdown - */ -'use strict'; - -const util = require('../util'); - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -module.exports = { - meta: { - type: 'problem', - docs: { - description: - 'Disallows the use of require statements except in import statements', - extraDescription: [util.tslintRule('no-var-requires')], - category: 'TypeScript', - url: util.metaDocsUrl('no-var-requires'), - recommended: 'error' - }, - schema: [] - }, - create(context) { - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- - - return { - CallExpression(node) { - if ( - node.callee.name === 'require' && - node.parent.type === 'VariableDeclarator' - ) { - context.report({ - node, - message: 'Require statement not part of import statement.' - }); - } - } - }; - } -}; diff --git a/packages/eslint-plugin/lib/rules/prefer-function-type.js b/packages/eslint-plugin/lib/rules/prefer-function-type.js deleted file mode 100644 index a34b7ba4c1a..00000000000 --- a/packages/eslint-plugin/lib/rules/prefer-function-type.js +++ /dev/null @@ -1,171 +0,0 @@ -/** - * @fileoverview Use function types instead of interfaces with call signatures - * @author Benjamin Lichtman - */ -'use strict'; -const util = require('../util'); - -/** - * @typedef {import("eslint").Rule.RuleModule} RuleModule - * @typedef {import("estree").Node} ESTreeNode - */ - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -/** - * @type {RuleModule} - */ -module.exports = { - meta: { - docs: { - description: - 'Use function types instead of interfaces with call signatures', - category: 'TypeScript', - recommended: false, - extraDescription: [util.tslintRule('callable-types')], - url: util.metaDocsUrl('prefer-function-type') - }, - fixable: 'code', - messages: { - functionTypeOverCallableType: - "{{ type }} has only a call signature - use '{{ sigSuggestion }}' instead." - }, - schema: [], - type: 'suggestion' - }, - - create(context) { - const sourceCode = context.getSourceCode(); - - //---------------------------------------------------------------------- - // Helpers - //---------------------------------------------------------------------- - - /** - * Checks if there is no supertype or if the supertype is 'Function' - * @param {ESTreeNode} node The node being checked - * @returns {boolean} Returns true iff there is no supertype or if the supertype is 'Function' - */ - function noSupertype(node) { - if (!node.extends || node.extends.length === 0) { - return true; - } - if (node.extends.length !== 1) { - return false; - } - const expr = node.extends[0].expression; - - return expr.type === 'Identifier' && expr.name === 'Function'; - } - - /** - * @param {ESTreeNode} parent The parent of the call signature causing the diagnostic - * @returns {boolean} true iff the parent node needs to be wrapped for readability - */ - function shouldWrapSuggestion(parent) { - switch (parent.type) { - case 'TSUnionType': - case 'TSIntersectionType': - case 'TSArrayType': - return true; - default: - return false; - } - } - - /** - * @param {ESTreeNode} call The call signature causing the diagnostic - * @param {ESTreeNode} parent The parent of the call - * @returns {string} The suggestion to report - */ - function renderSuggestion(call, parent) { - const start = call.range[0]; - const colonPos = call.returnType.range[0] - start; - const text = sourceCode.getText().slice(start, call.range[1]); - - let suggestion = `${text.slice(0, colonPos)} =>${text.slice( - colonPos + 1 - )}`; - - if (shouldWrapSuggestion(parent.parent)) { - suggestion = `(${suggestion})`; - } - if (parent.type === 'TSInterfaceDeclaration') { - if (typeof parent.typeParameters !== 'undefined') { - return `type ${sourceCode - .getText() - .slice( - parent.id.range[0], - parent.typeParameters.range[1] - )} = ${suggestion}`; - } - return `type ${parent.id.name} = ${suggestion}`; - } - return suggestion.endsWith(';') ? suggestion.slice(0, -1) : suggestion; - } - - /** - * @param {ESTreeNode} member The TypeElement being checked - * @param {ESTreeNode} node The parent of member being checked - * @returns {void} - */ - function checkMember(member, node) { - if ( - (member.type === 'TSCallSignatureDeclaration' || - member.type === 'TSConstructSignatureDeclaration') && - typeof member.returnType !== 'undefined' - ) { - const suggestion = renderSuggestion(member, node); - const fixStart = - node.type === 'TSTypeLiteral' - ? node.range[0] - : sourceCode - .getTokens(node) - .filter( - token => - token.type === 'Keyword' && token.value === 'interface' - )[0].range[0]; - - context.report({ - node: member, - messageId: 'functionTypeOverCallableType', - data: { - type: node.type === 'TSTypeLiteral' ? 'Type literal' : 'Interface', - sigSuggestion: suggestion - }, - fix(fixer) { - return fixer.replaceTextRange( - [fixStart, node.range[1]], - suggestion - ); - } - }); - } - } - - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- - - return { - /** - * @param {TSInterfaceDeclaration} node The node being checked - * @returns {void} - */ - TSInterfaceDeclaration(node) { - if (noSupertype(node) && node.body.body.length === 1) { - checkMember(node.body.body[0], node); - } - }, - /** - * @param {TSTypeLiteral} node The node being checked - * @returns {void} - */ - 'TSTypeLiteral[members.length = 1]'(node) { - checkMember(node.members[0], node); - } - }; - } -}; diff --git a/packages/eslint-plugin/lib/util.js b/packages/eslint-plugin/lib/util.js deleted file mode 100644 index 6714e600ee6..00000000000 --- a/packages/eslint-plugin/lib/util.js +++ /dev/null @@ -1,129 +0,0 @@ -'use strict'; - -const version = require('../package.json').version; - -exports.tslintRule = name => `\`${name}\` from TSLint`; - -exports.metaDocsUrl = name => - `https://github.com/typescript-eslint/typescript-eslint/blob/v${version}/packages/eslint-plugin/docs/rules/${name}.md`; - -/** - * Check if the context file name is *.ts or *.tsx - * @param {string} fileName The context file name - * @returns {boolean} `true` if the file name ends in *.ts or *.tsx - * @private - */ -exports.isTypescript = fileName => /\.tsx?$/i.test(fileName || ''); - -/** - * Check if the context file name is *.d.ts or *.d.ts - * @param {string} fileName The context file name - * @returns {boolean} `true` if the file name ends in *.d.ts or *.d.ts - * @private - */ -exports.isDefinitionFile = fileName => /\.d\.tsx?$/i.test(fileName || ''); - -/** - * Check if the variable contains an object stricly rejecting arrays - * @param {any} obj an object - * @returns {boolean} `true` if obj is an object - */ -function isObjectNotArray(obj) { - return typeof obj === 'object' && !Array.isArray(obj); -} - -/** - * Pure function - doesn't mutate either parameter! - * Merges two objects together deeply, overwriting the properties in first with the properties in second - * @template TFirst,TSecond - * @param {TFirst} first The first object - * @param {TSecond} second The second object - * @returns {Record} a new object - */ -function deepMerge(first = {}, second = {}) { - // get the unique set of keys across both objects - const keys = new Set(Object.keys(first).concat(Object.keys(second))); - - return Array.from(keys).reduce((acc, key) => { - const firstHasKey = key in first; - const secondHasKey = key in second; - - if (firstHasKey && secondHasKey) { - if (isObjectNotArray(first[key]) && isObjectNotArray(second[key])) { - // object type - acc[key] = deepMerge(first[key], second[key]); - } else { - // value type - acc[key] = second[key]; - } - } else if (firstHasKey) { - acc[key] = first[key]; - } else { - acc[key] = second[key]; - } - - return acc; - }, {}); -} -exports.deepMerge = deepMerge; - -/** - * Pure function - doesn't mutate either parameter! - * Uses the default options and overrides with the options provided by the user - * @template TOptions - * @param {TOptions} defaultOptions the defaults - * @param {any[]} userOptions the user opts - * @returns {TOptions} the options with defaults - */ -exports.applyDefault = (defaultOptions, userOptions) => { - // clone defaults - const options = JSON.parse(JSON.stringify(defaultOptions)); - - // eslint-disable-next-line eqeqeq - if (userOptions == null) { - return options; - } - - options.forEach((opt, i) => { - if (userOptions[i]) { - const userOpt = userOptions[i]; - - if (isObjectNotArray(userOpt) && isObjectNotArray(opt)) { - options[i] = deepMerge(opt, userOpt); - } else { - options[i] = userOpt; - } - } - }); - - return options; -}; - -/** - * Upper cases the first character or the string - * @param {string} str a string - * @returns {string} upper case first - */ -exports.upperCaseFirst = str => str[0].toUpperCase() + str.slice(1); - -/** - * Try to retrieve typescript parser service from context - * @param {RuleContext} context Rule context - * @returns {{program: Program, esTreeNodeToTSNodeMap: NodeMap}} parserServices - */ -exports.getParserServices = context => { - if ( - !context.parserServices || - !context.parserServices.program || - !context.parserServices.esTreeNodeToTSNodeMap - ) { - /** - * The user needs to have configured "project" in their parserOptions - * for @typescript-eslint/parser - */ - throw new Error( - `You have used a rule which requires parserServices to be generated. You must therefore provide a value for the "parserOptions.project" property for @typescript-eslint/parser.` - ); - } - return context.parserServices; -}; diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 7b1c3d4bc20..5f3b1647ec2 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -11,26 +11,38 @@ "engines": { "node": "^6.14.0 || ^8.10.0 || >=9.10.0" }, + "files": [ + "dist", + "docs", + "package.json", + "README.md", + "LICENSE" + ], "repository": "typescript-eslint/typescript-eslint", "bugs": { "url": "https://github.com/typescript-eslint/typescript-eslint/issues" }, "license": "MIT", - "main": "lib/index.js", + "main": "dist/index.js", "scripts": { "docs": "eslint-docs", "docs:check": "eslint-docs check", "test": "jest --coverage", - "recommended:update": "node tools/update-recommended.js" + "recommended:update": "ts-node tools/update-recommended.ts", + "prebuild": "npm run clean", + "build": "tsc -p tsconfig.build.json", + "clean": "rimraf dist/", + "typecheck": "tsc --noEmit" }, "dependencies": { "@typescript-eslint/parser": "1.3.0", + "@typescript-eslint/typescript-estree": "1.3.0", "requireindex": "^1.2.0", "tsutils": "^3.7.0" }, "devDependencies": { - "eslint": "^5.9.0", - "eslint-docs": "^0.2.6" + "eslint-docs": "^0.2.6", + "ts-node": "^8.0.1" }, "peerDependencies": { "eslint": "^5.0.0", diff --git a/packages/eslint-plugin/lib/configs/recommended.json b/packages/eslint-plugin/src/configs/recommended.json similarity index 97% rename from packages/eslint-plugin/lib/configs/recommended.json rename to packages/eslint-plugin/src/configs/recommended.json index cd21778bfff..e107a645724 100644 --- a/packages/eslint-plugin/lib/configs/recommended.json +++ b/packages/eslint-plugin/src/configs/recommended.json @@ -3,9 +3,7 @@ "parserOptions": { "sourceType": "module" }, - "plugins": [ - "@typescript-eslint" - ], + "plugins": ["@typescript-eslint"], "rules": { "@typescript-eslint/adjacent-overload-signatures": "error", "@typescript-eslint/array-type": "error", diff --git a/packages/eslint-plugin/src/index.ts b/packages/eslint-plugin/src/index.ts new file mode 100644 index 00000000000..f18b90cf41e --- /dev/null +++ b/packages/eslint-plugin/src/index.ts @@ -0,0 +1,27 @@ +/** + * @fileoverview TypeScript plugin for ESLint + * @author Nicholas C. Zakas + */ + +import requireIndex from 'requireindex'; +import path from 'path'; + +import recommended from './configs/recommended.json'; + +const rules = requireIndex(path.join(__dirname, 'rules')); +// eslint expects the rule to be on rules[name], not rules[name].default +const rulesWithoutDefault = Object.keys(rules).reduce>( + (acc, ruleName) => { + acc[ruleName] = rules[ruleName].default; + return acc; + }, + {} +); + +// import all rules in lib/rules +export = { + rules: rulesWithoutDefault, + configs: { + recommended + } +}; diff --git a/packages/eslint-plugin/src/rules/adjacent-overload-signatures.ts b/packages/eslint-plugin/src/rules/adjacent-overload-signatures.ts new file mode 100644 index 00000000000..912b91ce7e0 --- /dev/null +++ b/packages/eslint-plugin/src/rules/adjacent-overload-signatures.ts @@ -0,0 +1,148 @@ +/** + * @fileoverview Enforces member overloads to be consecutive. + * @author Patricio Trevino + */ + +import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/typescript-estree'; +import * as util from '../util'; + +type RuleNode = + | TSESTree.ClassBody + | TSESTree.Program + | TSESTree.TSModuleBlock + | TSESTree.TSTypeLiteral + | TSESTree.TSInterfaceBody; +type Member = TSESTree.ClassElement | TSESTree.Statement | TSESTree.TypeElement; + +export default util.createRule({ + name: 'adjacent-overload-signatures', + meta: { + type: 'suggestion', + docs: { + description: 'Require that member overloads be consecutive', + category: 'Best Practices', + tslintName: 'adjacent-overload-signatures', + recommended: 'error' + }, + schema: [], + messages: { + adjacentSignature: "All '{{name}}' signatures should be adjacent." + } + }, + defaultOptions: [], + create(context) { + /** + * Gets the name of the member being processed. + * @param member the member being processed. + * @returns the name of the member or null if it's a member not relevant to the rule. + */ + function getMemberName(member: TSESTree.Node): string | null { + if (!member) { + return null; + } + + switch (member.type) { + case AST_NODE_TYPES.ExportDefaultDeclaration: + case AST_NODE_TYPES.ExportNamedDeclaration: { + // export statements (e.g. export { a };) + // have no declarations, so ignore them + if (!member.declaration) { + return null; + } + + return getMemberName(member.declaration); + } + case AST_NODE_TYPES.TSDeclareFunction: + case AST_NODE_TYPES.FunctionDeclaration: + return member.id && member.id.name; + case AST_NODE_TYPES.TSMethodSignature: + return util.getNameFromPropertyName(member.key); + case AST_NODE_TYPES.TSCallSignatureDeclaration: + return 'call'; + case AST_NODE_TYPES.TSConstructSignatureDeclaration: + return 'new'; + case AST_NODE_TYPES.MethodDefinition: + return util.getNameFromPropertyName(member.key); + } + + return null; + } + + interface Method { + name: string; + static: boolean; + } + function isSameMethod(method1: Method, method2: Method | null): boolean { + return ( + !!method2 && + method1.name === method2.name && + method1.static === method2.static + ); + } + + function getMembers(node: RuleNode): Member[] { + switch (node.type) { + case AST_NODE_TYPES.ClassBody: + case AST_NODE_TYPES.Program: + case AST_NODE_TYPES.TSModuleBlock: + case AST_NODE_TYPES.TSInterfaceBody: + return node.body; + + case AST_NODE_TYPES.TSTypeLiteral: + return node.members; + } + + return []; + } + + /** + * Check the body for overload methods. + * @param {ASTNode} node the body to be inspected. + */ + function checkBodyForOverloadMethods(node: RuleNode): void { + const members = getMembers(node); + + if (members) { + let lastMethod: Method | null = null; + const seenMethods: Method[] = []; + + members.forEach(member => { + const name = getMemberName(member); + if (name === null) { + lastMethod = null; + return; + } + const method = { + name, + static: 'static' in member && !!member.static + }; + + const index = seenMethods.findIndex(seenMethod => + isSameMethod(method, seenMethod) + ); + if (index > -1 && !isSameMethod(method, lastMethod)) { + context.report({ + node: member, + messageId: 'adjacentSignature', + data: { + name: (method.static ? 'static ' : '') + method.name + } + }); + } else if (index === -1) { + seenMethods.push(method); + } + + lastMethod = method; + }); + } + } + + return { + ClassBody: checkBodyForOverloadMethods, + Program: checkBodyForOverloadMethods, + TSModuleBlock: checkBodyForOverloadMethods, + TSTypeLiteral: checkBodyForOverloadMethods, + TSInterfaceBody: checkBodyForOverloadMethods + }; + } +}); diff --git a/packages/eslint-plugin/lib/rules/array-type.js b/packages/eslint-plugin/src/rules/array-type.ts similarity index 62% rename from packages/eslint-plugin/lib/rules/array-type.js rename to packages/eslint-plugin/src/rules/array-type.ts index ba7d83a5a7f..39b3715c47c 100644 --- a/packages/eslint-plugin/lib/rules/array-type.js +++ b/packages/eslint-plugin/src/rules/array-type.ts @@ -3,37 +3,40 @@ * @author Mackie Underdown * @author Armano */ -'use strict'; -const util = require('../util'); +import { + AST_NODE_TYPES, + AST_TOKEN_TYPES, + TSESTree +} from '@typescript-eslint/typescript-estree'; +import * as util from '../util'; /** * Check whatever node can be considered as simple - * @param {ASTNode} node the node to be evaluated. - * @returns {*} true or false + * @param node the node to be evaluated. */ -function isSimpleType(node) { +function isSimpleType(node: TSESTree.Node): boolean { switch (node.type) { - case 'Identifier': - case 'TSAnyKeyword': - case 'TSBooleanKeyword': - case 'TSNeverKeyword': - case 'TSNumberKeyword': - case 'TSObjectKeyword': - case 'TSStringKeyword': - case 'TSSymbolKeyword': - case 'TSUnknownKeyword': - case 'TSVoidKeyword': - case 'TSNullKeyword': - case 'TSArrayType': - case 'TSUndefinedKeyword': - case 'TSThisType': - case 'TSQualifiedName': + case AST_NODE_TYPES.Identifier: + case AST_NODE_TYPES.TSAnyKeyword: + case AST_NODE_TYPES.TSBooleanKeyword: + case AST_NODE_TYPES.TSNeverKeyword: + case AST_NODE_TYPES.TSNumberKeyword: + case AST_NODE_TYPES.TSObjectKeyword: + case AST_NODE_TYPES.TSStringKeyword: + case AST_NODE_TYPES.TSSymbolKeyword: + case AST_NODE_TYPES.TSUnknownKeyword: + case AST_NODE_TYPES.TSVoidKeyword: + case AST_NODE_TYPES.TSNullKeyword: + case AST_NODE_TYPES.TSArrayType: + case AST_NODE_TYPES.TSUndefinedKeyword: + case AST_NODE_TYPES.TSThisType: + case AST_NODE_TYPES.TSQualifiedName: return true; - case 'TSTypeReference': + case AST_NODE_TYPES.TSTypeReference: if ( node.typeName && - node.typeName.type === 'Identifier' && + node.typeName.type === AST_NODE_TYPES.Identifier && node.typeName.name === 'Array' ) { if (!node.typeParameters) { @@ -56,38 +59,38 @@ function isSimpleType(node) { /** * Check if node needs parentheses - * @param {ASTNode} node the node to be evaluated. - * @returns {*} true or false + * @param node the node to be evaluated. */ -function typeNeedsParentheses(node) { +function typeNeedsParentheses(node: TSESTree.Node): boolean { switch (node.type) { - case 'TSTypeReference': + case AST_NODE_TYPES.TSTypeReference: return typeNeedsParentheses(node.typeName); - case 'TSUnionType': - case 'TSFunctionType': - case 'TSIntersectionType': - case 'TSTypeOperator': - case 'TSInferType': + case AST_NODE_TYPES.TSUnionType: + case AST_NODE_TYPES.TSFunctionType: + case AST_NODE_TYPES.TSIntersectionType: + case AST_NODE_TYPES.TSTypeOperator: + case AST_NODE_TYPES.TSInferType: return true; default: return false; } } -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ +type Options = ['array' | 'generic' | 'array-simple']; +type MessageIds = + | 'errorStringGeneric' + | 'errorStringGenericSimple' + | 'errorStringArray' + | 'errorStringArraySimple'; -const defaultOptions = ['array']; - -module.exports = { +export default util.createRule({ + name: 'array-type', meta: { type: 'suggestion', docs: { description: 'Requires using either `T[]` or `Array` for arrays', - extraDescription: [util.tslintRule('array-type')], - category: 'TypeScript', - url: util.metaDocsUrl('array-type'), + tslintRuleName: 'array-type', + category: 'Stylistic Issues', recommended: 'error' }, fixable: 'code', @@ -107,32 +110,33 @@ module.exports = { } ] }, - create(context) { - const option = util.applyDefault(defaultOptions, context.options)[0]; + defaultOptions: ['array'], + create(context, [option]) { const sourceCode = context.getSourceCode(); /** * Check if whitespace is needed before this node - * @param {ASTNode} node the node to be evaluated. - * @returns {boolean} true of false + * @param node the node to be evaluated. */ - function requireWhitespaceBefore(node) { + function requireWhitespaceBefore(node: TSESTree.Node): boolean { const prevToken = sourceCode.getTokenBefore(node); + if (!prevToken) { + return false; + } if (node.range[0] - prevToken.range[1] > 0) { return false; } - return prevToken.type === 'Identifier'; + return prevToken.type === AST_TOKEN_TYPES.Identifier; } /** - * @param {ASTNode} node the node to be evaluated. - * @returns {string} Type used in message + * @param node the node to be evaluated. */ - function getMessageType(node) { + function getMessageType(node: TSESTree.Node): string { if (node) { - if (node.type === 'TSParenthesizedType') { + if (node.type === AST_NODE_TYPES.TSParenthesizedType) { return getMessageType(node.typeAnnotation); } if (isSimpleType(node)) { @@ -142,12 +146,8 @@ module.exports = { return 'T'; } - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- - return { - TSArrayType(node) { + TSArrayType(node: TSESTree.TSArrayType) { if ( option === 'array' || (option === 'array-simple' && isSimpleType(node.elementType)) @@ -172,23 +172,25 @@ module.exports = { fixer.insertTextBefore(node, `${startText ? ' ' : ''}Array<`) ]; - if (node.elementType.type === 'TSParenthesizedType') { - toFix.push( - fixer.remove(sourceCode.getFirstToken(node.elementType)) - ); - toFix.push( - fixer.remove(sourceCode.getLastToken(node.elementType)) - ); + if (node.elementType.type === AST_NODE_TYPES.TSParenthesizedType) { + const first = sourceCode.getFirstToken(node.elementType); + const last = sourceCode.getLastToken(node.elementType); + if (!first || !last) { + return null; + } + + toFix.push(fixer.remove(first)); + toFix.push(fixer.remove(last)); } return toFix; } }); }, - TSTypeReference(node) { + TSTypeReference(node: TSESTree.TSTypeReference) { if ( option === 'generic' || - node.typeName.type !== 'Identifier' || + node.typeName.type !== AST_NODE_TYPES.Identifier || node.typeName.name !== 'Array' ) { return; @@ -245,4 +247,4 @@ module.exports = { } }; } -}; +}); diff --git a/packages/eslint-plugin/lib/rules/ban-types.js b/packages/eslint-plugin/src/rules/ban-types.ts similarity index 51% rename from packages/eslint-plugin/lib/rules/ban-types.js rename to packages/eslint-plugin/src/rules/ban-types.ts index 42dd693463a..c981b9df9d4 100644 --- a/packages/eslint-plugin/lib/rules/ban-types.js +++ b/packages/eslint-plugin/src/rules/ban-types.ts @@ -2,49 +2,34 @@ * @fileoverview Enforces that types will not to be used * @author Armano */ -'use strict'; -const util = require('../util'); +import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/typescript-estree'; +import { ReportFixFunction } from 'ts-eslint'; +import * as util from '../util'; -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -const defaultOptions = [ +type Options = [ { - types: { - String: { - message: 'Use string instead', - fixWith: 'string' - }, - Boolean: { - message: 'Use boolean instead', - fixWith: 'boolean' - }, - Number: { - message: 'Use number instead', - fixWith: 'number' - }, - Object: { - message: 'Use Record instead', - fixWith: 'Record' - }, - Symbol: { - message: 'Use symbol instead', - fixWith: 'symbol' - } - } + types: Record< + string, + | string + | null + | { + message: string; + fixWith?: string; + } + >; } ]; +type MessageIds = 'bannedTypeMessage'; -module.exports = { +export default util.createRule({ + name: 'ban-types', meta: { type: 'suggestion', docs: { description: 'Enforces that types will not to be used', - extraDescription: [util.tslintRule('ban-types')], - category: 'TypeScript', - url: util.metaDocsUrl('ban-types'), + tslintRuleName: 'ban-types', + category: 'Best Practices', recommended: 'error' }, fixable: 'code', @@ -77,22 +62,44 @@ module.exports = { } ] }, - - create(context) { - const banedTypes = util.applyDefault(defaultOptions, context.options)[0] - .types; - - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- - + defaultOptions: [ + { + types: { + String: { + message: 'Use string instead', + fixWith: 'string' + }, + Boolean: { + message: 'Use boolean instead', + fixWith: 'boolean' + }, + Number: { + message: 'Use number instead', + fixWith: 'number' + }, + Object: { + message: 'Use Record instead', + fixWith: 'Record' + }, + Symbol: { + message: 'Use symbol instead', + fixWith: 'symbol' + } + } + } + ], + create(context, [{ types: bannedTypes }]) { return { - 'TSTypeReference Identifier'(node) { - if (node.parent && node.parent.type !== 'TSQualifiedName') { - if (node.name in banedTypes) { + 'TSTypeReference Identifier'(node: TSESTree.Identifier) { + if ( + node.parent && + node.parent.type !== AST_NODE_TYPES.TSQualifiedName + ) { + if (node.name in bannedTypes) { let customMessage = ''; - const bannedCfgValue = banedTypes[node.name]; - let fixWith = null; + const bannedCfgValue = bannedTypes[node.name]; + + let fix: ReportFixFunction | null = null; if (typeof bannedCfgValue === 'string') { customMessage += ` ${bannedCfgValue}`; @@ -101,7 +108,8 @@ module.exports = { customMessage += ` ${bannedCfgValue.message}`; } if (bannedCfgValue.fixWith) { - fixWith = bannedCfgValue.fixWith; + const fixWith = bannedCfgValue.fixWith; + fix = fixer => fixer.replaceText(node, fixWith); } } @@ -112,12 +120,11 @@ module.exports = { name: node.name, customMessage }, - fix: - fixWith !== null && (fixer => fixer.replaceText(node, fixWith)) + fix }); } } } }; } -}; +}); diff --git a/packages/eslint-plugin/lib/rules/camelcase.js b/packages/eslint-plugin/src/rules/camelcase.ts similarity index 52% rename from packages/eslint-plugin/lib/rules/camelcase.js rename to packages/eslint-plugin/src/rules/camelcase.ts index cffc47366cc..42285f2b0be 100644 --- a/packages/eslint-plugin/lib/rules/camelcase.js +++ b/packages/eslint-plugin/src/rules/camelcase.ts @@ -2,83 +2,85 @@ * @fileoverview Rule to flag non-camelcased identifiers * @author Patricio Trevino */ -'use strict'; -const baseRule = require('eslint/lib/rules/camelcase'); -const util = require('../util'); +import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/typescript-estree'; +import baseRule from 'eslint/lib/rules/camelcase'; +import * as util from '../util'; -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ -const defaultOptions = [ - { - allow: ['^UNSAFE_'], - ignoreDestructuring: false, - properties: 'never' - } -]; +type Options = util.InferOptionsTypeFromRule; +type MessageIds = util.InferMessageIdsTypeFromRule; -/* eslint-disable eslint-plugin/require-meta-type */ -module.exports = { - meta: Object.assign({}, baseRule.meta, { +export default util.createRule({ + name: 'ban-types', + meta: { + type: 'suggestion', docs: { description: 'Enforce camelCase naming convention', - url: util.metaDocsUrl('ban-types'), + category: 'Stylistic Issues', recommended: 'error' + }, + schema: baseRule.meta.schema!, + messages: baseRule.meta.messages + }, + defaultOptions: [ + { + allow: ['^UNSAFE_'], + ignoreDestructuring: false, + properties: 'never' } - }), - - create(context) { + ], + create(context, [options]) { const rules = baseRule.create(context); const TS_PROPERTY_TYPES = [ - 'TSPropertySignature', - 'ClassProperty', - 'TSParameterProperty', - 'TSAbstractClassProperty' + AST_NODE_TYPES.TSPropertySignature, + AST_NODE_TYPES.ClassProperty, + AST_NODE_TYPES.TSParameterProperty, + AST_NODE_TYPES.TSAbstractClassProperty ]; - const options = util.applyDefault(defaultOptions, context.options)[0]; const properties = options.properties; - const allow = options.allow; + const allow = options.allow!; /** * Checks if a string contains an underscore and isn't all upper-case - * @param {string} name The string to check. - * @returns {boolean} if the string is underscored - * @private + * @param name The string to check. */ - function isUnderscored(name) { + function isUnderscored(name: string): boolean { // if there's an underscore, it might be A_CONSTANT, which is okay return name.indexOf('_') > -1 && name !== name.toUpperCase(); } /** * Checks if a string match the ignore list - * @param {string} name The string to check. - * @returns {boolean} if the string is ignored + * @param name The string to check. + * @returns if the string is ignored * @private */ - function isAllowed(name) { + function isAllowed(name: string): boolean { return ( allow.findIndex( - entry => name === entry || name.match(new RegExp(entry)) + entry => name === entry || name.match(new RegExp(entry)) !== null ) !== -1 ); } /** * Checks if the the node is a valid TypeScript property type. - * @param {Node} node the node to be validated. - * @returns {boolean} true if the node is a TypeScript property type. + * @param node the node to be validated. + * @returns true if the node is a TypeScript property type. * @private */ - function isTSPropertyType(node) { - if (!node.parent) return false; - if (TS_PROPERTY_TYPES.includes(node.parent.type)) return true; + function isTSPropertyType(node: TSESTree.Node): boolean { + if (!node.parent) { + return false; + } + if (TS_PROPERTY_TYPES.includes(node.parent.type)) { + return true; + } - if (node.parent.type === 'AssignmentPattern') { + if (node.parent.type === AST_NODE_TYPES.AssignmentPattern) { return ( - node.parent.parent && + node.parent.parent !== undefined && TS_PROPERTY_TYPES.includes(node.parent.parent.type) ); } @@ -87,7 +89,7 @@ module.exports = { } return { - Identifier(node) { + Identifier(node: TSESTree.Identifier) { /* * Leading and trailing underscores are commonly used to flag * private/protected identifiers, strip them @@ -113,9 +115,8 @@ module.exports = { } // Let the base rule deal with the rest - // eslint-disable-next-line new-cap rules.Identifier(node); } }; } -}; +}); diff --git a/packages/eslint-plugin/src/rules/class-name-casing.ts b/packages/eslint-plugin/src/rules/class-name-casing.ts new file mode 100644 index 00000000000..87068155376 --- /dev/null +++ b/packages/eslint-plugin/src/rules/class-name-casing.ts @@ -0,0 +1,102 @@ +/** + * @fileoverview Enforces PascalCased class and interface names. + * @author Jed Fox + * @author Armano + */ + +import * as util from '../util'; +import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/typescript-estree'; + +export default util.createRule({ + name: 'class-name-casing', + meta: { + type: 'suggestion', + docs: { + description: 'Require PascalCased class and interface names', + tslintRuleName: 'class-name', + category: 'Best Practices', + recommended: 'error' + }, + messages: { + notPascalCased: "{{friendlyName}} '{{name}}' must be PascalCased." + }, + schema: [] + }, + defaultOptions: [], + create(context) { + /** + * Determine if the identifier name is PascalCased + * @param name The identifier name + */ + function isPascalCase(name: string): boolean { + return /^[A-Z][0-9A-Za-z]*$/.test(name); + } + + /** + * Report a class declaration as invalid + * @param decl The declaration + * @param id The name of the declaration + */ + function report(decl: TSESTree.Node, id: TSESTree.Identifier): void { + let friendlyName; + + switch (decl.type) { + case AST_NODE_TYPES.ClassDeclaration: + case AST_NODE_TYPES.ClassExpression: + friendlyName = decl.abstract ? 'Abstract class' : 'Class'; + break; + case AST_NODE_TYPES.TSInterfaceDeclaration: + friendlyName = 'Interface'; + break; + default: + friendlyName = decl.type; + } + + context.report({ + node: id, + messageId: 'notPascalCased', + data: { + friendlyName, + name: id.name + } + }); + } + + return { + 'ClassDeclaration, TSInterfaceDeclaration, ClassExpression'( + node: + | TSESTree.ClassDeclaration + | TSESTree.TSInterfaceDeclaration + | TSESTree.ClassExpression + ) { + // class expressions (i.e. export default class {}) are OK + if (node.id && !isPascalCase(node.id.name)) { + report(node, node.id); + } + }, + "VariableDeclarator[init.type='ClassExpression']"( + node: TSESTree.VariableDeclarator + ) { + if ( + node.id.type === AST_NODE_TYPES.ArrayPattern || + node.id.type === AST_NODE_TYPES.ObjectPattern + ) { + // TODO - handle the BindingPattern case maybe? + /* + // this example makes me barf, but it's valid code + var { bar } = class { + static bar() { return 2 } + } + */ + } else { + const id = node.id; + const nodeInit = node.init as TSESTree.ClassExpression; + + if (id && !nodeInit.id && !isPascalCase(id.name)) { + report(nodeInit, id); + } + } + } + }; + } +}); diff --git a/packages/eslint-plugin/src/rules/explicit-function-return-type.ts b/packages/eslint-plugin/src/rules/explicit-function-return-type.ts new file mode 100644 index 00000000000..0a7a0db7399 --- /dev/null +++ b/packages/eslint-plugin/src/rules/explicit-function-return-type.ts @@ -0,0 +1,120 @@ +/** + * @fileoverview Enforces explicit return type for functions + * @author Scott O'Hara + */ + +import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/typescript-estree'; +import * as util from '../util'; + +type Options = [ + { + allowExpressions?: boolean; + } +]; +type MessageIds = 'missingReturnType'; + +export default util.createRule({ + name: 'explicit-function-return-type', + meta: { + type: 'problem', + docs: { + description: + 'Require explicit return types on functions and class methods', + category: 'Stylistic Issues', + recommended: 'warn' + }, + messages: { + missingReturnType: 'Missing return type on function.' + }, + schema: [ + { + type: 'object', + properties: { + allowExpressions: { + type: 'boolean' + } + }, + additionalProperties: false + } + ] + }, + defaultOptions: [ + { + allowExpressions: true + } + ], + create(context, [options]) { + /** + * Checks if the parent of a function expression is a constructor. + * @param parent The parent of a function expression node + */ + function isConstructor(parent: TSESTree.Node): boolean { + return ( + parent.type === AST_NODE_TYPES.MethodDefinition && + parent.kind === 'constructor' + ); + } + + /** + * Checks if the parent of a function expression is a setter. + * @param parent The parent of a function expression node + */ + function isSetter(parent: TSESTree.Node): boolean { + return ( + parent.type === AST_NODE_TYPES.MethodDefinition && parent.kind === 'set' + ); + } + + /** + * Checks if a function declaration/expression has a return type. + * @param node The node representing a function. + */ + function checkFunctionReturnType( + node: + | TSESTree.ArrowFunctionExpression + | TSESTree.FunctionDeclaration + | TSESTree.FunctionExpression + ): void { + if ( + !node.returnType && + node.parent && + !isConstructor(node.parent) && + !isSetter(node.parent) && + util.isTypeScriptFile(context.getFilename()) + ) { + context.report({ + node, + messageId: 'missingReturnType' + }); + } + } + + /** + * Checks if a function declaration/expression has a return type. + * @param {ASTNode} node The node representing a function. + */ + function checkFunctionExpressionReturnType( + node: + | TSESTree.ArrowFunctionExpression + | TSESTree.FunctionDeclaration + | TSESTree.FunctionExpression + ): void { + if ( + options.allowExpressions && + node.parent && + node.parent.type !== AST_NODE_TYPES.VariableDeclarator && + node.parent.type !== AST_NODE_TYPES.MethodDefinition + ) { + return; + } + + checkFunctionReturnType(node); + } + + return { + ArrowFunctionExpression: checkFunctionExpressionReturnType, + FunctionDeclaration: checkFunctionReturnType, + FunctionExpression: checkFunctionExpressionReturnType + }; + } +}); diff --git a/packages/eslint-plugin/src/rules/explicit-member-accessibility.ts b/packages/eslint-plugin/src/rules/explicit-member-accessibility.ts new file mode 100644 index 00000000000..06a04b24e7a --- /dev/null +++ b/packages/eslint-plugin/src/rules/explicit-member-accessibility.ts @@ -0,0 +1,77 @@ +/** + * @fileoverview Enforces explicit accessibility modifier for class members + * @author Danny Fritz + */ + +import { TSESTree } from '@typescript-eslint/typescript-estree'; +import * as util from '../util'; + +export default util.createRule({ + name: 'explicit-member-accessibility', + meta: { + type: 'problem', + docs: { + description: + 'Require explicit accessibility modifiers on class properties and methods', + tslintRuleName: 'member-access', + category: 'Best Practices', + recommended: 'error' + }, + messages: { + missingAccessibility: + 'Missing accessibility modifier on {{type}} {{name}}.' + }, + schema: [] + }, + defaultOptions: [], + create(context) { + /** + * Checks if a method declaration has an accessibility modifier. + * @param methodDefinition The node representing a MethodDefinition. + */ + function checkMethodAccessibilityModifier( + methodDefinition: TSESTree.MethodDefinition + ): void { + if ( + !methodDefinition.accessibility && + util.isTypeScriptFile(context.getFilename()) + ) { + context.report({ + node: methodDefinition, + messageId: 'missingAccessibility', + data: { + type: 'method definition', + name: util.getNameFromPropertyName(methodDefinition.key) + } + }); + } + } + + /** + * Checks if property has an accessibility modifier. + * @param classProperty The node representing a ClassProperty. + */ + function checkPropertyAccessibilityModifier( + classProperty: TSESTree.ClassProperty + ): void { + if ( + !classProperty.accessibility && + util.isTypeScriptFile(context.getFilename()) + ) { + context.report({ + node: classProperty, + messageId: 'missingAccessibility', + data: { + type: 'class property', + name: util.getNameFromPropertyName(classProperty.key) + } + }); + } + } + + return { + ClassProperty: checkPropertyAccessibilityModifier, + MethodDefinition: checkMethodAccessibilityModifier + }; + } +}); diff --git a/packages/eslint-plugin/src/rules/generic-type-naming.ts b/packages/eslint-plugin/src/rules/generic-type-naming.ts new file mode 100644 index 00000000000..473fa6bc812 --- /dev/null +++ b/packages/eslint-plugin/src/rules/generic-type-naming.ts @@ -0,0 +1,53 @@ +/** + * @fileoverview Enforces naming of generic type variables. + */ + +import { TSESTree } from '@typescript-eslint/typescript-estree'; +import * as util from '../util'; + +type Options = [string?]; +type MessageIds = 'paramNotMatchRule'; + +export default util.createRule({ + name: 'generic-type-naming', + meta: { + type: 'suggestion', + docs: { + description: 'Enforces naming of generic type variables', + category: 'Stylistic Issues', + recommended: false + }, + messages: { + paramNotMatchRule: 'Type parameter {{name}} does not match rule {{rule}}.' + }, + schema: [ + { + type: 'string' + } + ] + }, + defaultOptions: [ + // Matches: T , TA , TAbc , TA1Bca , T1 , T2 + '^T([A-Z0-9][a-zA-Z0-9]*){0,1}$' + ], + create(context, [rule]) { + const regex = new RegExp(rule!); + + return { + TSTypeParameter(node: TSESTree.TSTypeParameter) { + const name = node.name.name; + + if (name && !regex.test(name)) { + context.report({ + node, + messageId: 'paramNotMatchRule', + data: { + name, + rule + } + }); + } + } + }; + } +}); diff --git a/packages/eslint-plugin/src/rules/indent.ts b/packages/eslint-plugin/src/rules/indent.ts new file mode 100644 index 00000000000..3ab9227c4b8 --- /dev/null +++ b/packages/eslint-plugin/src/rules/indent.ts @@ -0,0 +1,445 @@ +/** + * @fileoverview Rule to flag non-camelcased identifiers + * + * Note this file is rather type-unsafe in its current state. + * This is due to some really funky type conversions between different node types. + * This is done intentionally based on the internal implementation of the base indent rule. + */ + +import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/typescript-estree'; +import baseRule from 'eslint/lib/rules/indent'; +import * as util from '../util'; + +type Options = util.InferOptionsTypeFromRule; +type MessageIds = util.InferMessageIdsTypeFromRule; + +const KNOWN_NODES = new Set([ + // Class properties aren't yet supported by eslint... + AST_NODE_TYPES.ClassProperty, + + // ts keywords + AST_NODE_TYPES.TSAbstractKeyword, + AST_NODE_TYPES.TSAnyKeyword, + AST_NODE_TYPES.TSBooleanKeyword, + AST_NODE_TYPES.TSNeverKeyword, + AST_NODE_TYPES.TSNumberKeyword, + AST_NODE_TYPES.TSStringKeyword, + AST_NODE_TYPES.TSSymbolKeyword, + AST_NODE_TYPES.TSUndefinedKeyword, + AST_NODE_TYPES.TSUnknownKeyword, + AST_NODE_TYPES.TSVoidKeyword, + AST_NODE_TYPES.TSNullKeyword, + + // ts specific nodes we want to support + AST_NODE_TYPES.TSAbstractClassProperty, + AST_NODE_TYPES.TSAbstractMethodDefinition, + AST_NODE_TYPES.TSArrayType, + AST_NODE_TYPES.TSAsExpression, + AST_NODE_TYPES.TSCallSignatureDeclaration, + AST_NODE_TYPES.TSConditionalType, + AST_NODE_TYPES.TSConstructorType, + AST_NODE_TYPES.TSConstructSignatureDeclaration, + AST_NODE_TYPES.TSDeclareFunction, + AST_NODE_TYPES.TSEmptyBodyFunctionExpression, + AST_NODE_TYPES.TSEnumDeclaration, + AST_NODE_TYPES.TSEnumMember, + AST_NODE_TYPES.TSExportAssignment, + AST_NODE_TYPES.TSExternalModuleReference, + AST_NODE_TYPES.TSFunctionType, + AST_NODE_TYPES.TSImportType, + AST_NODE_TYPES.TSIndexedAccessType, + AST_NODE_TYPES.TSIndexSignature, + AST_NODE_TYPES.TSInferType, + AST_NODE_TYPES.TSInterfaceBody, + AST_NODE_TYPES.TSInterfaceDeclaration, + AST_NODE_TYPES.TSInterfaceHeritage, + AST_NODE_TYPES.TSIntersectionType, + AST_NODE_TYPES.TSImportEqualsDeclaration, + AST_NODE_TYPES.TSLiteralType, + AST_NODE_TYPES.TSMappedType, + AST_NODE_TYPES.TSMethodSignature, + 'TSMinusToken', + AST_NODE_TYPES.TSModuleBlock, + AST_NODE_TYPES.TSModuleDeclaration, + AST_NODE_TYPES.TSNonNullExpression, + AST_NODE_TYPES.TSParameterProperty, + AST_NODE_TYPES.TSParenthesizedType, + 'TSPlusToken', + AST_NODE_TYPES.TSPropertySignature, + AST_NODE_TYPES.TSQualifiedName, + AST_NODE_TYPES.TSQuestionToken, + AST_NODE_TYPES.TSRestType, + AST_NODE_TYPES.TSThisType, + AST_NODE_TYPES.TSTupleType, + AST_NODE_TYPES.TSTypeAnnotation, + AST_NODE_TYPES.TSTypeLiteral, + AST_NODE_TYPES.TSTypeOperator, + AST_NODE_TYPES.TSTypeParameter, + AST_NODE_TYPES.TSTypeParameterDeclaration, + AST_NODE_TYPES.TSTypeReference, + AST_NODE_TYPES.TSUnionType +]); + +export default util.createRule({ + name: 'indent', + meta: { + type: 'layout', + docs: { + description: 'Enforce consistent indentation', + tslintRuleName: 'indent', + category: 'Stylistic Issues', + recommended: 'error' + }, + fixable: 'whitespace', + schema: baseRule.meta!.schema, + messages: baseRule.meta!.messages + }, + defaultOptions: [ + // typescript docs and playground use 4 space indent + 4, + { + // typescript docs indent the case from the switch + // https://www.typescriptlang.org/docs/handbook/release-notes/typescript-1-8.html#example-4 + SwitchCase: 1, + flatTernaryExpressions: false, + ignoredNodes: [] + } + ], + create(context, optionsWithDefaults) { + // because we extend the base rule, have to update opts on the context + // the context defines options as readonly though... + const contextWithDefaults: typeof context = Object.create(context, { + options: { + writable: false, + configurable: false, + value: optionsWithDefaults + } + }); + + const rules = baseRule.create(contextWithDefaults); + + /** + * Converts from a TSPropertySignature to a Property + * @param node a TSPropertySignature node + * @param [type] the type to give the new node + * @returns a Property node + */ + function TSPropertySignatureToProperty( + node: + | TSESTree.TSPropertySignature + | TSESTree.TSEnumMember + | TSESTree.TypeElement, + type: + | AST_NODE_TYPES.ClassProperty + | AST_NODE_TYPES.Property = AST_NODE_TYPES.Property + ): TSESTree.Node | null { + const base = { + // indent doesn't actually use these + key: null as any, + value: null as any, + + // Property flags + computed: false, + method: false, + kind: 'init', + // this will stop eslint from interrogating the type literal + shorthand: true, + + // location data + parent: node.parent, + range: node.range, + loc: node.loc + }; + if (type === AST_NODE_TYPES.Property) { + return { + type, + ...base + } as TSESTree.Property; + } else { + return { + type, + static: false, + readonly: false, + ...base + } as TSESTree.ClassProperty; + } + } + + return Object.assign({}, rules, { + // overwrite the base rule here so we can use our KNOWN_NODES list instead + '*:exit'(node: TSESTree.Node) { + // For nodes we care about, skip the default handling, because it just marks the node as ignored... + if (!KNOWN_NODES.has(node.type)) { + rules['*:exit'](node); + } + }, + + TSAsExpression(node: TSESTree.TSAsExpression) { + // transform it to a BinaryExpression + return rules['BinaryExpression, LogicalExpression']({ + type: AST_NODE_TYPES.BinaryExpression, + operator: 'as', + left: node.expression, + // the first typeAnnotation includes the as token + right: node.typeAnnotation as any, + + // location data + parent: node.parent, + range: node.range, + loc: node.loc + }); + }, + + TSConditionalType(node: TSESTree.TSConditionalType) { + // transform it to a ConditionalExpression + return rules.ConditionalExpression({ + type: AST_NODE_TYPES.ConditionalExpression, + test: { + type: AST_NODE_TYPES.BinaryExpression, + operator: 'extends', + left: node.checkType as any, + right: node.extendsType as any, + + // location data + range: [node.checkType.range[0], node.extendsType.range[1]], + loc: { + start: node.checkType.loc.start, + end: node.extendsType.loc.end + } + }, + consequent: node.trueType as any, + alternate: node.falseType as any, + + // location data + parent: node.parent, + range: node.range, + loc: node.loc + }); + }, + + 'TSEnumDeclaration, TSTypeLiteral'( + node: TSESTree.TSEnumDeclaration | TSESTree.TSTypeLiteral + ) { + // transform it to an ObjectExpression + return rules['ObjectExpression, ObjectPattern']({ + type: AST_NODE_TYPES.ObjectExpression, + properties: (node.members as ( + | TSESTree.TSEnumMember + | TSESTree.TypeElement)[]).map( + member => TSPropertySignatureToProperty(member) as TSESTree.Property + ), + + // location data + parent: node.parent, + range: node.range, + loc: node.loc + }); + }, + + TSImportEqualsDeclaration(node: TSESTree.TSImportEqualsDeclaration) { + // transform it to an VariableDeclaration + // use VariableDeclaration instead of ImportDeclaration because it's essentially the same thing + const { id, moduleReference } = node; + + return rules.VariableDeclaration({ + type: AST_NODE_TYPES.VariableDeclaration, + kind: 'const' as 'const', + declarations: [ + { + type: AST_NODE_TYPES.VariableDeclarator, + range: [id.range[0], moduleReference.range[1]], + loc: { + start: id.loc.start, + end: moduleReference.loc.end + }, + id: id, + init: { + type: AST_NODE_TYPES.CallExpression, + callee: { + type: AST_NODE_TYPES.Identifier, + name: 'require', + range: [ + moduleReference.range[0], + moduleReference.range[0] + 'require'.length + ], + loc: { + start: moduleReference.loc.start, + end: { + line: moduleReference.loc.end.line, + column: moduleReference.loc.start.line + 'require'.length + } + } + }, + arguments: + 'expression' in moduleReference + ? [moduleReference.expression] + : [], + + // location data + range: moduleReference.range, + loc: moduleReference.loc + } + } + ], + + // location data + parent: node.parent, + range: node.range, + loc: node.loc + }); + }, + + TSIndexedAccessType(node: TSESTree.TSIndexedAccessType) { + // convert to a MemberExpression + return rules['MemberExpression, JSXMemberExpression, MetaProperty']({ + type: AST_NODE_TYPES.MemberExpression, + object: node.objectType as any, + property: node.indexType as any, + + // location data + parent: node.parent, + range: node.range, + loc: node.loc + }); + }, + + TSInterfaceBody(node: TSESTree.TSInterfaceBody) { + // transform it to an ClassBody + return rules['BlockStatement, ClassBody']({ + type: AST_NODE_TYPES.ClassBody, + body: node.body.map( + p => + TSPropertySignatureToProperty( + p, + AST_NODE_TYPES.ClassProperty + ) as TSESTree.ClassProperty + ), + + // location data + parent: node.parent, + range: node.range, + loc: node.loc + }); + }, + + 'TSInterfaceDeclaration[extends.length > 0]'( + node: TSESTree.TSInterfaceDeclaration + ) { + // transform it to a ClassDeclaration + return rules[ + 'ClassDeclaration[superClass], ClassExpression[superClass]' + ]({ + type: AST_NODE_TYPES.ClassDeclaration, + body: node.body as any, + id: undefined, + // TODO: This is invalid, there can be more than one extends in interface + superClass: node.extends![0].expression as any, + + // location data + parent: node.parent, + range: node.range, + loc: node.loc + }); + }, + + TSMappedType(node: TSESTree.TSMappedType) { + const sourceCode = context.getSourceCode(); + const squareBracketStart = sourceCode.getTokenBefore( + node.typeParameter + )!; + + // transform it to an ObjectExpression + return rules['ObjectExpression, ObjectPattern']({ + type: AST_NODE_TYPES.ObjectExpression, + properties: [ + { + type: AST_NODE_TYPES.Property, + key: node.typeParameter as any, + value: node.typeAnnotation as any, + + // location data + range: [ + squareBracketStart.range[0], + node.typeAnnotation + ? node.typeAnnotation.range[1] + : squareBracketStart.range[0] + ], + loc: { + start: squareBracketStart.loc.start, + end: node.typeAnnotation + ? node.typeAnnotation.loc.end + : squareBracketStart.loc.end + }, + kind: 'init' as 'init', + computed: false, + method: false, + shorthand: false + } + ], + + // location data + parent: node.parent, + range: node.range, + loc: node.loc + }); + }, + + TSModuleBlock(node: TSESTree.TSModuleBlock) { + // transform it to a BlockStatement + return rules['BlockStatement, ClassBody']({ + type: AST_NODE_TYPES.BlockStatement, + body: node.body, + + // location data + parent: node.parent, + range: node.range, + loc: node.loc + }); + }, + + TSQualifiedName(node: TSESTree.TSQualifiedName) { + return rules['MemberExpression, JSXMemberExpression, MetaProperty']({ + type: AST_NODE_TYPES.MemberExpression, + object: node.left as any, + property: node.right as any, + + // location data + parent: node.parent, + range: node.range, + loc: node.loc + }); + }, + + TSTupleType(node: TSESTree.TSTupleType) { + // transform it to an ArrayExpression + return rules['ArrayExpression, ArrayPattern']({ + type: AST_NODE_TYPES.ArrayExpression, + elements: node.elementTypes as any, + + // location data + parent: node.parent, + range: node.range, + loc: node.loc + }); + }, + + TSTypeParameterDeclaration(node: TSESTree.TSTypeParameterDeclaration) { + const [name, ...attributes] = node.params; + + // JSX is about the closest we can get because the angle brackets + // it's not perfect but it works! + return rules.JSXOpeningElement({ + type: AST_NODE_TYPES.JSXOpeningElement, + selfClosing: false, + name: name as any, + attributes: attributes as any, + + // location data + parent: node.parent, + range: node.range, + loc: node.loc + }); + } + }); + } +}); diff --git a/packages/eslint-plugin/src/rules/interface-name-prefix.ts b/packages/eslint-plugin/src/rules/interface-name-prefix.ts new file mode 100644 index 00000000000..0f895774ffb --- /dev/null +++ b/packages/eslint-plugin/src/rules/interface-name-prefix.ts @@ -0,0 +1,67 @@ +/** + * @fileoverview Enforces interface names are prefixed with "I". + * @author Danny Fritz + */ + +import { TSESTree } from '@typescript-eslint/typescript-estree'; +import * as util from '../util'; + +type Options = ['never' | 'always']; +type MessageIds = 'noPrefix'; + +export default util.createRule({ + name: 'interface-name-prefix', + meta: { + type: 'suggestion', + docs: { + description: 'Require that interface names be prefixed with `I`', + tslintRuleName: 'interface-name', + category: 'Stylistic Issues', + recommended: 'error' + }, + messages: { + noPrefix: 'Interface name must not be prefixed with "I".' + }, + schema: [ + { + enum: ['never', 'always'] + } + ] + }, + defaultOptions: ['never'], + create(context, [option]) { + const never = option !== 'always'; + + /** + * Checks if a string is prefixed with "I". + * @param name The string to check + */ + function isPrefixedWithI(name: string): boolean { + if (typeof name !== 'string') { + return false; + } + + return /^I[A-Z]/.test(name); + } + + return { + TSInterfaceDeclaration(node: TSESTree.TSInterfaceDeclaration): void { + if (never) { + if (isPrefixedWithI(node.id.name)) { + context.report({ + node: node.id, + messageId: 'noPrefix' + }); + } + } else { + if (!isPrefixedWithI(node.id.name)) { + context.report({ + node: node.id, + messageId: 'noPrefix' + }); + } + } + } + }; + } +}); diff --git a/packages/eslint-plugin/lib/rules/member-delimiter-style.js b/packages/eslint-plugin/src/rules/member-delimiter-style.ts similarity index 68% rename from packages/eslint-plugin/lib/rules/member-delimiter-style.js rename to packages/eslint-plugin/src/rules/member-delimiter-style.ts index 2fc49166ba3..81cbbbfe1c8 100644 --- a/packages/eslint-plugin/lib/rules/member-delimiter-style.js +++ b/packages/eslint-plugin/src/rules/member-delimiter-style.ts @@ -3,26 +3,31 @@ * @author Patricio Trevino * @author Brad Zacher */ -'use strict'; -const { deepMerge, metaDocsUrl, applyDefault } = require('../util'); - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -const defaultOptions = [ - { - multiline: { - delimiter: 'semi', - requireLast: true - }, - singleline: { - delimiter: 'semi', - requireLast: false - } - } -]; +import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/typescript-estree'; +import * as util from '../util'; + +type Delimiter = 'comma' | 'none' | 'semi'; +interface TypeOptions { + delimiter?: Delimiter; + requireLast?: boolean; +} +interface BaseOptions { + multiline?: TypeOptions; + singleline?: TypeOptions; +} +interface Config extends BaseOptions { + overrides?: { + typeLiteral?: BaseOptions; + interface?: BaseOptions; + }; +} +type Options = [Config]; +type MessageIds = + | 'unexpectedComma' + | 'unexpectedSemi' + | 'expectedComma' + | 'expectedSemi'; const definition = { type: 'object', @@ -48,14 +53,14 @@ const definition = { additionalProperties: false }; -module.exports = { +export default util.createRule({ + name: 'member-delimiter-style', meta: { type: 'suggestion', docs: { description: 'Require a specific member delimiter style for interfaces and type literals', - category: 'TypeScript', - url: metaDocsUrl('member-delimiter-style'), + category: 'Stylistic Issues', recommended: 'error' }, fixable: 'code', @@ -82,37 +87,49 @@ module.exports = { } ] }, - - create(context) { + defaultOptions: [ + { + multiline: { + delimiter: 'semi', + requireLast: true + }, + singleline: { + delimiter: 'semi', + requireLast: false + } + } + ], + create(context, [options]) { const sourceCode = context.getSourceCode(); - const options = applyDefault(defaultOptions, context.options)[0]; // use the base options as the defaults for the cases const baseOptions = options; const overrides = baseOptions.overrides || {}; - const interfaceOptions = deepMerge(baseOptions, overrides.interface); - const typeLiteralOptions = deepMerge(baseOptions, overrides.typeLiteral); - - //---------------------------------------------------------------------- - // Helpers - //---------------------------------------------------------------------- + const interfaceOptions: BaseOptions = util.deepMerge( + baseOptions, + overrides.interface + ); + const typeLiteralOptions: BaseOptions = util.deepMerge( + baseOptions, + overrides.typeLiteral + ); /** * Check the last token in the given member. - * @param {ASTNode} member the member to be evaluated. - * @param {Object} opts the options to be validated. - * @param {boolean} isLast a flag indicating `member` is the last in the - * interface or type literal. - * @returns {void} - * @private + * @param member the member to be evaluated. + * @param opts the options to be validated. + * @param isLast a flag indicating `member` is the last in the interface or type literal. */ - function checkLastToken(member, opts, isLast) { + function checkLastToken( + member: TSESTree.TypeElement, + opts: TypeOptions, + isLast: boolean + ): void { /** * Resolves the boolean value for the given setting enum value - * @param {"semi" | "comma" | "none"} type the option name - * @returns {boolean} the resolved value + * @param type the option name */ - function getOption(type) { + function getOption(type: Delimiter): boolean { if (isLast && !opts.requireLast) { // only turn the option on if its expecting no delimiter for the last member return type === 'none'; @@ -120,11 +137,14 @@ module.exports = { return opts.delimiter === type; } - let messageId; + let messageId: MessageIds | null = null; let missingDelimiter = false; const lastToken = sourceCode.getLastToken(member, { includeComments: false }); + if (!lastToken) { + return; + } const optsSemi = getOption('semi'); const optsComma = getOption('comma'); @@ -191,30 +211,29 @@ module.exports = { /** * Check the member separator being used matches the delimiter. * @param {ASTNode} node the node to be evaluated. - * @returns {void} - * @private */ - function checkMemberSeparatorStyle(node) { - const isInterface = node.type === 'TSInterfaceBody'; + function checkMemberSeparatorStyle( + node: TSESTree.TSInterfaceBody | TSESTree.TSTypeLiteral + ): void { const isSingleLine = node.loc.start.line === node.loc.end.line; - const members = isInterface ? node.body : node.members; + const members = + node.type === AST_NODE_TYPES.TSInterfaceBody ? node.body : node.members; - const typeOpts = isInterface ? interfaceOptions : typeLiteralOptions; + const typeOpts = + node.type === AST_NODE_TYPES.TSInterfaceBody + ? interfaceOptions + : typeLiteralOptions; const opts = isSingleLine ? typeOpts.singleline : typeOpts.multiline; members.forEach((member, index) => { - checkLastToken(member, opts, index === members.length - 1); + checkLastToken(member, opts || {}, index === members.length - 1); }); } - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- - return { TSInterfaceBody: checkMemberSeparatorStyle, TSTypeLiteral: checkMemberSeparatorStyle }; } -}; +}); diff --git a/packages/eslint-plugin/lib/rules/member-naming.js b/packages/eslint-plugin/src/rules/member-naming.ts similarity index 53% rename from packages/eslint-plugin/lib/rules/member-naming.js rename to packages/eslint-plugin/src/rules/member-naming.ts index 49dc33570c8..58524763e5d 100644 --- a/packages/eslint-plugin/lib/rules/member-naming.js +++ b/packages/eslint-plugin/src/rules/member-naming.ts @@ -2,26 +2,33 @@ * @fileoverview Enforces naming conventions for class members by visibility. * @author Ian MacLeod */ -'use strict'; -const util = require('../util'); +import { TSESTree } from '@typescript-eslint/typescript-estree'; +import * as util from '../util'; -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ +interface Config { + private?: T; + protected?: T; + public?: T; +} +type Modifiers = keyof Config; +type Options = [Config]; +type MessageIds = 'incorrectName'; -const defaultOptions = [{}]; - -module.exports = { +export default util.createRule({ + name: 'member-naming', meta: { type: 'suggestion', docs: { description: 'Enforces naming conventions for class members by visibility.', - category: 'TypeScript', - url: util.metaDocsUrl('member-naming'), + category: 'Stylistic Issues', recommended: false }, + messages: { + incorrectName: + '{{accessibility}} property {{name}} should match {{convention}}.' + }, schema: [ { type: 'object', @@ -47,19 +54,16 @@ module.exports = { } ] }, - - create(context) { - const config = util.applyDefault(defaultOptions, context.options)[0]; - const conventions = Object.keys(config).reduce((acc, accessibility) => { - acc[accessibility] = new RegExp(config[accessibility]); + defaultOptions: [{}], + create(context, [config]) { + const conventions = (Object.keys(config) as Modifiers[]).reduce< + Config + >((acc, accessibility) => { + acc[accessibility] = new RegExp(config[accessibility]!); return acc; }, {}); - //---------------------------------------------------------------------- - // Helpers - //---------------------------------------------------------------------- - /** * Check that the property name matches the convention for its * accessibility. @@ -67,28 +71,25 @@ module.exports = { * @returns {void} * @private */ - function validateName(node) { - const name = node.key.name; - const accessibility = node.accessibility || 'public'; + function validateName( + node: TSESTree.MethodDefinition | TSESTree.ClassProperty + ): void { + const name = util.getNameFromPropertyName(node.key); + const accessibility: Modifiers = node.accessibility || 'public'; const convention = conventions[accessibility]; if (!convention || convention.test(name)) return; context.report({ node: node.key, - message: - '{{accessibility}} property {{name}} should match {{convention}}.', + messageId: 'incorrectName', data: { accessibility, name, convention } }); } - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- - return { MethodDefinition: validateName, ClassProperty: validateName }; } -}; +}); diff --git a/packages/eslint-plugin/lib/rules/member-ordering.js b/packages/eslint-plugin/src/rules/member-ordering.ts similarity index 53% rename from packages/eslint-plugin/lib/rules/member-ordering.js rename to packages/eslint-plugin/src/rules/member-ordering.ts index e8b72ac0bcc..b16c878bc71 100644 --- a/packages/eslint-plugin/lib/rules/member-ordering.js +++ b/packages/eslint-plugin/src/rules/member-ordering.ts @@ -2,15 +2,23 @@ * @fileoverview Enforces a standard member declaration order. * @author Patricio Trevino */ -'use strict'; -const util = require('../util'); +import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/typescript-estree'; +import * as util from '../util'; -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ +type MessageIds = 'incorrectOrder'; +type OrderConfig = string[] | 'never'; +type Options = [ + { + default?: OrderConfig; + classes?: OrderConfig; + classExpressions?: OrderConfig; + interfaces?: OrderConfig; + typeLiterals?: OrderConfig; + } +]; -const schemaOptions = ['field', 'method', 'constructor'].reduce( +const schemaOptions = ['field', 'method', 'constructor'].reduce( (options, type) => { options.push(type); @@ -31,58 +39,20 @@ const schemaOptions = ['field', 'method', 'constructor'].reduce( [] ); -const defaultOptions = [ - { - default: [ - 'public-static-field', - 'protected-static-field', - 'private-static-field', - - 'public-instance-field', - 'protected-instance-field', - 'private-instance-field', - - 'public-field', - 'protected-field', - 'private-field', - - 'static-field', - 'instance-field', - - 'field', - - 'constructor', - - 'public-static-method', - 'protected-static-method', - 'private-static-method', - - 'public-instance-method', - 'protected-instance-method', - 'private-instance-method', - - 'public-method', - 'protected-method', - 'private-method', - - 'static-method', - 'instance-method', - - 'method' - ] - } -]; - -module.exports = { +export default util.createRule({ + name: 'member-ordering', meta: { type: 'suggestion', docs: { description: 'Require a consistent member declaration order', - extraDescription: [util.tslintRule('member-ordering')], - category: 'TypeScript', - url: util.metaDocsUrl('member-ordering'), + tslintRuleName: 'member-ordering', + category: 'Stylistic Issues', recommended: false }, + messages: { + incorrectOrder: + 'Member {{name}} should be declared before all {{rank}} definitions.' + }, schema: [ { type: 'object', @@ -157,48 +127,74 @@ module.exports = { } ] }, + defaultOptions: [ + { + default: [ + 'public-static-field', + 'protected-static-field', + 'private-static-field', - create(context) { - const options = util.applyDefault(defaultOptions, context.options)[0]; + 'public-instance-field', + 'protected-instance-field', + 'private-instance-field', - const functionExpressions = [ - 'FunctionExpression', - 'ArrowFunctionExpression' - ]; + 'public-field', + 'protected-field', + 'private-field', - //---------------------------------------------------------------------- - // Helpers - //---------------------------------------------------------------------- + 'static-field', + 'instance-field', - /** - * Determines if `node` should be processed as a method instead of a field. - * @param {ASTNode} node the node to be inspected. - * @returns {boolean} `true` if node should be processed as a method; `false` for fields. - * @private - */ - function shouldBeProcessedAsMethod(node) { - // check for bound methods in ClassProperty nodes. - return node.value && functionExpressions.indexOf(node.value.type) > -1; + 'field', + + 'constructor', + + 'public-static-method', + 'protected-static-method', + 'private-static-method', + + 'public-instance-method', + 'protected-instance-method', + 'private-instance-method', + + 'public-method', + 'protected-method', + 'private-method', + + 'static-method', + 'instance-method', + + 'method' + ] } + ], + create(context, [options]) { + const functionExpressions = [ + AST_NODE_TYPES.FunctionExpression, + AST_NODE_TYPES.ArrowFunctionExpression + ]; /** * Gets the node type. - * @param {ASTNode} node the node to be evaluated. - * @returns {string|null} the type of the node. - * @private + * @param node the node to be evaluated. */ - function getNodeType(node) { + function getNodeType( + node: TSESTree.ClassElement | TSESTree.TypeElement + ): string | null { // TODO: add missing TSCallSignatureDeclaration switch (node.type) { - case 'MethodDefinition': + case AST_NODE_TYPES.MethodDefinition: return node.kind; - case 'TSMethodSignature': + case AST_NODE_TYPES.TSMethodSignature: return 'method'; - case 'TSConstructSignatureDeclaration': + case AST_NODE_TYPES.TSConstructSignatureDeclaration: return 'constructor'; - case 'ClassProperty': - case 'TSPropertySignature': - return shouldBeProcessedAsMethod(node) ? 'method' : 'field'; + case AST_NODE_TYPES.ClassProperty: + return node.value && functionExpressions.indexOf(node.value.type) > -1 + ? 'method' + : 'field'; + case AST_NODE_TYPES.TSPropertySignature: + return 'field'; default: return null; } @@ -206,19 +202,21 @@ module.exports = { /** * Gets the member name based on the member type. - * @param {ASTNode} node the node to be evaluated. - * @returns {string|null} the name of the member. - * @private + * @param node the node to be evaluated. */ - function getMemberName(node) { + function getMemberName( + node: TSESTree.ClassElement | TSESTree.TypeElement + ): string | null { switch (node.type) { - case 'ClassProperty': - case 'MethodDefinition': - return node.kind === 'constructor' ? 'constructor' : node.key.name; - case 'TSPropertySignature': - case 'TSMethodSignature': - return node.key.name; - case 'TSConstructSignatureDeclaration': + case AST_NODE_TYPES.TSPropertySignature: + case AST_NODE_TYPES.TSMethodSignature: + case AST_NODE_TYPES.ClassProperty: + return util.getNameFromPropertyName(node.key); + case AST_NODE_TYPES.MethodDefinition: + return node.kind === 'constructor' + ? 'constructor' + : util.getNameFromPropertyName(node.key); + case AST_NODE_TYPES.TSConstructSignatureDeclaration: return 'new'; default: return null; @@ -232,17 +230,15 @@ module.exports = { * - If there is no order for accessibility-scope-type, then strip out the accessibility. * - If there is no order for scope-type, then strip out the scope. * - If there is no order for type, then return -1 - * @param {Array} names the valid names to be validated. - * @param {Array} order the current order to be validated. - * @returns {number} the rank of the method definition in the given order. - * @private + * @param names the valid names to be validated. + * @param order the current order to be validated. */ - function getRankOrder(names, order) { + function getRankOrder(names: string[], order: string[]): number { let rank = -1; const stack = names.slice(); while (stack.length > 0 && rank === -1) { - rank = order.indexOf(stack.shift()); + rank = order.indexOf(stack.shift()!); } return rank; @@ -250,16 +246,26 @@ module.exports = { /** * Gets the rank of the node given the order. - * @param {ASTNode} node the node to be evaluated. - * @param {Array} order the current order to be validated. - * @param {boolean} supportsModifiers a flag indicating whether the type supports modifiers or not. - * @returns {number} the rank of the node. - * @private + * @param node the node to be evaluated. + * @param order the current order to be validated. + * @param supportsModifiers a flag indicating whether the type supports modifiers or not. */ - function getRank(node, order, supportsModifiers) { + function getRank( + node: TSESTree.ClassElement | TSESTree.TypeElement, + order: string[], + supportsModifiers: boolean + ): number { const type = getNodeType(node); - const scope = node.static ? 'static' : 'instance'; - const accessibility = node.accessibility || 'public'; + if (type === null) { + // shouldn't happen but just in case, put it on the end + return Number.MAX_SAFE_INTEGER; + } + + const scope = 'static' in node && node.static ? 'static' : 'instance'; + const accessibility = + 'accessibility' in node && node.accessibility + ? node.accessibility + : 'public'; const names = []; @@ -290,13 +296,16 @@ module.exports = { * and considering that a public-instance-method has already been declared, so ranks contains * public-instance-method, then the lowest possible rank for public-static-method is * public-instance-method. - * @param {Array} ranks the existing ranks in the object. - * @param {number} target the target rank. - * @param {Array} order the current order to be validated. - * @returns {string} the name of the lowest possible rank without dashes (-). - * @private + * @param ranks the existing ranks in the object. + * @param target the target rank. + * @param order the current order to be validated. + * @returns the name of the lowest possible rank without dashes (-). */ - function getLowestRank(ranks, target, order) { + function getLowestRank( + ranks: number[], + target: number, + order: string[] + ): string { let lowest = ranks[ranks.length - 1]; ranks.forEach(rank => { @@ -310,15 +319,17 @@ module.exports = { /** * Validates each member rank. - * @param {Array} members the members to be validated. - * @param {(Array|string)} order the current order to be validated. - * @param {boolean} supportsModifiers a flag indicating whether the type supports modifiers or not. - * @returns {void} - * @private + * @param members the members to be validated. + * @param order the current order to be validated. + * @param supportsModifiers a flag indicating whether the type supports modifiers or not. */ - function validateMembers(members, order, supportsModifiers) { + function validateMembers( + members: (TSESTree.ClassElement | TSESTree.TypeElement)[], + order: OrderConfig, + supportsModifiers: boolean + ): void { if (members && order !== 'never') { - const previousRanks = []; + const previousRanks: number[] = []; members.forEach(member => { const rank = getRank(member, order, supportsModifiers); @@ -327,8 +338,7 @@ module.exports = { if (rank < previousRanks[previousRanks.length - 1]) { context.report({ node: member, - message: - 'Member {{name}} should be declared before all {{rank}} definitions.', + messageId: 'incorrectOrder', data: { name: getMemberName(member), rank: getLowestRank(previousRanks, rank, order) @@ -342,38 +352,35 @@ module.exports = { } } - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- return { - ClassDeclaration(node) { + ClassDeclaration(node: TSESTree.ClassDeclaration) { validateMembers( node.body.body, - options.classes || options.default, + options.classes || options.default!, true ); }, - ClassExpression(node) { + ClassExpression(node: TSESTree.ClassExpression) { validateMembers( node.body.body, - options.classExpressions || options.default, + options.classExpressions || options.default!, true ); }, - TSInterfaceDeclaration(node) { + TSInterfaceDeclaration(node: TSESTree.TSInterfaceDeclaration) { validateMembers( node.body.body, - options.interfaces || options.default, + options.interfaces || options.default!, false ); }, - TSTypeLiteral(node) { + TSTypeLiteral(node: TSESTree.TSTypeLiteral) { validateMembers( node.members, - options.typeLiterals || options.default, + options.typeLiterals || options.default!, false ); } }; } -}; +}); diff --git a/packages/eslint-plugin/src/rules/no-angle-bracket-type-assertion.ts b/packages/eslint-plugin/src/rules/no-angle-bracket-type-assertion.ts new file mode 100644 index 00000000000..65ae3cd0d83 --- /dev/null +++ b/packages/eslint-plugin/src/rules/no-angle-bracket-type-assertion.ts @@ -0,0 +1,41 @@ +/** + * @fileoverview Enforces the use of `as Type` assertions instead of `` assertions. + * @author Patricio Trevino + */ + +import { TSESTree } from '@typescript-eslint/typescript-estree'; +import * as util from '../util'; + +export default util.createRule({ + name: 'no-angle-bracket-type-assertion', + meta: { + type: 'problem', + docs: { + description: + 'Enforces the use of `as Type` assertions instead of `` assertions', + tslintRuleName: 'no-angle-bracket-type-assertion', + category: 'Stylistic Issues', + recommended: 'error' + }, + messages: { + preferAs: + "Prefer 'as {{cast}}' instead of '<{{cast}}>' when doing type assertions." + }, + schema: [] + }, + defaultOptions: [], + create(context) { + const sourceCode = context.getSourceCode(); + return { + TSTypeAssertion(node: TSESTree.TSTypeAssertion) { + context.report({ + node, + messageId: 'preferAs', + data: { + cast: sourceCode.getText(node.typeAnnotation) + } + }); + } + }; + } +}); diff --git a/packages/eslint-plugin/lib/rules/no-array-constructor.js b/packages/eslint-plugin/src/rules/no-array-constructor.ts similarity index 68% rename from packages/eslint-plugin/lib/rules/no-array-constructor.js rename to packages/eslint-plugin/src/rules/no-array-constructor.ts index bf4139396c2..c36c1ba21f3 100644 --- a/packages/eslint-plugin/lib/rules/no-array-constructor.js +++ b/packages/eslint-plugin/src/rules/no-array-constructor.ts @@ -3,44 +3,43 @@ * @author Jed Fox * @author Matt DuVall */ -'use strict'; -const util = require('../util'); +import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/typescript-estree'; +import * as util from '../util'; -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -module.exports = { +export default util.createRule({ + name: 'no-array-constructor', meta: { type: 'suggestion', docs: { description: 'Disallow generic `Array` constructors', category: 'Stylistic Issues', - url: util.metaDocsUrl('no-array-constructor'), recommended: 'error' }, fixable: 'code', + messages: { + useLiteral: 'The array literal notation [] is preferrable.' + }, schema: [] }, - + defaultOptions: [], create(context) { /** * Disallow construction of dense arrays using the Array constructor - * @param {ASTNode} node node to evaluate - * @returns {void} - * @private + * @param node node to evaluate */ - function check(node) { + function check( + node: TSESTree.CallExpression | TSESTree.NewExpression + ): void { if ( node.arguments.length !== 1 && - node.callee.type === 'Identifier' && + node.callee.type === AST_NODE_TYPES.Identifier && node.callee.name === 'Array' && !node.typeParameters ) { context.report({ node, - message: 'The array literal notation [] is preferrable.', + messageId: 'useLiteral', fix(fixer) { if (node.arguments.length === 0) { return fixer.replaceText(node, '[]'); @@ -62,4 +61,4 @@ module.exports = { NewExpression: check }; } -}; +}); diff --git a/packages/eslint-plugin/src/rules/no-empty-interface.ts b/packages/eslint-plugin/src/rules/no-empty-interface.ts new file mode 100644 index 00000000000..52e1d0ddf9a --- /dev/null +++ b/packages/eslint-plugin/src/rules/no-empty-interface.ts @@ -0,0 +1,48 @@ +/** + * @fileoverview Disallows the declaration of empty interfaces. + * @author Patricio Trevino + */ + +import { TSESTree } from '@typescript-eslint/typescript-estree'; +import * as util from '../util'; + +export default util.createRule({ + name: 'no-empty-interface', + meta: { + type: 'suggestion', + docs: { + description: 'Disallow the declaration of empty interfaces', + tslintRuleName: 'no-empty-interface', + category: 'Best Practices', + recommended: 'error' + }, + messages: { + noEmpty: 'An empty interface is equivalent to `{}`.', + noEmptyWithSuper: + 'An interface declaring no members is equivalent to its supertype.' + }, + schema: [] + }, + defaultOptions: [], + create(context) { + return { + TSInterfaceDeclaration(node: TSESTree.TSInterfaceDeclaration) { + if (node.body.body.length !== 0) { + return; + } + + if (!node.extends || node.extends.length === 0) { + context.report({ + node: node.id, + messageId: 'noEmpty' + }); + } else if (node.extends.length === 1) { + context.report({ + node: node.id, + messageId: 'noEmptyWithSuper' + }); + } + } + }; + } +}); diff --git a/packages/eslint-plugin/src/rules/no-explicit-any.ts b/packages/eslint-plugin/src/rules/no-explicit-any.ts new file mode 100644 index 00000000000..48a296bd25f --- /dev/null +++ b/packages/eslint-plugin/src/rules/no-explicit-any.ts @@ -0,0 +1,35 @@ +/** + * @fileoverview Enforces the any type is not used. + * @author Danny Fritz + * @author Patricio Trevino + */ + +import * as util from '../util'; + +export default util.createRule({ + name: 'no-explicit-any', + meta: { + type: 'suggestion', + docs: { + description: 'Disallow usage of the `any` type', + tslintRuleName: 'no-any', + category: 'Best Practices', + recommended: 'warn' + }, + messages: { + unexpectedAny: 'Unexpected any. Specify a different type.' + }, + schema: [] + }, + defaultOptions: [], + create(context) { + return { + TSAnyKeyword(node) { + context.report({ + node, + messageId: 'unexpectedAny' + }); + } + }; + } +}); diff --git a/packages/eslint-plugin/lib/rules/no-extraneous-class.js b/packages/eslint-plugin/src/rules/no-extraneous-class.ts similarity index 57% rename from packages/eslint-plugin/lib/rules/no-extraneous-class.js rename to packages/eslint-plugin/src/rules/no-extraneous-class.ts index 0690fc3963d..cb6c58de86c 100644 --- a/packages/eslint-plugin/lib/rules/no-extraneous-class.js +++ b/packages/eslint-plugin/src/rules/no-extraneous-class.ts @@ -2,33 +2,29 @@ * @fileoverview Forbids the use of classes as namespaces * @author Jed Fox */ -'use strict'; -const util = require('../util'); +import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/typescript-estree'; +import * as util from '../util'; -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -const defaultOptions = [ +type Options = [ { - allowConstructorOnly: false, - allowEmpty: false, - allowStaticOnly: false + allowConstructorOnly?: boolean; + allowEmpty?: boolean; + allowStaticOnly?: boolean; } ]; +type MessageIds = 'empty' | 'onlyStatic' | 'onlyConstructor'; -module.exports = { +export default util.createRule({ + name: 'no-extraneous-class', meta: { type: 'suggestion', docs: { description: 'Forbids the use of classes as namespaces', - extraDescription: [util.tslintRule('no-unnecessary-class')], + tslintRuleName: 'no-unnecessary-class', category: 'Best Practices', - url: util.metaDocsUrl('no-extraneous-class'), recommended: false }, - fixable: null, schema: [ { type: 'object', @@ -52,26 +48,37 @@ module.exports = { onlyConstructor: 'Unexpected class with only a constructor.' } }, - - create(context) { - const { - allowConstructorOnly, - allowEmpty, - allowStaticOnly - } = util.applyDefault(defaultOptions, context.options)[0]; - + defaultOptions: [ + { + allowConstructorOnly: false, + allowEmpty: false, + allowStaticOnly: false + } + ], + create(context, [{ allowConstructorOnly, allowEmpty, allowStaticOnly }]) { return { - ClassBody(node) { - const { id, superClass } = node.parent; + ClassBody(node: TSESTree.ClassBody) { + const parent = node.parent as + | TSESTree.ClassDeclaration + | TSESTree.ClassExpression + | undefined; + + if (!parent || parent.superClass) { + return; + } - if (superClass) return; + const reportNode = 'id' in parent && parent.id ? parent.id : parent; if (node.body.length === 0) { - if (allowEmpty) return; + if (allowEmpty) { + return; + } + context.report({ - node: id || node.parent, + node: reportNode, messageId: 'empty' }); + return; } @@ -79,10 +86,10 @@ module.exports = { let onlyConstructor = true; for (const prop of node.body) { - if (prop.kind === 'constructor') { + if ('kind' in prop && prop.kind === 'constructor') { if ( prop.value.params.some( - param => param.type === 'TSParameterProperty' + param => param.type === AST_NODE_TYPES.TSParameterProperty ) ) { onlyConstructor = false; @@ -90,7 +97,7 @@ module.exports = { } } else { onlyConstructor = false; - if (!prop.static) { + if ('static' in prop && !prop.static) { onlyStatic = false; } } @@ -100,7 +107,7 @@ module.exports = { if (onlyConstructor) { if (!allowConstructorOnly) { context.report({ - node: id || node.parent, + node: reportNode, messageId: 'onlyConstructor' }); } @@ -108,11 +115,11 @@ module.exports = { } if (onlyStatic && !allowStaticOnly) { context.report({ - node: id || node.parent, + node: reportNode, messageId: 'onlyStatic' }); } } }; } -}; +}); diff --git a/packages/eslint-plugin/lib/rules/no-for-in-array.js b/packages/eslint-plugin/src/rules/no-for-in-array.ts similarity index 65% rename from packages/eslint-plugin/lib/rules/no-for-in-array.js rename to packages/eslint-plugin/src/rules/no-for-in-array.ts index 23a19732ac4..c958d39670f 100644 --- a/packages/eslint-plugin/lib/rules/no-for-in-array.js +++ b/packages/eslint-plugin/src/rules/no-for-in-array.ts @@ -2,27 +2,20 @@ * @fileoverview Disallow iterating over an array with a for-in loop * @author Benjamin Lichtman */ -'use strict'; -const ts = require('typescript'); -const util = require('../util'); -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ +import { TSESTree } from '@typescript-eslint/typescript-estree'; +import ts from 'typescript'; +import * as util from '../util'; -/** - * @type {import("eslint").Rule.RuleModule} - */ -module.exports = { +export default util.createRule({ + name: 'no-for-in-array', meta: { docs: { description: 'Disallow iterating over an array with a for-in loop', - category: 'Functionality', + category: 'Best Practices', recommended: false, - extraDescription: [util.tslintRule('no-for-in-array')], - url: util.metaDocsUrl('no-for-in-array') + tslintName: 'no-for-in-array' }, - fixable: null, messages: { forInViolation: 'For-in loops over arrays are forbidden. Use for-of or array.forEach instead.' @@ -30,13 +23,15 @@ module.exports = { schema: [], type: 'problem' }, - + defaultOptions: [], create(context) { return { - ForInStatement(node) { + ForInStatement(node: TSESTree.ForInStatement) { const parserServices = util.getParserServices(context); const checker = parserServices.program.getTypeChecker(); - const originalNode = parserServices.esTreeNodeToTSNodeMap.get(node); + const originalNode = parserServices.esTreeNodeToTSNodeMap.get< + ts.ForInStatement + >(node); const type = checker.getTypeAtLocation(originalNode.expression); @@ -53,4 +48,4 @@ module.exports = { } }; } -}; +}); diff --git a/packages/eslint-plugin/src/rules/no-inferrable-types.ts b/packages/eslint-plugin/src/rules/no-inferrable-types.ts new file mode 100644 index 00000000000..4f695c996c1 --- /dev/null +++ b/packages/eslint-plugin/src/rules/no-inferrable-types.ts @@ -0,0 +1,199 @@ +/** + * @fileoverview Disallows explicit type declarations for inferrable types + * @author James Garbutt + */ + +import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/typescript-estree'; +import * as util from '../util'; + +type Options = [ + { + ignoreParameters?: boolean; + ignoreProperties?: boolean; + } +]; +type MessageIds = 'noInferrableType'; + +export default util.createRule({ + name: 'no-inferrable-types', + meta: { + type: 'suggestion', + docs: { + description: + 'Disallows explicit type declarations for variables or parameters initialized to a number, string, or boolean.', + tslintRuleName: 'no-inferrable-types', + category: 'Best Practices', + recommended: 'error' + }, + fixable: 'code', + messages: { + noInferrableType: + 'Type {{type}} trivially inferred from a {{type}} literal, remove type annotation.' + }, + schema: [ + { + type: 'object', + properties: { + ignoreParameters: { + type: 'boolean' + }, + ignoreProperties: { + type: 'boolean' + } + }, + additionalProperties: false + } + ] + }, + defaultOptions: [ + { + ignoreParameters: true, + ignoreProperties: true + } + ], + create(context, [{ ignoreParameters, ignoreProperties }]) { + /** + * Returns whether a node has an inferrable value or not + * @param node the node to check + * @param init the initializer + */ + function isInferrable( + node: TSESTree.TSTypeAnnotation, + init: TSESTree.Expression + ): boolean { + if ( + node.type !== AST_NODE_TYPES.TSTypeAnnotation || + !node.typeAnnotation + ) { + return false; + } + + const annotation = node.typeAnnotation; + + if (annotation.type === AST_NODE_TYPES.TSStringKeyword) { + if (init.type === AST_NODE_TYPES.Literal) { + return typeof init.value === 'string'; + } + return false; + } + + if (annotation.type === AST_NODE_TYPES.TSBooleanKeyword) { + return init.type === AST_NODE_TYPES.Literal; + } + + if (annotation.type === AST_NODE_TYPES.TSNumberKeyword) { + // Infinity is special + if ( + (init.type === AST_NODE_TYPES.UnaryExpression && + init.operator === '-' && + init.argument.type === AST_NODE_TYPES.Identifier && + init.argument.name === 'Infinity') || + (init.type === AST_NODE_TYPES.Identifier && init.name === 'Infinity') + ) { + return true; + } + + return ( + init.type === AST_NODE_TYPES.Literal && typeof init.value === 'number' + ); + } + + return false; + } + + /** + * Reports an inferrable type declaration, if any + * @param node the node being visited + * @param typeNode the type annotation node + * @param initNode the initializer node + */ + function reportInferrableType( + node: + | TSESTree.VariableDeclarator + | TSESTree.Parameter + | TSESTree.ClassProperty, + typeNode: TSESTree.TSTypeAnnotation | undefined, + initNode: TSESTree.Expression | null | undefined + ): void { + if (!typeNode || !initNode || !typeNode.typeAnnotation) { + return; + } + + if (!isInferrable(typeNode, initNode)) { + return; + } + + let type = null; + if (typeNode.typeAnnotation.type === AST_NODE_TYPES.TSBooleanKeyword) { + type = 'boolean'; + } else if ( + typeNode.typeAnnotation.type === AST_NODE_TYPES.TSNumberKeyword + ) { + type = 'number'; + } else if ( + typeNode.typeAnnotation.type === AST_NODE_TYPES.TSStringKeyword + ) { + type = 'string'; + } else { + // shouldn't happen... + return; + } + + context.report({ + node, + messageId: 'noInferrableType', + data: { + type + }, + fix: fixer => fixer.remove(typeNode) + }); + } + + function inferrableVariableVisitor( + node: TSESTree.VariableDeclarator + ): void { + if (!node.id) { + return; + } + reportInferrableType(node, node.id.typeAnnotation, node.init); + } + + function inferrableParameterVisitor( + node: + | TSESTree.FunctionExpression + | TSESTree.FunctionDeclaration + | TSESTree.ArrowFunctionExpression + ): void { + if (ignoreParameters || !node.params) { + return; + } + (node.params.filter( + param => + param.type === AST_NODE_TYPES.AssignmentPattern && + param.left && + param.right + ) as TSESTree.AssignmentPattern[]).forEach(param => { + reportInferrableType(param, param.left.typeAnnotation, param.right); + }); + } + + function inferrablePropertyVisitor(node: TSESTree.ClassProperty): void { + // We ignore `readonly` because of Microsoft/TypeScript#14416 + // Essentially a readonly property without a type + // will result in its value being the type, leading to + // compile errors if the type is stripped. + if (ignoreProperties || node.readonly) { + return; + } + reportInferrableType(node, node.typeAnnotation, node.value); + } + + return { + VariableDeclarator: inferrableVariableVisitor, + FunctionExpression: inferrableParameterVisitor, + FunctionDeclaration: inferrableParameterVisitor, + ArrowFunctionExpression: inferrableParameterVisitor, + ClassProperty: inferrablePropertyVisitor + }; + } +}); diff --git a/packages/eslint-plugin/src/rules/no-misused-new.ts b/packages/eslint-plugin/src/rules/no-misused-new.ts new file mode 100644 index 00000000000..dfd4c012f1f --- /dev/null +++ b/packages/eslint-plugin/src/rules/no-misused-new.ts @@ -0,0 +1,114 @@ +/** + * @fileoverview Enforce valid definition of `new` and `constructor`. + * @author Armano + */ + +import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/typescript-estree'; +import * as util from '../util'; + +export default util.createRule({ + name: 'no-misused-new', + meta: { + type: 'problem', + docs: { + description: 'Enforce valid definition of `new` and `constructor`.', + tslintRuleName: 'no-misused-new', + category: 'Best Practices', + recommended: 'error' + }, + schema: [], + messages: { + errorMessageInterface: 'Interfaces cannot be constructed, only classes.', + errorMessageClass: 'Class cannon have method named `new`.' + } + }, + defaultOptions: [], + create(context) { + /** + * @param {ASTNode} node type to be inspected. + * @returns name of simple type or null + */ + function getTypeReferenceName( + node: + | TSESTree.TSTypeAnnotation + | TSESTree.TypeNode + | TSESTree.EntityName + | undefined + ): string | null { + if (node) { + switch (node.type) { + case AST_NODE_TYPES.TSTypeAnnotation: + return getTypeReferenceName(node.typeAnnotation); + case AST_NODE_TYPES.TSTypeReference: + return getTypeReferenceName(node.typeName); + case AST_NODE_TYPES.Identifier: + return node.name; + default: + break; + } + } + return null; + } + + /** + * @param {ASTNode} parent parent node. + * @param {ASTNode} returnType type to be compared + */ + function isMatchingParentType( + parent: undefined | TSESTree.Node, + returnType: TSESTree.TSTypeAnnotation | undefined + ): boolean { + if ( + parent && + 'id' in parent && + parent.id && + parent.id.type === AST_NODE_TYPES.Identifier + ) { + return getTypeReferenceName(returnType) === parent.id.name; + } + return false; + } + + return { + 'TSInterfaceBody > TSConstructSignatureDeclaration'( + node: TSESTree.TSConstructSignatureDeclaration + ) { + if ( + isMatchingParentType( + node.parent!.parent as TSESTree.TSInterfaceDeclaration, + node.returnType + ) + ) { + // constructor + context.report({ + node, + messageId: 'errorMessageInterface' + }); + } + }, + "TSMethodSignature[key.name='constructor']"( + node: TSESTree.TSMethodSignature + ) { + context.report({ + node, + messageId: 'errorMessageInterface' + }); + }, + "ClassBody > MethodDefinition[key.name='new']"( + node: TSESTree.MethodDefinition + ) { + if (node.value.type === AST_NODE_TYPES.TSEmptyBodyFunctionExpression) { + if ( + node.parent && + isMatchingParentType(node.parent.parent, node.value.returnType) + ) { + context.report({ + node, + messageId: 'errorMessageClass' + }); + } + } + } + }; + } +}); diff --git a/packages/eslint-plugin/lib/rules/no-namespace.js b/packages/eslint-plugin/src/rules/no-namespace.ts similarity index 56% rename from packages/eslint-plugin/lib/rules/no-namespace.js rename to packages/eslint-plugin/src/rules/no-namespace.ts index fe4ca8bebb8..8e48d722092 100644 --- a/packages/eslint-plugin/lib/rules/no-namespace.js +++ b/packages/eslint-plugin/src/rules/no-namespace.ts @@ -2,30 +2,27 @@ * @fileoverview Disallows the use of custom TypeScript modules and namespaces. * @author Patricio Trevino */ -'use strict'; -const util = require('../util'); +import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/typescript-estree'; +import * as util from '../util'; -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -const defaultOptions = [ +type Options = [ { - allowDeclarations: false, - allowDefinitionFiles: true + allowDeclarations?: boolean; + allowDefinitionFiles?: boolean; } ]; +type MessageIds = 'moduleSyntaxIsPreferred'; -module.exports = { +export default util.createRule({ + name: 'no-namespace', meta: { type: 'suggestion', docs: { description: 'Disallow the use of custom TypeScript modules and namespaces', - extraDescription: [util.tslintRule('no-namespace')], - category: 'TypeScript', - url: util.metaDocsUrl('no-namespace'), + tslintRuleName: 'no-namespace', + category: 'Best Practices', recommended: 'error' }, messages: { @@ -47,21 +44,22 @@ module.exports = { } ] }, - - create(context) { - const { allowDeclarations, allowDefinitionFiles } = util.applyDefault( - defaultOptions, - context.options - )[0]; + defaultOptions: [ + { + allowDeclarations: false, + allowDefinitionFiles: true + } + ], + create(context, [{ allowDeclarations, allowDefinitionFiles }]) { const filename = context.getFilename(); - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- return { - "TSModuleDeclaration[global!=true][id.type='Identifier']"(node) { + "TSModuleDeclaration[global!=true][id.type='Identifier']"( + node: TSESTree.TSModuleDeclaration + ) { if ( - (node.parent && node.parent.type === 'TSModuleDeclaration') || + (node.parent && + node.parent.type === AST_NODE_TYPES.TSModuleDeclaration) || (allowDefinitionFiles && util.isDefinitionFile(filename)) || (allowDeclarations && node.declare === true) ) { @@ -75,4 +73,4 @@ module.exports = { } }; } -}; +}); diff --git a/packages/eslint-plugin/src/rules/no-non-null-assertion.ts b/packages/eslint-plugin/src/rules/no-non-null-assertion.ts new file mode 100644 index 00000000000..3abe06f71a2 --- /dev/null +++ b/packages/eslint-plugin/src/rules/no-non-null-assertion.ts @@ -0,0 +1,36 @@ +/** + * @fileoverview Disallows non-null assertions using the `!` postfix operator. + * @author Macklin Underdown + */ + +import { TSESTree } from '@typescript-eslint/typescript-estree'; +import * as util from '../util'; + +export default util.createRule({ + name: 'no-non-null-assertion', + meta: { + type: 'problem', + docs: { + description: + 'Disallows non-null assertions using the `!` postfix operator', + tslintRuleName: 'no-non-null-assertion', + category: 'Stylistic Issues', + recommended: 'error' + }, + messages: { + noNonNull: 'Forbidden non-null assertion.' + }, + schema: [] + }, + defaultOptions: [], + create(context) { + return { + TSNonNullExpression(node: TSESTree.TSNonNullExpression) { + context.report({ + node, + messageId: 'noNonNull' + }); + } + }; + } +}); diff --git a/packages/eslint-plugin/src/rules/no-object-literal-type-assertion.ts b/packages/eslint-plugin/src/rules/no-object-literal-type-assertion.ts new file mode 100644 index 00000000000..260bf47d95d --- /dev/null +++ b/packages/eslint-plugin/src/rules/no-object-literal-type-assertion.ts @@ -0,0 +1,88 @@ +/** + * @fileoverview Forbids an object literal to appear in a type assertion expression + * @author Armano + */ + +import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/typescript-estree'; +import * as util from '../util'; + +type Options = [ + { + allowAsParameter?: boolean; + } +]; +type MessageIds = 'unexpectedTypeAssertion'; + +export default util.createRule({ + name: 'no-object-literal-type-assertions', + meta: { + type: 'problem', + docs: { + description: + 'Forbids an object literal to appear in a type assertion expression', + tslintRuleName: 'no-object-literal-type-assertion', + category: 'Stylistic Issues', + recommended: 'error' + }, + messages: { + unexpectedTypeAssertion: + 'Type assertion on object literals is forbidden, use a type annotation instead.' + }, + schema: [ + { + type: 'object', + additionalProperties: false, + properties: { + allowAsParameter: { + type: 'boolean' + } + } + } + ] + }, + defaultOptions: [ + { + allowAsParameter: false + } + ], + create(context, [{ allowAsParameter }]) { + /** + * Check whatever node should be reported + * @param node the node to be evaluated. + */ + function checkType(node: TSESTree.TypeNode): boolean { + switch (node.type) { + case AST_NODE_TYPES.TSAnyKeyword: + case AST_NODE_TYPES.TSUnknownKeyword: + return false; + default: + return true; + } + } + + return { + 'TSTypeAssertion, TSAsExpression'( + node: TSESTree.TSTypeAssertion | TSESTree.TSAsExpression + ) { + if ( + allowAsParameter && + node.parent && + (node.parent.type === AST_NODE_TYPES.NewExpression || + node.parent.type === AST_NODE_TYPES.CallExpression) + ) { + return; + } + + if ( + checkType(node.typeAnnotation) && + node.expression.type === AST_NODE_TYPES.ObjectExpression + ) { + context.report({ + node, + messageId: 'unexpectedTypeAssertion' + }); + } + } + }; + } +}); diff --git a/packages/eslint-plugin/src/rules/no-parameter-properties.ts b/packages/eslint-plugin/src/rules/no-parameter-properties.ts new file mode 100644 index 00000000000..cce3af7b362 --- /dev/null +++ b/packages/eslint-plugin/src/rules/no-parameter-properties.ts @@ -0,0 +1,116 @@ +/** + * @fileoverview Disallows parameter properties in class constructors. + * @author Patricio Trevino + */ + +import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/typescript-estree'; +import * as util from '../util'; + +type Modifier = + | 'readonly' + | 'private' + | 'protected' + | 'public' + | 'private readonly' + | 'protected readonly' + | 'public readonly'; +type Options = [ + { + allows: Modifier[]; + } +]; +type MessageIds = 'noParamProp'; + +export default util.createRule({ + name: 'no-parameter-properties', + meta: { + type: 'problem', + docs: { + description: + 'Disallow the use of parameter properties in class constructors.', + tslintRuleName: 'no-parameter-properties', + category: 'Stylistic Issues', + recommended: 'error' + }, + messages: { + noParamProp: + 'Property {{parameter}} cannot be declared in the constructor.' + }, + schema: [ + { + type: 'object', + properties: { + allows: { + type: 'array', + items: { + enum: [ + 'readonly', + 'private', + 'protected', + 'public', + 'private readonly', + 'protected readonly', + 'public readonly' + ] + }, + minItems: 1 + } + }, + additionalProperties: false + } + ] + }, + defaultOptions: [ + { + allows: [] + } + ], + create(context, [{ allows }]) { + /** + * Gets the modifiers of `node`. + * @param node the node to be inspected. + */ + function getModifiers(node: TSESTree.TSParameterProperty): Modifier { + const modifiers: Modifier[] = []; + + if (node.accessibility) { + modifiers.push(node.accessibility); + } + if (node.readonly) { + modifiers.push('readonly'); + } + + return modifiers.filter(Boolean).join(' ') as Modifier; + } + + return { + TSParameterProperty(node: TSESTree.TSParameterProperty) { + const modifiers = getModifiers(node); + + if (allows.indexOf(modifiers) === -1) { + // HAS to be an identifier or assignment or TSC will throw + if ( + node.parameter.type !== AST_NODE_TYPES.Identifier && + node.parameter.type !== AST_NODE_TYPES.AssignmentPattern + ) { + return; + } + + const name = + node.parameter.type === AST_NODE_TYPES.Identifier + ? node.parameter.name + : // has to be an Identifier or TSC will throw an error + (node.parameter.left as TSESTree.Identifier).name; + + context.report({ + node, + messageId: 'noParamProp', + data: { + parameter: name + } + }); + } + } + }; + } +}); diff --git a/packages/eslint-plugin/src/rules/no-require-imports.ts b/packages/eslint-plugin/src/rules/no-require-imports.ts new file mode 100644 index 00000000000..0985c6a91f4 --- /dev/null +++ b/packages/eslint-plugin/src/rules/no-require-imports.ts @@ -0,0 +1,41 @@ +/** + * @fileoverview Disallows invocation of `require()`. + * @author Kanitkorn Sujautra + */ + +import { TSESTree } from '@typescript-eslint/typescript-estree'; +import * as util from '../util'; + +export default util.createRule({ + name: 'no-require-imports', + meta: { + type: 'problem', + docs: { + description: 'Disallows invocation of `require()`.', + tslintName: 'no-require-imports', + category: 'Best Practices', + recommended: 'error' + }, + schema: [], + messages: { + noRequireImports: 'A `require()` style import is forbidden.' + } + }, + defaultOptions: [], + create(context) { + return { + 'CallExpression > Identifier[name="require"]'(node: TSESTree.Identifier) { + context.report({ + node: node.parent!, + messageId: 'noRequireImports' + }); + }, + TSExternalModuleReference(node: TSESTree.TSExternalModuleReference) { + context.report({ + node, + messageId: 'noRequireImports' + }); + } + }; + } +}); diff --git a/packages/eslint-plugin/src/rules/no-this-alias.ts b/packages/eslint-plugin/src/rules/no-this-alias.ts new file mode 100644 index 00000000000..6f0c34add03 --- /dev/null +++ b/packages/eslint-plugin/src/rules/no-this-alias.ts @@ -0,0 +1,83 @@ +/** + * @fileoverview Disallow aliasing `this` + * @author Jed Fox + */ + +import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/typescript-estree'; +import * as util from '../util'; + +type Options = [ + { + allowDestructuring?: boolean; + allowedNames?: string[]; + } +]; +type MessageIds = 'thisAssignment' | 'thisDestructure'; + +export default util.createRule({ + name: 'no-this-alias', + meta: { + type: 'suggestion', + docs: { + description: 'Disallow aliasing `this`', + tslintRuleName: 'no-this-assignment', + category: 'Best Practices', + recommended: false + }, + schema: [ + { + type: 'object', + additionalProperties: false, + properties: { + allowDestructuring: { + type: 'boolean' + }, + allowedNames: { + type: 'array', + items: { + type: 'string' + } + } + } + } + ], + messages: { + thisAssignment: "Unexpected aliasing of 'this' to local variable.", + thisDestructure: + "Unexpected aliasing of members of 'this' to local variables." + } + }, + defaultOptions: [ + { + allowDestructuring: false, + allowedNames: [] + } + ], + create(context, [{ allowDestructuring, allowedNames }]) { + return { + "VariableDeclarator[init.type='ThisExpression']"( + node: TSESTree.VariableDeclarator + ) { + const { id } = node; + + if (allowDestructuring && id.type !== AST_NODE_TYPES.Identifier) { + return; + } + + const hasAllowedName = + id.type === AST_NODE_TYPES.Identifier + ? allowedNames!.includes(id.name) + : false; + if (!hasAllowedName) { + context.report({ + node: id, + messageId: + id.type === AST_NODE_TYPES.Identifier + ? 'thisAssignment' + : 'thisDestructure' + }); + } + } + }; + } +}); diff --git a/packages/eslint-plugin/src/rules/no-triple-slash-reference.ts b/packages/eslint-plugin/src/rules/no-triple-slash-reference.ts new file mode 100644 index 00000000000..9691edb201e --- /dev/null +++ b/packages/eslint-plugin/src/rules/no-triple-slash-reference.ts @@ -0,0 +1,47 @@ +/** + * @fileoverview Enforces triple slash references are not used. + * @author Danny Fritz + */ + +import { TSESTree } from '@typescript-eslint/typescript-estree'; +import * as util from '../util'; + +export default util.createRule({ + name: 'no-triple-slash-reference', + meta: { + type: 'suggestion', + docs: { + description: 'Disallow `/// ` comments', + tslintRuleName: 'no-reference', + category: 'Best Practices', + recommended: 'error' + }, + schema: [], + messages: { + tripleSlashReference: 'Do not use a triple slash reference.' + } + }, + defaultOptions: [], + create(context) { + const referenceRegExp = /^\/\s* { + if (comment.type !== 'Line') { + return; + } + if (referenceRegExp.test(comment.value)) { + context.report({ + node: comment, + messageId: 'tripleSlashReference' + }); + } + }); + } + }; + } +}); diff --git a/packages/eslint-plugin/lib/rules/no-type-alias.js b/packages/eslint-plugin/src/rules/no-type-alias.ts similarity index 50% rename from packages/eslint-plugin/lib/rules/no-type-alias.js rename to packages/eslint-plugin/src/rules/no-type-alias.ts index e43b5964220..d73d436a002 100644 --- a/packages/eslint-plugin/lib/rules/no-type-alias.js +++ b/packages/eslint-plugin/src/rules/no-type-alias.ts @@ -2,31 +2,44 @@ * @fileoverview Disallows the use of type aliases. * @author Patricio Trevino */ -'use strict'; -const util = require('../util'); +import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/typescript-estree'; +import { ReportDescriptor } from 'ts-eslint'; +import * as util from '../util'; -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -const defaultOptions = [ +type Options = [ { - allowAliases: 'never', - allowCallbacks: 'never', - allowLiterals: 'never', - allowMappedTypes: 'never' + allowAliases?: + | 'always' + | 'never' + | 'in-unions' + | 'in-intersections' + | 'in-unions-and-intersections'; + allowCallbacks?: 'always' | 'never'; + allowLiterals?: + | 'always' + | 'never' + | 'in-unions' + | 'in-intersections' + | 'in-unions-and-intersections'; + allowMappedTypes?: + | 'always' + | 'never' + | 'in-unions' + | 'in-intersections' + | 'in-unions-and-intersections'; } ]; +type MessageIds = 'noTypeAlias' | 'noCompositionAlias'; -module.exports = { +export default util.createRule({ + name: 'no-type-alias', meta: { type: 'suggestion', docs: { description: 'Disallow the use of type aliases', - extraDescription: [util.tslintRule('interface-over-type-literal')], - category: 'TypeScript', - url: util.metaDocsUrl('no-type-alias'), + tslintRuleName: 'interface-over-type-literal', + category: 'Stylistic Issues', recommended: false }, messages: { @@ -73,15 +86,18 @@ module.exports = { } ] }, - - create(context) { - const { - allowAliases, - allowCallbacks, - allowLiterals, - allowMappedTypes - } = util.applyDefault(defaultOptions, context.options)[0]; - + defaultOptions: [ + { + allowAliases: 'never', + allowCallbacks: 'never', + allowLiterals: 'never', + allowMappedTypes: 'never' + } + ], + create( + context, + [{ allowAliases, allowCallbacks, allowLiterals, allowMappedTypes }] + ) { const unions = ['always', 'in-unions', 'in-unions-and-intersections']; const intersections = [ 'always', @@ -94,55 +110,51 @@ module.exports = { 'in-unions-and-intersections' ]; const aliasTypes = [ - 'TSLastTypeNode', - 'TSArrayType', - 'TSTypeReference', - 'TSLiteralType' + AST_NODE_TYPES.TSArrayType, + AST_NODE_TYPES.TSTypeReference, + AST_NODE_TYPES.TSLiteralType ]; - //---------------------------------------------------------------------- - // Helpers - //---------------------------------------------------------------------- - + type CompositionType = TSESTree.TSUnionType | TSESTree.TSIntersectionType; /** * Determines if the given node is a union or an intersection. - * @param {TSNode} node the node to be evaluated. - * @returns {boolean} true when node if a union or intersection. - * @private */ - function isComposition(node) { + function isComposition(node: TSESTree.TypeNode): node is CompositionType { return ( node && - (node.type === 'TSUnionType' || node.type === 'TSIntersectionType') + (node.type === AST_NODE_TYPES.TSUnionType || + node.type === AST_NODE_TYPES.TSIntersectionType) ); } /** * Determines if the composition type is supported by the allowed flags. - * @param {boolean} isTopLevel a flag indicating this is the top level node. - * @param {string} compositionType the composition type (either TSUnionType or TSIntersectionType) - * @param {string} allowed the currently allowed flags. - * @returns {boolean} true if the composition type supported by the allowed flags. - * @private + * @param isTopLevel a flag indicating this is the top level node. + * @param compositionType the composition type (either TSUnionType or TSIntersectionType) + * @param allowed the currently allowed flags. */ - function isSupportedComposition(isTopLevel, compositionType, allowed) { + function isSupportedComposition( + isTopLevel: boolean, + compositionType: string | undefined, + allowed: string + ): boolean { return ( compositions.indexOf(allowed) === -1 || (!isTopLevel && - ((compositionType === 'TSUnionType' && + ((compositionType === AST_NODE_TYPES.TSUnionType && unions.indexOf(allowed) > -1) || - (compositionType === 'TSIntersectionType' && + (compositionType === AST_NODE_TYPES.TSIntersectionType && intersections.indexOf(allowed) > -1))) ); } /** * Determines if the given node is an alias type (keywords, arrays, type references and constants). - * @param {TSNode} node the node to be evaluated. - * @returns {boolean} true when the node is an alias type. - * @private + * @param node the node to be evaluated. */ - function isAlias(node) { + function isAlias( + node: TSESTree.Node + ): boolean /* not worth enumerating the ~25 individual types here */ { return ( node && (/Keyword$/.test(node.type) || aliasTypes.indexOf(node.type) > -1) @@ -151,45 +163,42 @@ module.exports = { /** * Determines if the given node is a callback type. - * @param {TSNode} node the node to be evaluated. - * @returns {boolean} true when the node is a callback type. - * @private + * @param node the node to be evaluated. */ - function isCallback(node) { - return node && node.type === 'TSFunctionType'; + function isCallback(node: TSESTree.Node): node is TSESTree.TSFunctionType { + return node && node.type === AST_NODE_TYPES.TSFunctionType; } /** * Determines if the given node is a literal type (objects). - * @param {TSNode} node the node to be evaluated. - * @returns {boolean} true when the node is a literal type (object). - * @private + * @param node the node to be evaluated. */ - function isLiteral(node) { - return node && node.type === 'TSTypeLiteral'; + function isLiteral(node: TSESTree.Node): node is TSESTree.TSTypeLiteral { + return node && node.type === AST_NODE_TYPES.TSTypeLiteral; } /** * Determines if the given node is a mapped type. - * @param {TSNode} node the node to be evaluated. - * @returns {boolean} true when the node is a mapped type. - * @private + * @param node the node to be evaluated. */ - function isMappedType(node) { - return node && node.type === 'TSMappedType'; + function isMappedType(node: TSESTree.Node): node is TSESTree.TSMappedType { + return node && node.type === AST_NODE_TYPES.TSMappedType; } /** * Gets the message to be displayed based on the node type and whether the node is a top level declaration. - * @param {Object} node the location - * @param {string} compositionType the type of composition this alias is part of (undefined if not + * @param node the location + * @param compositionType the type of composition this alias is part of (undefined if not * part of a composition) - * @param {boolean} isRoot a flag indicating we are dealing with the top level declaration. - * @param {string} type the kind of type alias being validated. - * @returns {string} the message to be displayed. - * @private + * @param isRoot a flag indicating we are dealing with the top level declaration. + * @param type the kind of type alias being validated. */ - function getMessage(node, compositionType, isRoot, type) { + function getMessage( + node: TSESTree.Node, + compositionType: string | undefined, + isRoot: boolean, + type?: string + ): ReportDescriptor { if (isRoot) { return { node, @@ -205,32 +214,27 @@ module.exports = { messageId: 'noCompositionAlias', data: { compositionType: - compositionType === 'TSUnionType' ? 'union' : 'intersection', - typeName: util.upperCaseFirst(type) + compositionType === AST_NODE_TYPES.TSUnionType + ? 'union' + : 'intersection', + typeName: util.upperCaseFirst(type!) } }; } /** * Validates the node looking for aliases, callbacks and literals. - * @param {TSNode} node the node to be validated. - * @param {boolean} isTopLevel a flag indicating this is the top level node. - * @param {boolean} compositionType the type of composition this alias is part of (undefined if not + * @param node the node to be validated. + * @param isTopLevel a flag indicating this is the top level node. + * @param compositionType the type of composition this alias is part of (undefined if not * part of a composition) - * @returns {void} - * @private */ - function validateTypeAliases(node, isTopLevel, compositionType) { - if (isAlias(node)) { - if ( - allowAliases === 'never' || - !isSupportedComposition(isTopLevel, compositionType, allowAliases) - ) { - context.report( - getMessage(node, compositionType, isTopLevel, 'aliases') - ); - } - } else if (isCallback(node)) { + function validateTypeAliases( + node: TSESTree.Node, + isTopLevel: boolean, + compositionType?: string + ): void { + if (isCallback(node)) { if (allowCallbacks === 'never') { context.report( getMessage(node, compositionType, isTopLevel, 'callbacks') @@ -239,7 +243,7 @@ module.exports = { } else if (isLiteral(node)) { if ( allowLiterals === 'never' || - !isSupportedComposition(isTopLevel, compositionType, allowLiterals) + !isSupportedComposition(isTopLevel, compositionType, allowLiterals!) ) { context.report( getMessage(node, compositionType, isTopLevel, 'literals') @@ -248,12 +252,25 @@ module.exports = { } else if (isMappedType(node)) { if ( allowMappedTypes === 'never' || - !isSupportedComposition(isTopLevel, compositionType, allowMappedTypes) + !isSupportedComposition( + isTopLevel, + compositionType, + allowMappedTypes! + ) ) { context.report( getMessage(node, compositionType, isTopLevel, 'mapped types') ); } + } else if (isAlias(node)) { + if ( + allowAliases === 'never' || + !isSupportedComposition(isTopLevel, compositionType, allowAliases!) + ) { + context.report( + getMessage(node, compositionType, isTopLevel, 'aliases') + ); + } } else { context.report(getMessage(node, compositionType, isTopLevel)); } @@ -261,11 +278,8 @@ module.exports = { /** * Validates compositions (unions and/or intersections). - * @param {TSNode} node the node to be validated. - * @returns {void} - * @private */ - function validateCompositions(node) { + function validateCompositions(node: CompositionType): void { node.types.forEach(type => { if (isComposition(type)) { validateCompositions(type); @@ -275,28 +289,14 @@ module.exports = { }); } - /** - * Validates the node looking for compositions, aliases, callbacks and literals. - * @param {TSNode} node the node to be validated. - * @param {boolean} isTopLevel a flag indicating this is the top level node. - * @returns {void} - * @private - */ - function validateNode(node, isTopLevel) { - if (isComposition(node)) { - validateCompositions(node); - } else { - validateTypeAliases(node, isTopLevel); - } - } - - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- return { - TSTypeAliasDeclaration(node) { - validateNode(node.typeAnnotation, true); + TSTypeAliasDeclaration(node: TSESTree.TSTypeAliasDeclaration) { + if (isComposition(node.typeAnnotation)) { + validateCompositions(node.typeAnnotation); + } else { + validateTypeAliases(node.typeAnnotation, true); + } } }; } -}; +}); diff --git a/packages/eslint-plugin/lib/rules/no-unnecessary-type-assertion.js b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts similarity index 65% rename from packages/eslint-plugin/lib/rules/no-unnecessary-type-assertion.js rename to packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts index bcbe300a01b..98dcf3f0597 100644 --- a/packages/eslint-plugin/lib/rules/no-unnecessary-type-assertion.js +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts @@ -3,28 +3,27 @@ * @author Benjamin Lichtman */ -'use strict'; -const tsutils = require('tsutils'); -const ts = require('typescript'); -const util = require('../util'); - -/** @typedef {import("estree").Node} Node */ -/** @typedef {import("eslint").Rule.RuleContext} Context */ - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ +import { TSESTree } from '@typescript-eslint/typescript-estree'; +import * as tsutils from 'tsutils'; +import ts from 'typescript'; +import * as util from '../util'; + +type Options = [ + { + typesToIgnore?: string[]; + } +]; +type MessageIds = 'unnecessaryAssertion'; -/** @type {import("eslint").Rule.RuleModule} */ -module.exports = { +export default util.createRule({ + name: 'no-unnecessary-type-assertion', meta: { docs: { description: 'Warns if a type assertion does not change the type of an expression', - category: 'TypeScript-specific', + category: 'Best Practices', recommended: false, - extraDescription: [util.tslintRule('no-unnecessary-type-assertion')], - url: util.metaDocsUrl('no-unnecessary-type-assertion') + tslintRuleName: 'no-unnecessary-type-assertion' }, fixable: 'code', messages: { @@ -46,18 +45,16 @@ module.exports = { ], type: 'suggestion' }, - - create(context) { + defaultOptions: [{}], + create(context, [options]) { const sourceCode = context.getSourceCode(); - const checker = util.getParserServices(context).program.getTypeChecker(); + const parserServices = util.getParserServices(context); /** * Sometimes tuple types don't have ObjectFlags.Tuple set, like when they're being matched against an inferred type. * So, in addition, check if there are integer properties 0..n and no other numeric keys - * @param {ts.ObjectType} type type - * @returns {boolean} true if type could be a tuple type */ - function couldBeTupleType(type) { + function couldBeTupleType(type: ts.ObjectType): boolean { const properties = type.getProperties(); if (properties.length === 0) { @@ -84,18 +81,13 @@ module.exports = { return true; } - /** - * @param {Node} node node being linted - * @returns {void} - */ - function checkNonNullAssertion(node) { - /** - * Corresponding TSNode is guaranteed to be in map - * @type {ts.NonNullExpression} - */ - const originalNode = context.parserServices.esTreeNodeToTSNodeMap.get( - node - ); + function checkNonNullAssertion( + node: TSESTree.Node, + checker: ts.TypeChecker + ): void { + const originalNode = parserServices.esTreeNodeToTSNodeMap.get< + ts.NonNullExpression + >(node); const type = checker.getTypeAtLocation(originalNode.expression); if (type === checker.getNonNullableType(type)) { @@ -112,13 +104,10 @@ module.exports = { } } - /** - * @param {Node} node node being linted - * @returns {void} - */ - function verifyCast(node) { - const options = context.options[0]; - + function verifyCast( + node: TSESTree.TSTypeAssertion | TSESTree.TSAsExpression, + checker: ts.TypeChecker + ): void { if ( options && options.typesToIgnore && @@ -129,13 +118,9 @@ module.exports = { return; } - /** - * Corresponding TSNode is guaranteed to be in map - * @type {ts.AssertionExpression} - */ - const originalNode = context.parserServices.esTreeNodeToTSNodeMap.get( - node - ); + const originalNode = parserServices.esTreeNodeToTSNodeMap.get< + ts.AssertionExpression + >(node); const castType = checker.getTypeAtLocation(originalNode); if ( @@ -170,16 +155,18 @@ module.exports = { } } + const checker = parserServices.program.getTypeChecker(); + return { - TSNonNullExpression(node) { - checkNonNullAssertion(node); + TSNonNullExpression(node: TSESTree.TSNonNullExpression) { + checkNonNullAssertion(node, checker); }, - TSTypeAssertion(node) { - verifyCast(node); + TSTypeAssertion(node: TSESTree.TSTypeAssertion) { + verifyCast(node, checker); }, - TSAsExpression(node) { - verifyCast(node); + TSAsExpression(node: TSESTree.TSAsExpression) { + verifyCast(node, checker); } }; } -}; +}); diff --git a/packages/eslint-plugin/lib/rules/no-unused-vars.js b/packages/eslint-plugin/src/rules/no-unused-vars.ts similarity index 61% rename from packages/eslint-plugin/lib/rules/no-unused-vars.js rename to packages/eslint-plugin/src/rules/no-unused-vars.ts index 79c60d437c2..2d63aa4564f 100644 --- a/packages/eslint-plugin/lib/rules/no-unused-vars.js +++ b/packages/eslint-plugin/src/rules/no-unused-vars.ts @@ -2,38 +2,33 @@ * @fileoverview Prevent TypeScript-specific variables being falsely marked as unused * @author James Henry */ -'use strict'; -const baseRule = require('eslint/lib/rules/no-unused-vars'); -const util = require('../util'); +import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/typescript-estree'; +import baseRule from 'eslint/lib/rules/no-unused-vars'; +import * as util from '../util'; -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -module.exports = Object.assign({}, baseRule, { +export default util.createRule({ + name: 'no-unused-vars', meta: { type: 'problem', docs: { description: 'Disallow unused variables', - extraDescription: [util.tslintRule('no-unused-variable')], + tslintRuleName: 'no-unused-variable', category: 'Variables', - url: util.metaDocsUrl('no-unused-vars'), recommended: 'warn' }, schema: baseRule.meta.schema, messages: baseRule.meta.messages }, - + defaultOptions: [], create(context) { const rules = baseRule.create(context); /** * Mark this function parameter as used - * @param {Identifier} node The node currently being traversed - * @returns {void} + * @param node The node currently being traversed */ - function markThisParameterAsUsed(node) { + function markThisParameterAsUsed(node: TSESTree.Identifier): void { if (node.name) { const variable = context .getScope() @@ -48,55 +43,53 @@ module.exports = Object.assign({}, baseRule, { /** * Mark heritage clause as used * @param node The node currently being traversed - * @returns {void} */ - function markHeritageAsUsed(node) { + function markHeritageAsUsed(node: TSESTree.Expression): void { switch (node.type) { - case 'Identifier': + case AST_NODE_TYPES.Identifier: context.markVariableAsUsed(node.name); break; - case 'MemberExpression': + case AST_NODE_TYPES.MemberExpression: markHeritageAsUsed(node.object); break; - case 'CallExpression': + case AST_NODE_TYPES.CallExpression: markHeritageAsUsed(node.callee); break; } } - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- return Object.assign({}, rules, { "FunctionDeclaration Identifier[name='this']": markThisParameterAsUsed, "FunctionExpression Identifier[name='this']": markThisParameterAsUsed, - 'TSTypeReference Identifier'(node) { + 'TSTypeReference Identifier'(node: TSESTree.Identifier) { context.markVariableAsUsed(node.name); }, - TSInterfaceHeritage(node) { + TSInterfaceHeritage(node: TSESTree.TSInterfaceHeritage) { if (node.expression) { markHeritageAsUsed(node.expression); } }, - TSClassImplements(node) { + TSClassImplements(node: TSESTree.TSClassImplements) { if (node.expression) { markHeritageAsUsed(node.expression); } }, - 'TSParameterProperty Identifier'(node) { + 'TSParameterProperty Identifier'(node: TSESTree.Identifier) { // just assume parameter properties are used context.markVariableAsUsed(node.name); }, - 'TSEnumMember Identifier'(node) { + 'TSEnumMember Identifier'(node: TSESTree.Identifier) { context.markVariableAsUsed(node.name); }, - '*[declare=true] Identifier'(node) { + '*[declare=true] Identifier'(node: TSESTree.Identifier) { context.markVariableAsUsed(node.name); const scope = context.getScope(); const { variableScope } = scope; if (variableScope !== scope) { const superVar = variableScope.set.get(node.name); - if (superVar) superVar.eslintUsed = true; + if (superVar) { + superVar.eslintUsed = true; + } } } }); diff --git a/packages/eslint-plugin/lib/rules/no-use-before-define.js b/packages/eslint-plugin/src/rules/no-use-before-define.ts similarity index 54% rename from packages/eslint-plugin/lib/rules/no-use-before-define.js rename to packages/eslint-plugin/src/rules/no-use-before-define.ts index 437fd0ffd49..6aff11933bf 100644 --- a/packages/eslint-plugin/lib/rules/no-use-before-define.js +++ b/packages/eslint-plugin/src/rules/no-use-before-define.ts @@ -1,29 +1,19 @@ /** * @fileoverview Rule to flag use of variables before they are defined - * @copyright ESLint - * @see https://github.com/eslint/eslint/blob/a113cd3/lib/rules/no-use-before-define.js * @author Ilya Volodin * @author Jed Fox */ -'use strict'; - -const util = require('../util'); - -//------------------------------------------------------------------------------ -// Helpers -//------------------------------------------------------------------------------ +import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/typescript-estree'; +import { Scope } from 'ts-eslint'; +import * as util from '../util'; const SENTINEL_TYPE = /^(?:(?:Function|Class)(?:Declaration|Expression)|ArrowFunctionExpression|CatchClause|ImportDeclaration|ExportNamedDeclaration)$/; -const FOR_IN_OF_TYPE = /^For(?:In|Of)Statement$/; /** * Parses a given value as options. - * - * @param {any} options - A value to parse. - * @returns {Object} The parsed options. */ -function parseOptions(options) { +function parseOptions(options: string | Config | null): Required { let functions = true; let classes = true; let variables = true; @@ -42,31 +32,26 @@ function parseOptions(options) { } /** - * @param {Scope} scope - a scope to check - * @returns {boolean} `true` if the scope is toplevel + * Checks whether or not a given scope is a top level scope. */ -function isTopLevelScope(scope) { +function isTopLevelScope(scope: Scope.Scope): boolean { return scope.type === 'module' || scope.type === 'global'; } /** * Checks whether or not a given variable is a function declaration. - * - * @param {eslint-scope.Variable} variable - A variable to check. - * @returns {boolean} `true` if the variable is a function declaration. */ -function isFunction(variable) { +function isFunction(variable: Scope.Variable): boolean { return variable.defs[0].type === 'FunctionName'; } /** * Checks whether or not a given variable is a class declaration in an upper function scope. - * - * @param {eslint-scope.Variable} variable - A variable to check. - * @param {eslint-scope.Reference} reference - A reference to check. - * @returns {boolean} `true` if the variable is a class declaration. */ -function isOuterClass(variable, reference) { +function isOuterClass( + variable: Scope.Variable, + reference: Scope.Reference +): boolean { if (variable.defs[0].type !== 'ClassName') { return false; } @@ -83,11 +68,11 @@ function isOuterClass(variable, reference) { /** * Checks whether or not a given variable is a variable declaration in an upper function scope. - * @param {eslint-scope.Variable} variable - A variable to check. - * @param {eslint-scope.Reference} reference - A reference to check. - * @returns {boolean} `true` if the variable is a variable declaration. */ -function isOuterVariable(variable, reference) { +function isOuterVariable( + variable: Scope.Variable, + reference: Scope.Reference +): boolean { if (variable.defs[0].type !== 'Variable') { return false; } @@ -102,45 +87,30 @@ function isOuterVariable(variable, reference) { return true; } -/** - * Checks whether or not a given variable is a type declaration. - * @param {eslint-scope.Variable} variable - A type to check. - * @returns {boolean} `true` if the variable is a type. - */ -function isType(variable) { - return ( - variable.defs[0].type === 'Variable' && - variable.defs[0].parent.kind === 'type' - ); -} - /** * Checks whether or not a given location is inside of the range of a given node. - * - * @param {ASTNode} node - An node to check. - * @param {number} location - A location to check. - * @returns {boolean} `true` if the location is inside of the range of the node. */ -function isInRange(node, location) { - return node && node.range[0] <= location && location <= node.range[1]; +function isInRange( + node: TSESTree.Expression | null | undefined, + location: number +): boolean { + return !!node && node.range[0] <= location && location <= node.range[1]; } /** * Checks whether or not a given reference is inside of the initializers of a given variable. * - * This returns `true` in the following cases: - * - * var a = a - * var [a = a] = list - * var {a = a} = obj - * for (var a in a) {} - * for (var a of a) {} - * - * @param {Variable} variable - A variable to check. - * @param {Reference} reference - A reference to check. - * @returns {boolean} `true` if the reference is inside of the initializers. + * @returns `true` in the following cases: + * - var a = a + * - var [a = a] = list + * - var {a = a} = obj + * - for (var a in a) {} + * - for (var a of a) {} */ -function isInInitializer(variable, reference) { +function isInInitializer( + variable: Scope.Variable, + reference: Scope.Reference +): boolean { if (variable.scope !== reference.from) { return false; } @@ -149,18 +119,21 @@ function isInInitializer(variable, reference) { const location = reference.identifier.range[1]; while (node) { - if (node.type === 'VariableDeclarator') { + if (node.type === AST_NODE_TYPES.VariableDeclarator) { if (isInRange(node.init, location)) { return true; } if ( - FOR_IN_OF_TYPE.test(node.parent.parent.type) && + node.parent && + node.parent.parent && + (node.parent.parent.type === AST_NODE_TYPES.ForInStatement || + node.parent.parent.type === AST_NODE_TYPES.ForOfStatement) && isInRange(node.parent.parent.right, location) ) { return true; } break; - } else if (node.type === 'AssignmentPattern') { + } else if (node.type === AST_NODE_TYPES.AssignmentPattern) { if (isInRange(node.right, location)) { return true; } @@ -174,28 +147,27 @@ function isInInitializer(variable, reference) { return false; } -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -const defaultOptions = [ - { - functions: true, - classes: true, - variables: true, - typedefs: true - } -]; +interface Config { + functions?: boolean; + classes?: boolean; + variables?: boolean; + typedefs?: boolean; +} +type Options = ['nofunc' | Config]; +type MessageIds = 'noUseBeforeDefine'; -module.exports = { +export default util.createRule({ + name: 'no-use-before-define', meta: { type: 'problem', docs: { description: 'Disallow the use of variables before they are defined', category: 'Variables', - url: util.metaDocsUrl('no-use-before-define'), recommended: 'error' }, + messages: { + noUseBeforeDefine: "'{{name}}' was used before it was defined." + }, schema: [ { oneOf: [ @@ -216,41 +188,42 @@ module.exports = { } ] }, - - create(context) { - const options = parseOptions( - util.applyDefault(defaultOptions, context.options)[0] - ); + defaultOptions: [ + { + functions: true, + classes: true, + variables: true, + typedefs: true + } + ], + create(context, optionsWithDefault) { + const options = parseOptions(optionsWithDefault[0]); /** * Determines whether a given use-before-define case should be reported according to the options. - * @param {eslint-scope.Variable} variable The variable that gets used before being defined - * @param {eslint-scope.Reference} reference The reference to the variable - * @returns {boolean} `true` if the usage should be reported + * @param variable The variable that gets used before being defined + * @param reference The reference to the variable */ - function isForbidden(variable, reference) { + function isForbidden( + variable: Scope.Variable, + reference: Scope.Reference + ): boolean { if (isFunction(variable)) { - return options.functions; + return !!options.functions; } if (isOuterClass(variable, reference)) { - return options.classes; - } - if (isType(variable) && !options.typedefs) { - return false; + return !!options.classes; } if (isOuterVariable(variable, reference)) { - return options.variables; + return !!options.variables; } return true; } /** * Finds and validates all variables in a given scope. - * @param {Scope} scope The scope object. - * @returns {void} - * @private */ - function findVariablesInScope(scope) { + function findVariablesInScope(scope: Scope.Scope): void { scope.references.forEach(reference => { const variable = reference.resolved; @@ -264,7 +237,7 @@ module.exports = { reference.init || !variable || variable.identifiers.length === 0 || - (variable.identifiers[0].range[1] < reference.identifier.range[1] && + (variable.identifiers[0].range![1] < reference.identifier.range![1] && !isInInitializer(variable, reference)) || !isForbidden(variable, reference) ) { @@ -274,7 +247,7 @@ module.exports = { // Reports. context.report({ node: reference.identifier, - message: "'{{name}}' was used before it was defined.", + messageId: 'noUseBeforeDefine', data: reference.identifier }); }); @@ -288,4 +261,4 @@ module.exports = { } }; } -}; +}); diff --git a/packages/eslint-plugin/lib/rules/no-useless-constructor.js b/packages/eslint-plugin/src/rules/no-useless-constructor.ts similarity index 50% rename from packages/eslint-plugin/lib/rules/no-useless-constructor.js rename to packages/eslint-plugin/src/rules/no-useless-constructor.ts index c30cdcf872b..944ad6ff0c6 100644 --- a/packages/eslint-plugin/lib/rules/no-useless-constructor.js +++ b/packages/eslint-plugin/src/rules/no-useless-constructor.ts @@ -2,29 +2,28 @@ * @fileoverview Disallow unnecessary constructors * @author Armano */ -'use strict'; -const baseRule = require('eslint/lib/rules/no-useless-constructor'); -const util = require('../util'); +import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/typescript-estree'; +import baseRule from 'eslint/lib/rules/no-useless-constructor'; +import * as util from '../util'; -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ +type Options = util.InferOptionsTypeFromRule; +type MessageIds = util.InferMessageIdsTypeFromRule; /** * Check if method with accessibility is not useless - * @param {MethodDefinition} node - * @returns {boolean} */ -function checkAccessibility(node) { +function checkAccessibility(node: TSESTree.MethodDefinition): boolean { switch (node.accessibility) { case 'protected': case 'private': return false; case 'public': if ( - node.parent.type === 'ClassBody' && + node.parent && + node.parent.type === AST_NODE_TYPES.ClassBody && node.parent.parent && + 'superClass' in node.parent.parent && node.parent.parent.superClass ) { return false; @@ -36,39 +35,36 @@ function checkAccessibility(node) { /** * Check if method is not unless due to typescript parameter properties - * @param {MethodDefinition} node - * @returns {boolean} */ -function checkParams(node) { +function checkParams(node: TSESTree.MethodDefinition): boolean { return ( !node.value.params || - !node.value.params.some(param => param.type === 'TSParameterProperty') + !node.value.params.some( + param => param.type === AST_NODE_TYPES.TSParameterProperty + ) ); } -module.exports = Object.assign({}, baseRule, { +export default util.createRule({ + name: 'no-useless-constructor', meta: { type: 'problem', docs: { description: 'Disallow unnecessary constructors', - category: 'ECMAScript 6', - url: util.metaDocsUrl('no-useless-constructor') + category: 'Best Practices', + recommended: false }, schema: baseRule.meta.schema, messages: baseRule.meta.messages }, - + defaultOptions: [], create(context) { const rules = baseRule.create(context); - - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- return { - MethodDefinition(node) { + MethodDefinition(node: TSESTree.MethodDefinition) { if ( node.value && - node.value.type === 'FunctionExpression' && + node.value.type === AST_NODE_TYPES.FunctionExpression && checkAccessibility(node) && checkParams(node) ) { diff --git a/packages/eslint-plugin/src/rules/no-var-requires.ts b/packages/eslint-plugin/src/rules/no-var-requires.ts new file mode 100644 index 00000000000..243bfcd4640 --- /dev/null +++ b/packages/eslint-plugin/src/rules/no-var-requires.ts @@ -0,0 +1,46 @@ +/** + * @fileoverview Disallows the use of require statements except in import statements. + * @author Macklin Underdown + */ + +import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/typescript-estree'; +import * as util from '../util'; + +type Options = []; +type MessageIds = 'noVarReqs'; + +export default util.createRule({ + name: 'no-var-requires', + meta: { + type: 'problem', + docs: { + description: + 'Disallows the use of require statements except in import statements', + tslintRuleName: 'no-var-requires', + category: 'Best Practices', + recommended: 'error' + }, + messages: { + noVarReqs: 'Require statement not part of import statement.' + }, + schema: [] + }, + defaultOptions: [], + create(context) { + return { + CallExpression(node: TSESTree.CallExpression) { + if ( + node.callee.type === AST_NODE_TYPES.Identifier && + node.callee.name === 'require' && + node.parent && + node.parent.type === AST_NODE_TYPES.VariableDeclarator + ) { + context.report({ + node, + messageId: 'noVarReqs' + }); + } + } + }; + } +}); diff --git a/packages/eslint-plugin/src/rules/prefer-function-type.ts b/packages/eslint-plugin/src/rules/prefer-function-type.ts new file mode 100644 index 00000000000..71bd2a88097 --- /dev/null +++ b/packages/eslint-plugin/src/rules/prefer-function-type.ts @@ -0,0 +1,160 @@ +/** + * @fileoverview Use function types instead of interfaces with call signatures + * @author Benjamin Lichtman + */ + +import { + AST_NODE_TYPES, + TSESTree, + AST_TOKEN_TYPES +} from '@typescript-eslint/typescript-estree'; +import * as util from '../util'; + +export default util.createRule({ + name: 'prefer-function-type', + meta: { + docs: { + description: + 'Use function types instead of interfaces with call signatures', + category: 'Best Practices', + recommended: false, + tslintName: 'callable-types' + }, + fixable: 'code', + messages: { + functionTypeOverCallableType: + "{{ type }} has only a call signature - use '{{ sigSuggestion }}' instead." + }, + schema: [], + type: 'suggestion' + }, + defaultOptions: [], + create(context) { + const sourceCode = context.getSourceCode(); + + /** + * Checks if there the interface has exactly one supertype that isn't named 'Function' + * @param node The node being checked + */ + function hasOneSupertype(node: TSESTree.TSInterfaceDeclaration): boolean { + if (!node.extends || node.extends.length === 0) { + return false; + } + if (node.extends.length !== 1) { + return true; + } + const expr = node.extends[0].expression; + + return ( + expr.type !== AST_NODE_TYPES.Identifier || expr.name !== 'Function' + ); + } + + /** + * @param parent The parent of the call signature causing the diagnostic + */ + function shouldWrapSuggestion(parent: TSESTree.Node | undefined): boolean { + if (!parent) { + return false; + } + + switch (parent.type) { + case AST_NODE_TYPES.TSUnionType: + case AST_NODE_TYPES.TSIntersectionType: + case AST_NODE_TYPES.TSArrayType: + return true; + default: + return false; + } + } + + /** + * @param call The call signature causing the diagnostic + * @param parent The parent of the call + * @returns The suggestion to report + */ + function renderSuggestion( + call: + | TSESTree.TSCallSignatureDeclaration + | TSESTree.TSConstructSignatureDeclaration, + parent: TSESTree.Node + ) { + const start = call.range[0]; + const colonPos = call.returnType!.range[0] - start; + const text = sourceCode.getText().slice(start, call.range[1]); + + let suggestion = `${text.slice(0, colonPos)} =>${text.slice( + colonPos + 1 + )}`; + + if (shouldWrapSuggestion(parent.parent)) { + suggestion = `(${suggestion})`; + } + if (parent.type === AST_NODE_TYPES.TSInterfaceDeclaration) { + if (typeof parent.typeParameters !== 'undefined') { + return `type ${sourceCode + .getText() + .slice( + parent.id.range[0], + parent.typeParameters.range[1] + )} = ${suggestion}`; + } + return `type ${parent.id.name} = ${suggestion}`; + } + return suggestion.endsWith(';') ? suggestion.slice(0, -1) : suggestion; + } + + /** + * @param member The TypeElement being checked + * @param node The parent of member being checked + */ + function checkMember(member: TSESTree.TypeElement, node: TSESTree.Node) { + if ( + (member.type === AST_NODE_TYPES.TSCallSignatureDeclaration || + member.type === AST_NODE_TYPES.TSConstructSignatureDeclaration) && + typeof member.returnType !== 'undefined' + ) { + const suggestion = renderSuggestion(member, node); + const fixStart = + node.type === AST_NODE_TYPES.TSTypeLiteral + ? node.range[0] + : sourceCode + .getTokens(node) + .filter( + token => + token.type === AST_TOKEN_TYPES.Keyword && + token.value === 'interface' + )[0].range[0]; + + context.report({ + node: member, + messageId: 'functionTypeOverCallableType', + data: { + type: + node.type === AST_NODE_TYPES.TSTypeLiteral + ? 'Type literal' + : 'Interface', + sigSuggestion: suggestion + }, + fix(fixer) { + return fixer.replaceTextRange( + [fixStart, node.range[1]], + suggestion + ); + } + }); + } + } + + return { + TSInterfaceDeclaration(node: TSESTree.TSInterfaceDeclaration) { + if (!hasOneSupertype(node) && node.body.body.length === 1) { + checkMember(node.body.body[0], node); + } + }, + 'TSTypeLiteral[members.length = 1]'(node: TSESTree.TSTypeLiteral) { + checkMember(node.members[0], node); + } + }; + } +}); diff --git a/packages/eslint-plugin/lib/rules/prefer-interface.js b/packages/eslint-plugin/src/rules/prefer-interface.ts similarity index 59% rename from packages/eslint-plugin/lib/rules/prefer-interface.js rename to packages/eslint-plugin/src/rules/prefer-interface.ts index 674337536f1..b9421e15953 100644 --- a/packages/eslint-plugin/lib/rules/prefer-interface.js +++ b/packages/eslint-plugin/src/rules/prefer-interface.ts @@ -2,23 +2,20 @@ * @fileoverview Prefer an interface declaration over a type literal (type T = { ... }) * @author Armano */ -'use strict'; -const util = require('../util'); +import { TSESTree } from '@typescript-eslint/typescript-estree'; +import { RuleFix } from 'ts-eslint'; +import * as util from '../util'; -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -module.exports = { +export default util.createRule({ + name: 'prefer-interface', meta: { type: 'suggestion', docs: { description: 'Prefer an interface declaration over a type literal (type T = { ... })', - extraDescription: [util.tslintRule('interface-over-type-literal')], - category: 'TypeScript', - url: util.metaDocsUrl('prefer-interface'), + tslintRuleName: 'interface-over-type-literal', + category: 'Stylistic Issues', recommended: 'error' }, fixable: 'code', @@ -27,31 +24,34 @@ module.exports = { }, schema: [] }, + defaultOptions: [], create(context) { const sourceCode = context.getSourceCode(); - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- return { // VariableDeclaration with kind type has only one VariableDeclarator - "TSTypeAliasDeclaration[typeAnnotation.type='TSTypeLiteral']"(node) { + "TSTypeAliasDeclaration[typeAnnotation.type='TSTypeLiteral']"( + node: TSESTree.TSTypeAliasDeclaration + ) { context.report({ node: node.id, messageId: 'interfaceOverType', fix(fixer) { const typeNode = node.typeParameters || node.id; + const fixes: RuleFix[] = []; - const fixes = [ - fixer.replaceText(sourceCode.getFirstToken(node), 'interface'), - fixer.replaceTextRange( - [typeNode.range[1], node.typeAnnotation.range[0]], - ' ' - ) - ]; + const firstToken = sourceCode.getFirstToken(node); + if (firstToken) { + fixes.push(fixer.replaceText(firstToken, 'interface')); + fixes.push( + fixer.replaceTextRange( + [typeNode.range[1], node.typeAnnotation.range[0]], + ' ' + ) + ); + } const afterToken = sourceCode.getTokenAfter(node.typeAnnotation); - if ( afterToken && afterToken.type === 'Punctuator' && @@ -66,4 +66,4 @@ module.exports = { } }; } -}; +}); diff --git a/packages/eslint-plugin/lib/rules/prefer-namespace-keyword.js b/packages/eslint-plugin/src/rules/prefer-namespace-keyword.ts similarity index 55% rename from packages/eslint-plugin/lib/rules/prefer-namespace-keyword.js rename to packages/eslint-plugin/src/rules/prefer-namespace-keyword.ts index 941d4f23092..83b668a1b8d 100644 --- a/packages/eslint-plugin/lib/rules/prefer-namespace-keyword.js +++ b/packages/eslint-plugin/src/rules/prefer-namespace-keyword.ts @@ -3,39 +3,40 @@ * @author Patricio Trevino * @author Armano */ -'use strict'; -const util = require('../util'); +import { + AST_NODE_TYPES, + AST_TOKEN_TYPES, + TSESTree +} from '@typescript-eslint/typescript-estree'; +import * as util from '../util'; -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -module.exports = { +export default util.createRule({ + name: 'prefer-namespace-keyword', meta: { type: 'suggestion', docs: { description: 'Require the use of the `namespace` keyword instead of the `module` keyword to declare custom TypeScript modules.', - extraDescription: [util.tslintRule('no-internal-module')], - category: 'TypeScript', - url: util.metaDocsUrl('prefer-namespace-keyword'), + tslintRuleName: 'no-internal-module', + category: 'Best Practices', recommended: 'error' }, fixable: 'code', + messages: { + useNamespace: + "Use 'namespace' instead of 'module' to declare custom TypeScript modules." + }, schema: [] }, - + defaultOptions: [], create(context) { const sourceCode = context.getSourceCode(); - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- return { - TSModuleDeclaration(node) { + TSModuleDeclaration(node: TSESTree.TSModuleDeclaration) { // Do nothing if the name is a string. - if (!node.id || node.id.type === 'Literal') { + if (!node.id || node.id.type === AST_NODE_TYPES.Literal) { return; } // Get tokens of the declaration header. @@ -43,13 +44,12 @@ module.exports = { if ( moduleType && - moduleType.type === 'Identifier' && + moduleType.type === AST_TOKEN_TYPES.Identifier && moduleType.value === 'module' ) { context.report({ node, - message: - "Use 'namespace' instead of 'module' to declare custom TypeScript modules.", + messageId: 'useNamespace', fix(fixer) { return fixer.replaceText(moduleType, 'namespace'); } @@ -58,4 +58,4 @@ module.exports = { } }; } -}; +}); diff --git a/packages/eslint-plugin/lib/rules/promise-function-async.js b/packages/eslint-plugin/src/rules/promise-function-async.ts similarity index 52% rename from packages/eslint-plugin/lib/rules/promise-function-async.js rename to packages/eslint-plugin/src/rules/promise-function-async.ts index febbbc2a012..e2e65cfd1b6 100644 --- a/packages/eslint-plugin/lib/rules/promise-function-async.js +++ b/packages/eslint-plugin/src/rules/promise-function-async.ts @@ -2,40 +2,36 @@ * @fileoverview Requires any function or method that returns a Promise to be marked async * @author Josh Goldberg */ -'use strict'; -const util = require('../util'); -const types = require('../utils/types'); +import { TSESTree } from '@typescript-eslint/typescript-estree'; +import * as util from '../util'; //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ -const defaultOptions = [ +type Options = [ { - allowedPromiseNames: [], - checkArrowFunctions: true, - checkFunctionDeclarations: true, - checkFunctionExpressions: true, - checkMethodDeclarations: true + allowedPromiseNames?: string[]; + checkArrowFunctions?: boolean; + checkFunctionDeclarations?: boolean; + checkFunctionExpressions?: boolean; + checkMethodDeclarations?: boolean; } ]; +type MessageIds = 'missingAsync'; -/** - * @type {import("eslint").Rule.RuleModule} - */ -module.exports = { +export default util.createRule({ + name: 'promise-function-async', meta: { type: 'suggestion', docs: { description: 'Requires any function or method that returns a Promise to be marked async.', - extraDescription: [util.tslintRule('promise-function-async')], - category: 'TypeScript', - url: util.metaDocsUrl('promise-function-async'), + tslintName: 'promise-function-async', + category: 'Best Practices', recommended: 'error' }, - fixable: null, messages: { missingAsync: 'Functions that return promises must be async.' }, @@ -66,31 +62,42 @@ module.exports = { } ] }, - - create(context) { - const { - allowedPromiseNames, - checkArrowFunctions, - checkFunctionDeclarations, - checkFunctionExpressions, - checkMethodDeclarations - } = util.applyDefault(defaultOptions, context.options)[0]; - - const allAllowedPromiseNames = new Set(['Promise', ...allowedPromiseNames]); + defaultOptions: [ + { + allowedPromiseNames: [], + checkArrowFunctions: true, + checkFunctionDeclarations: true, + checkFunctionExpressions: true, + checkMethodDeclarations: true + } + ], + create( + context, + [ + { + allowedPromiseNames, + checkArrowFunctions, + checkFunctionDeclarations, + checkFunctionExpressions, + checkMethodDeclarations + } + ] + ) { + const allAllowedPromiseNames = new Set([ + 'Promise', + ...allowedPromiseNames! + ]); const parserServices = util.getParserServices(context); const checker = parserServices.program.getTypeChecker(); - /** - * @param {import("estree").Function} node - */ - function validateNode(node) { + function validateNode(node: TSESTree.Node) { const originalNode = parserServices.esTreeNodeToTSNodeMap.get(node); const [callSignature] = checker .getTypeAtLocation(originalNode) .getCallSignatures(); const returnType = checker.getReturnTypeOfSignature(callSignature); - if (!types.containsTypeByName(returnType, allAllowedPromiseNames)) { + if (!util.containsTypeByName(returnType, allAllowedPromiseNames)) { return; } @@ -101,25 +108,31 @@ module.exports = { } return { - ArrowFunctionExpression(node) { - if (checkArrowFunctions && !node.async) { + 'ArrowFunctionExpression[async = false]'( + node: TSESTree.ArrowFunctionExpression + ) { + if (checkArrowFunctions) { validateNode(node); } }, - FunctionDeclaration(node) { - if (checkFunctionDeclarations && !node.async) { + 'FunctionDeclaration[async = false]'(node: TSESTree.FunctionDeclaration) { + if (checkFunctionDeclarations) { validateNode(node); } }, - FunctionExpression(node) { - if (!!node.parent && node.parent.kind === 'method') { - if (checkMethodDeclarations && !node.async) { + 'FunctionExpression[async = false]'(node: TSESTree.FunctionExpression) { + if ( + node.parent && + 'kind' in node.parent && + node.parent.kind === 'method' + ) { + if (checkMethodDeclarations) { validateNode(node.parent); } - } else if (checkFunctionExpressions && !node.async) { + } else if (checkFunctionExpressions) { validateNode(node); } } }; } -}; +}); diff --git a/packages/eslint-plugin/lib/rules/restrict-plus-operands.js b/packages/eslint-plugin/src/rules/restrict-plus-operands.ts similarity index 70% rename from packages/eslint-plugin/lib/rules/restrict-plus-operands.js rename to packages/eslint-plugin/src/rules/restrict-plus-operands.ts index 222f51e5c8a..f0e5696a559 100644 --- a/packages/eslint-plugin/lib/rules/restrict-plus-operands.js +++ b/packages/eslint-plugin/src/rules/restrict-plus-operands.ts @@ -3,23 +3,21 @@ * @author James Henry * @author Armano */ -'use strict'; -const util = require('../util'); +import { TSESTree } from '@typescript-eslint/typescript-estree'; +import ts from 'typescript'; +import * as util from '../util'; -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -module.exports = { +export default util.createRule({ + name: 'restrict-plus-operands', meta: { type: 'problem', docs: { description: 'When adding two variables, operands must both be of type number or of type string.', - extraDescription: [util.tslintRule('restrict-plus-operands')], - category: 'TypeScript', - url: util.metaDocsUrl('restrict-plus-operands') + tslintRuleName: 'restrict-plus-operands', + category: 'Best Practices', + recommended: false }, messages: { notNumbers: @@ -29,18 +27,20 @@ module.exports = { }, schema: [] }, - + defaultOptions: [], create(context) { const service = util.getParserServices(context); const typeChecker = service.program.getTypeChecker(); + type BaseLiteral = 'string' | 'number' | 'invalid'; + /** * Helper function to get base type of node - * @param {ts.Type} type type to be evaluated - * @returns {*} string, number or invalid + * @param type type to be evaluated + * @returns string, number or invalid */ - function getBaseTypeOfLiteralType(type) { + function getBaseTypeOfLiteralType(type: ts.Type): BaseLiteral { if (type.isNumberLiteral()) { return 'number'; } @@ -63,21 +63,17 @@ module.exports = { /** * Helper function to get base type of node - * @param {ASTNode} node the node to be evaluated. - * @returns {*} string, number or invalid + * @param node the node to be evaluated. */ - function getNodeType(node) { + function getNodeType(node: TSESTree.Node): BaseLiteral { const tsNode = service.esTreeNodeToTSNodeMap.get(node); const type = typeChecker.getTypeAtLocation(tsNode); return getBaseTypeOfLiteralType(type); } - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- return { - "BinaryExpression[operator='+']"(node) { + "BinaryExpression[operator='+']"(node: TSESTree.BinaryExpression) { const leftType = getNodeType(node.left); const rightType = getNodeType(node.right); @@ -101,4 +97,4 @@ module.exports = { } }; } -}; +}); diff --git a/packages/eslint-plugin/lib/rules/type-annotation-spacing.js b/packages/eslint-plugin/src/rules/type-annotation-spacing.ts similarity index 70% rename from packages/eslint-plugin/lib/rules/type-annotation-spacing.js rename to packages/eslint-plugin/src/rules/type-annotation-spacing.ts index e20c1b230eb..391f6523095 100644 --- a/packages/eslint-plugin/lib/rules/type-annotation-spacing.js +++ b/packages/eslint-plugin/src/rules/type-annotation-spacing.ts @@ -3,13 +3,31 @@ * @author Nicholas C. Zakas * @author Patricio Trevino */ -'use strict'; -const util = require('../util'); - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ +import * as util from '../util'; +import { TSESTree } from '@typescript-eslint/typescript-estree'; + +type Options = [ + { + before?: boolean; + after?: boolean; + overrides?: { + colon?: { + before?: boolean; + after?: boolean; + }; + arrow?: { + before?: boolean; + after?: boolean; + }; + }; + }? +]; +type MessageIds = + | 'expectedSpaceAfter' + | 'expectedSpaceBefore' + | 'unexpectedSpaceAfter' + | 'unexpectedSpaceBefore'; const definition = { type: 'object', @@ -20,23 +38,23 @@ const definition = { additionalProperties: false }; -const defaultOptions = [ - // technically there is a default, but the overrides mean - // that if we apply them here, it will break the no override case. - {} -]; - -module.exports = { +export default util.createRule({ + name: 'type-annotation-spacing', meta: { type: 'layout', docs: { description: 'Require consistent spacing around type annotations', - extraDescription: [util.tslintRule('typedef-whitespace')], - category: 'TypeScript', - url: util.metaDocsUrl('type-annotation-spacing'), + tslintRuleName: 'typedef-whitespace', + category: 'Stylistic Issues', recommended: 'error' }, fixable: 'whitespace', + messages: { + expectedSpaceAfter: "Expected a space after the '{{type}}'.", + expectedSpaceBefore: "Expected a space before the '{{type}}'.", + unexpectedSpaceAfter: "Unexpected a space after the '{{type}}'.", + unexpectedSpaceBefore: "Unexpected a space before the '{{type}}'." + }, schema: [ { type: 'object', @@ -51,17 +69,21 @@ module.exports = { }, additionalProperties: false } - } + }, + additionalProperties: false } ] }, - - create(context) { + defaultOptions: [ + // technically there is a default, but the overrides mean + // that if we apply them here, it will break the no override case. + {} + ], + create(context, [options]) { const punctuators = [':', '=>']; const sourceCode = context.getSourceCode(); - const options = util.applyDefault(defaultOptions, context.options)[0]; - const overrides = options.overrides || {}; + const overrides = options!.overrides || { colon: {}, arrow: {} }; const colonOptions = Object.assign( {}, @@ -76,22 +98,17 @@ module.exports = { overrides.arrow ); - //---------------------------------------------------------------------- - // Helpers - //---------------------------------------------------------------------- - /** * Checks if there's proper spacing around type annotations (no space * before colon, one space after). - * @param {ASTNode} typeAnnotation The TSTypeAnnotation node. - * @returns {void} - * @private */ - function checkTypeAnnotationSpacing(typeAnnotation) { + function checkTypeAnnotationSpacing( + typeAnnotation: TSESTree.TypeNode + ): void { const nextToken = typeAnnotation; - const punctuatorTokenEnd = sourceCode.getTokenBefore(nextToken); + const punctuatorTokenEnd = sourceCode.getTokenBefore(nextToken)!; let punctuatorTokenStart = punctuatorTokenEnd; - let previousToken = sourceCode.getTokenBefore(punctuatorTokenEnd); + let previousToken = sourceCode.getTokenBefore(punctuatorTokenEnd)!; let type = punctuatorTokenEnd.value; if (punctuators.indexOf(type) === -1) { @@ -105,13 +122,13 @@ module.exports = { // shift the start to the ? type = '?:'; punctuatorTokenStart = previousToken; - previousToken = sourceCode.getTokenBefore(previousToken); + previousToken = sourceCode.getTokenBefore(previousToken)!; // handle the +/- modifiers for optional modification operators if (previousToken.value === '+' || previousToken.value === '-') { type = `${previousToken.value}?:`; punctuatorTokenStart = previousToken; - previousToken = sourceCode.getTokenBefore(previousToken); + previousToken = sourceCode.getTokenBefore(previousToken)!; } } @@ -122,7 +139,7 @@ module.exports = { if (after && nextDelta === 0) { context.report({ node: punctuatorTokenEnd, - message: "Expected a space after the '{{type}}'.", + messageId: 'expectedSpaceAfter', data: { type }, @@ -133,7 +150,7 @@ module.exports = { } else if (!after && nextDelta > 0) { context.report({ node: punctuatorTokenEnd, - message: "Unexpected space after the '{{type}}'.", + messageId: 'unexpectedSpaceAfter', data: { type }, @@ -149,7 +166,7 @@ module.exports = { if (before && previousDelta === 0) { context.report({ node: punctuatorTokenStart, - message: "Expected a space before the '{{type}}'.", + messageId: 'expectedSpaceBefore', data: { type }, @@ -160,7 +177,7 @@ module.exports = { } else if (!before && previousDelta > 0) { context.report({ node: punctuatorTokenStart, - message: "Unexpected space before the '{{type}}'.", + messageId: 'unexpectedSpaceBefore', data: { type }, @@ -174,18 +191,15 @@ module.exports = { } } - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- return { - TSMappedType(node) { + TSMappedType(node: TSESTree.TSMappedType) { if (node.typeAnnotation) { checkTypeAnnotationSpacing(node.typeAnnotation); } }, - TSTypeAnnotation(node) { + TSTypeAnnotation(node: TSESTree.TSTypeAnnotation) { checkTypeAnnotationSpacing(node.typeAnnotation); } }; } -}; +}); diff --git a/packages/eslint-plugin/src/util/applyDefault.ts b/packages/eslint-plugin/src/util/applyDefault.ts new file mode 100644 index 00000000000..37a399ad68e --- /dev/null +++ b/packages/eslint-plugin/src/util/applyDefault.ts @@ -0,0 +1,34 @@ +import { deepMerge, isObjectNotArray } from './deepMerge'; + +/** + * Pure function - doesn't mutate either parameter! + * Uses the default options and overrides with the options provided by the user + * @param defaultOptions the defaults + * @param userOptions the user opts + * @returns the options with defaults + */ +export function applyDefault( + defaultOptions: TDefault, + userOptions: TUser | null +): TDefault { + // clone defaults + const options: TDefault = JSON.parse(JSON.stringify(defaultOptions)); + + if (userOptions === null || userOptions === undefined) { + return options; + } + + options.forEach((opt, i) => { + if (userOptions[i]) { + const userOpt = userOptions[i]; + + if (isObjectNotArray(userOpt) && isObjectNotArray(opt)) { + options[i] = deepMerge(opt, userOpt); + } else { + options[i] = userOpt; + } + } + }); + + return options; +} diff --git a/packages/eslint-plugin/src/util/createRule.ts b/packages/eslint-plugin/src/util/createRule.ts new file mode 100644 index 00000000000..511c51c7e0c --- /dev/null +++ b/packages/eslint-plugin/src/util/createRule.ts @@ -0,0 +1,62 @@ +import RuleModule, { + RuleListener, + RuleMetaData, + RuleMetaDataDocs, + RuleContext +} from 'ts-eslint'; +import { applyDefault } from './applyDefault'; + +// note - cannot migrate this to an import statement because it will make TSC copy the package.json to the dist folder +const version = require('../../package.json').version; + +// Utility type to remove a list of properties from an object +type RemoveProps< + TObj extends Record, + TKeys extends keyof TObj +> = Pick>; + +// we'll automatically add the url + tslint description for people. +type CreateRuleMetaDocs = RemoveProps & { + tslintName?: string; +}; +type CreateRuleMeta = { + docs: CreateRuleMetaDocs; +} & RemoveProps, 'docs'>; + +// This function will get much easier to call when this is merged https://github.com/Microsoft/TypeScript/pull/26349 +// TODO - when the above rule lands; add type checking for the context.report `data` property +export function createRule< + TOptions extends Readonly, + TMessageIds extends string, + TRuleListener extends RuleListener = RuleListener +>({ + name, + meta, + defaultOptions, + create +}: { + name: string; + meta: CreateRuleMeta; + defaultOptions: TOptions; + create: ( + context: RuleContext, + optionsWithDefault: TOptions + ) => TRuleListener; +}): RuleModule { + return { + meta: { + ...meta, + docs: { + ...meta.docs, + url: `https://github.com/typescript-eslint/typescript-eslint/blob/${version}/packages/eslint-plugin/docs/rules/${name}.md`, + extraDescription: meta.docs.tslintName + ? [`\`${meta.docs.tslintName}\` from TSLint`] + : undefined + } + }, + create(context) { + const optionsWithDefault = applyDefault(defaultOptions, context.options); + return create(context, optionsWithDefault); + } + }; +} diff --git a/packages/eslint-plugin/src/util/deepMerge.ts b/packages/eslint-plugin/src/util/deepMerge.ts new file mode 100644 index 00000000000..9839bd82e56 --- /dev/null +++ b/packages/eslint-plugin/src/util/deepMerge.ts @@ -0,0 +1,49 @@ +export type ObjectLike = Record; + +/** + * Check if the variable contains an object stricly rejecting arrays + * @param obj an object + * @returns `true` if obj is an object + */ +export function isObjectNotArray(obj: T | any[]): obj is T { + return typeof obj === 'object' && !Array.isArray(obj); +} + +/** + * Pure function - doesn't mutate either parameter! + * Merges two objects together deeply, overwriting the properties in first with the properties in second + * @param first The first object + * @param second The second object + * @returns a new object + */ +export function deepMerge( + first: ObjectLike = {}, + second: ObjectLike = {} +): T { + // get the unique set of keys across both objects + const keys = new Set(Object.keys(first).concat(Object.keys(second))); + + return Array.from(keys).reduce( + (acc, key) => { + const firstHasKey = key in first; + const secondHasKey = key in second; + + if (firstHasKey && secondHasKey) { + if (isObjectNotArray(first[key]) && isObjectNotArray(second[key])) { + // object type + acc[key] = deepMerge(first[key], second[key]); + } else { + // value type + acc[key] = second[key]; + } + } else if (firstHasKey) { + acc[key] = first[key]; + } else { + acc[key] = second[key]; + } + + return acc; + }, + {} as T + ); +} diff --git a/packages/eslint-plugin/src/util/getParserServices.ts b/packages/eslint-plugin/src/util/getParserServices.ts new file mode 100644 index 00000000000..6d23a5f24f9 --- /dev/null +++ b/packages/eslint-plugin/src/util/getParserServices.ts @@ -0,0 +1,29 @@ +import { ParserServices } from '@typescript-eslint/parser'; +import { RuleContext } from 'ts-eslint'; + +type RequiredParserServices = { + [k in keyof ParserServices]: Exclude +}; + +/** + * Try to retrieve typescript parser service from context + */ +export function getParserServices< + TMessageIds extends string, + TOptions extends any[] +>(context: RuleContext): RequiredParserServices { + if ( + !context.parserServices || + !context.parserServices.program || + !context.parserServices.esTreeNodeToTSNodeMap + ) { + /** + * The user needs to have configured "project" in their parserOptions + * for @typescript-eslint/parser + */ + throw new Error( + 'You have used a rule which requires parserServices to be generated. You must therefore provide a value for the "parserOptions.project" property for @typescript-eslint/parser.' + ); + } + return context.parserServices as RequiredParserServices; +} diff --git a/packages/eslint-plugin/src/util/index.ts b/packages/eslint-plugin/src/util/index.ts new file mode 100644 index 00000000000..56890ae19fc --- /dev/null +++ b/packages/eslint-plugin/src/util/index.ts @@ -0,0 +1,6 @@ +export * from './applyDefault'; +export * from './createRule'; +export * from './deepMerge'; +export * from './getParserServices'; +export * from './misc'; +export * from './types'; diff --git a/packages/eslint-plugin/src/util/misc.ts b/packages/eslint-plugin/src/util/misc.ts new file mode 100644 index 00000000000..483fb6a140e --- /dev/null +++ b/packages/eslint-plugin/src/util/misc.ts @@ -0,0 +1,65 @@ +/** + * @fileoverview Really small utility functions that didn't deserve their own files + */ + +import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/typescript-estree'; +import RuleModule from 'ts-eslint'; + +/** + * Check if the context file name is *.ts or *.tsx + */ +export function isTypeScriptFile(fileName: string) { + return /\.tsx?$/i.test(fileName || ''); +} + +/** + * Check if the context file name is *.d.ts or *.d.tsx + */ +export function isDefinitionFile(fileName: string) { + return /\.d\.tsx?$/i.test(fileName || ''); +} + +/** + * Upper cases the first character or the string + */ +export function upperCaseFirst(str: string) { + return str[0].toUpperCase() + str.slice(1); +} + +type InferOptionsTypeFromRuleNever = T extends RuleModule< + never, + infer TOptions +> + ? TOptions + : unknown; +/** + * Uses type inference to fetch the TOptions type from the given RuleModule + */ +export type InferOptionsTypeFromRule = T extends RuleModule< + any, + infer TOptions +> + ? TOptions + : InferOptionsTypeFromRuleNever; + +/** + * Uses type inference to fetch the TMessageIds type from the given RuleModule + */ +export type InferMessageIdsTypeFromRule = T extends RuleModule< + infer TMessageIds, + any +> + ? TMessageIds + : unknown; + +/** + * Gets a string name representation of the given PropertyName node + */ +export function getNameFromPropertyName( + propertyName: TSESTree.PropertyName +): string { + if (propertyName.type === AST_NODE_TYPES.Identifier) { + return propertyName.name; + } + return `${propertyName.value}`; +} diff --git a/packages/eslint-plugin/lib/utils/types.js b/packages/eslint-plugin/src/util/types.ts similarity index 58% rename from packages/eslint-plugin/lib/utils/types.js rename to packages/eslint-plugin/src/util/types.ts index adfb402b72a..14133054862 100644 --- a/packages/eslint-plugin/lib/utils/types.js +++ b/packages/eslint-plugin/src/util/types.ts @@ -1,14 +1,15 @@ -'use strict'; - -const tsutils = require('tsutils'); -const ts = require('typescript'); +import * as tsutils from 'tsutils'; +import ts from 'typescript'; /** - * @param {string} type Type being checked by name. - * @param {Set} allowedNames Symbol names checking on the type. - * @returns {boolean} Whether the type is, extends, or contains any of the allowed names. + * @param type Type being checked by name. + * @param allowedNames Symbol names checking on the type. + * @returns Whether the type is, extends, or contains any of the allowed names. */ -function containsTypeByName(type, allowedNames) { +export function containsTypeByName( + type: ts.Type, + allowedNames: Set +): boolean { if (tsutils.isTypeFlagSet(type, ts.TypeFlags.Any | ts.TypeFlags.Unknown)) { return true; } @@ -34,5 +35,3 @@ function containsTypeByName(type, allowedNames) { bases.some(t => containsTypeByName(t, allowedNames)) ); } - -exports.containsTypeByName = containsTypeByName; diff --git a/packages/eslint-plugin/tests/RuleTester.ts b/packages/eslint-plugin/tests/RuleTester.ts new file mode 100644 index 00000000000..20896e9d848 --- /dev/null +++ b/packages/eslint-plugin/tests/RuleTester.ts @@ -0,0 +1,68 @@ +import { ParserOptions } from '@typescript-eslint/parser'; +import { AST_NODE_TYPES } from '@typescript-eslint/typescript-estree'; +import { RuleTester as ESLintRuleTester } from 'eslint'; +import * as path from 'path'; +import RuleModule from 'ts-eslint'; + +interface ValidTestCase> { + code: string; + options?: TOptions; + filename?: string; + parserOptions?: ParserOptions; + settings?: Record; + parser?: string; + globals?: Record; +} + +interface InvalidTestCase< + TMessageIds extends string, + TOptions extends Readonly +> extends ValidTestCase { + errors: TestCaseError[]; + output?: string; +} + +interface TestCaseError { + messageId: TMessageIds; + data?: Record; + type?: AST_NODE_TYPES; + line?: number; + column?: number; +} + +interface RunTests< + TMessageIds extends string, + TOptions extends Readonly +> { + // RuleTester.run also accepts strings for valid cases + valid: (ValidTestCase | string)[]; + invalid: InvalidTestCase[]; +} + +declare class RuleTesterTyped { + run>( + name: string, + rule: RuleModule, + tests: RunTests + ): void; +} + +const RuleTester = (ESLintRuleTester as any) as { + new (config?: { + parser: '@typescript-eslint/parser'; + parserOptions?: ParserOptions; + }): RuleTesterTyped; +}; + +function getFixturesRootDir() { + return path.join(process.cwd(), 'tests/fixtures/'); +} + +export { + RuleTester, + RunTests, + TestCaseError, + InvalidTestCase, + ValidTestCase, + getFixturesRootDir +}; diff --git a/packages/eslint-plugin/tests/lib/eslint-rules/arrow-parens.js b/packages/eslint-plugin/tests/eslint-rules/arrow-parens.test.ts similarity index 74% rename from packages/eslint-plugin/tests/lib/eslint-rules/arrow-parens.js rename to packages/eslint-plugin/tests/eslint-rules/arrow-parens.test.ts index e8a77ca2013..5e51e9e3906 100644 --- a/packages/eslint-plugin/tests/lib/eslint-rules/arrow-parens.js +++ b/packages/eslint-plugin/tests/eslint-rules/arrow-parens.test.ts @@ -1,11 +1,5 @@ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('eslint/lib/rules/arrow-parens'), - RuleTester = require('eslint').RuleTester; +import rule from 'eslint/lib/rules/arrow-parens'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' diff --git a/packages/eslint-plugin/tests/lib/eslint-rules/no-dupe-args.js b/packages/eslint-plugin/tests/eslint-rules/no-dupe-args.test.ts similarity index 57% rename from packages/eslint-plugin/tests/lib/eslint-rules/no-dupe-args.js rename to packages/eslint-plugin/tests/eslint-rules/no-dupe-args.test.ts index 65d2c88314e..4d9e8d3c9a6 100644 --- a/packages/eslint-plugin/tests/lib/eslint-rules/no-dupe-args.js +++ b/packages/eslint-plugin/tests/eslint-rules/no-dupe-args.test.ts @@ -1,11 +1,5 @@ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('eslint/lib/rules/no-dupe-args'), - RuleTester = require('eslint').RuleTester; +import rule from 'eslint/lib/rules/no-dupe-args'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parserOptions: { diff --git a/packages/eslint-plugin/tests/lib/eslint-rules/no-implicit-globals.js b/packages/eslint-plugin/tests/eslint-rules/no-implicit-globals.test.ts similarity index 55% rename from packages/eslint-plugin/tests/lib/eslint-rules/no-implicit-globals.js rename to packages/eslint-plugin/tests/eslint-rules/no-implicit-globals.test.ts index 654c3374ca8..b5f1616e265 100644 --- a/packages/eslint-plugin/tests/lib/eslint-rules/no-implicit-globals.js +++ b/packages/eslint-plugin/tests/eslint-rules/no-implicit-globals.test.ts @@ -1,11 +1,5 @@ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('eslint/lib/rules/no-implicit-globals'), - RuleTester = require('eslint').RuleTester; +import rule from 'eslint/lib/rules/no-implicit-globals'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parserOptions: { diff --git a/packages/eslint-plugin/tests/lib/eslint-rules/no-redeclare.js b/packages/eslint-plugin/tests/eslint-rules/no-redeclare.test.ts similarity index 63% rename from packages/eslint-plugin/tests/lib/eslint-rules/no-redeclare.js rename to packages/eslint-plugin/tests/eslint-rules/no-redeclare.test.ts index 05b3efa9bb4..51b56f72f6f 100644 --- a/packages/eslint-plugin/tests/lib/eslint-rules/no-redeclare.js +++ b/packages/eslint-plugin/tests/eslint-rules/no-redeclare.test.ts @@ -1,11 +1,5 @@ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('eslint/lib/rules/no-redeclare'), - RuleTester = require('eslint').RuleTester; +import rule from 'eslint/lib/rules/no-redeclare'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parserOptions: { diff --git a/packages/eslint-plugin/tests/lib/eslint-rules/no-restricted-globals.js b/packages/eslint-plugin/tests/eslint-rules/no-restricted-globals.test.ts similarity index 61% rename from packages/eslint-plugin/tests/lib/eslint-rules/no-restricted-globals.js rename to packages/eslint-plugin/tests/eslint-rules/no-restricted-globals.test.ts index a88cb359a5d..a664a3bce20 100644 --- a/packages/eslint-plugin/tests/lib/eslint-rules/no-restricted-globals.js +++ b/packages/eslint-plugin/tests/eslint-rules/no-restricted-globals.test.ts @@ -1,11 +1,5 @@ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('eslint/lib/rules/no-restricted-globals'), - RuleTester = require('eslint').RuleTester; +import rule from 'eslint/lib/rules/no-restricted-globals'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parserOptions: { diff --git a/packages/eslint-plugin/tests/lib/eslint-rules/no-shadow.js b/packages/eslint-plugin/tests/eslint-rules/no-shadow.test.ts similarity index 63% rename from packages/eslint-plugin/tests/lib/eslint-rules/no-shadow.js rename to packages/eslint-plugin/tests/eslint-rules/no-shadow.test.ts index 68901aed431..78580e7296d 100644 --- a/packages/eslint-plugin/tests/lib/eslint-rules/no-shadow.js +++ b/packages/eslint-plugin/tests/eslint-rules/no-shadow.test.ts @@ -1,11 +1,5 @@ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('eslint/lib/rules/no-shadow'), - RuleTester = require('eslint').RuleTester; +import rule from 'eslint/lib/rules/no-shadow'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parserOptions: { diff --git a/packages/eslint-plugin/tests/lib/eslint-rules/no-undef.js b/packages/eslint-plugin/tests/eslint-rules/no-undef.test.ts similarity index 78% rename from packages/eslint-plugin/tests/lib/eslint-rules/no-undef.js rename to packages/eslint-plugin/tests/eslint-rules/no-undef.test.ts index 53a148eee9d..700cb1522df 100644 --- a/packages/eslint-plugin/tests/lib/eslint-rules/no-undef.js +++ b/packages/eslint-plugin/tests/eslint-rules/no-undef.test.ts @@ -1,15 +1,5 @@ -/** - * @fileoverview Check internal rule - * @author Armano - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('eslint/lib/rules/no-undef'), - RuleTester = require('eslint').RuleTester; +import rule from 'eslint/lib/rules/no-undef'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parserOptions: { diff --git a/packages/eslint-plugin/tests/lib/eslint-rules/no-unused-vars.js b/packages/eslint-plugin/tests/eslint-rules/no-unused-vars.test.ts similarity index 62% rename from packages/eslint-plugin/tests/lib/eslint-rules/no-unused-vars.js rename to packages/eslint-plugin/tests/eslint-rules/no-unused-vars.test.ts index 1e80a0dd650..ffa062ddd4f 100644 --- a/packages/eslint-plugin/tests/lib/eslint-rules/no-unused-vars.js +++ b/packages/eslint-plugin/tests/eslint-rules/no-unused-vars.test.ts @@ -1,11 +1,5 @@ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('eslint/lib/rules/no-unused-vars'), - RuleTester = require('eslint').RuleTester; +import rule from 'eslint/lib/rules/no-unused-vars'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parserOptions: { diff --git a/packages/eslint-plugin/tests/lib/eslint-rules/no-use-before-define.js b/packages/eslint-plugin/tests/eslint-rules/no-use-before-define.test.ts similarity index 65% rename from packages/eslint-plugin/tests/lib/eslint-rules/no-use-before-define.js rename to packages/eslint-plugin/tests/eslint-rules/no-use-before-define.test.ts index 4a16ea4c8a0..8c8e3353382 100644 --- a/packages/eslint-plugin/tests/lib/eslint-rules/no-use-before-define.js +++ b/packages/eslint-plugin/tests/eslint-rules/no-use-before-define.test.ts @@ -1,11 +1,5 @@ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('eslint/lib/rules/no-use-before-define'), - RuleTester = require('eslint').RuleTester; +import rule from 'eslint/lib/rules/no-use-before-define'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parserOptions: { diff --git a/packages/eslint-plugin/tests/lib/eslint-rules/strict.js b/packages/eslint-plugin/tests/eslint-rules/strict.test.ts similarity index 70% rename from packages/eslint-plugin/tests/lib/eslint-rules/strict.js rename to packages/eslint-plugin/tests/eslint-rules/strict.test.ts index 173849371d9..76e3c22e5b7 100644 --- a/packages/eslint-plugin/tests/lib/eslint-rules/strict.js +++ b/packages/eslint-plugin/tests/eslint-rules/strict.test.ts @@ -1,11 +1,5 @@ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('eslint/lib/rules/strict'), - RuleTester = require('eslint').RuleTester; +import rule from 'eslint/lib/rules/strict'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parserOptions: { @@ -44,7 +38,7 @@ window.whatevs = { message: "Use the function form of 'use strict'.", line: 3, column: 9 - } + } as any // the base rule doesn't use messageId ] } ] diff --git a/packages/eslint-plugin/tests/lib/rules/no-array-constructor.js b/packages/eslint-plugin/tests/lib/rules/no-array-constructor.js deleted file mode 100644 index 90a55172253..00000000000 --- a/packages/eslint-plugin/tests/lib/rules/no-array-constructor.js +++ /dev/null @@ -1,93 +0,0 @@ -/** - * @fileoverview disallow generic `Array` constructors - * @author Jed Fox - * @author Matt DuVall - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/no-array-constructor'), - RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const ruleTester = new RuleTester({ - parser: '@typescript-eslint/parser' -}); - -const message = 'The array literal notation [] is preferrable.'; - -ruleTester.run('no-array-constructor', rule, { - valid: [ - 'new Array(x)', - 'Array(x)', - 'new Array(9)', - 'Array(9)', - 'new foo.Array()', - 'foo.Array()', - 'new Array.foo', - 'Array.foo()', - - // TypeScript - 'new Array(1, 2, 3)', - 'new Array()', - 'Array(1, 2, 3)', - 'Array()' - ], - - invalid: [ - { - code: 'new Array()', - output: '[]', - errors: [{ message, type: 'NewExpression' }] - }, - { - code: 'Array()', - output: '[]', - errors: [{ message, type: 'CallExpression' }] - }, - { - code: 'new Array', - output: '[]', - errors: [{ message, type: 'NewExpression' }] - }, - { - code: 'new Array(x, y)', - output: '[x, y]', - errors: [{ message, type: 'NewExpression' }] - }, - { - code: 'Array(x, y)', - output: '[x, y]', - errors: [{ message, type: 'CallExpression' }] - }, - { - code: 'new Array(0, 1, 2)', - output: '[0, 1, 2]', - errors: [{ message, type: 'NewExpression' }] - }, - { - code: 'Array(0, 1, 2)', - output: '[0, 1, 2]', - errors: [{ message, type: 'CallExpression' }] - }, - { - code: `new Array( - 0, - 1, - 2 - )`, - output: `[ - 0, - 1, - 2 - ]`, - errors: [{ message, type: 'NewExpression' }] - } - ] -}); diff --git a/packages/eslint-plugin/tests/lib/rules/no-for-in-array.js b/packages/eslint-plugin/tests/lib/rules/no-for-in-array.js deleted file mode 100644 index 91984453000..00000000000 --- a/packages/eslint-plugin/tests/lib/rules/no-for-in-array.js +++ /dev/null @@ -1,69 +0,0 @@ -/** - * @fileoverview Disallow iterating over an array with a for-in loop - * @author Benjamin Lichtman - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/no-for-in-array'), - RuleTester = require('eslint').RuleTester, - path = require('path'); - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const rootDir = path.join(process.cwd(), 'tests/fixtures/'); -const parserOptions = { - ecmaVersion: 2015, - tsconfigRootDir: rootDir, - project: './tsconfig.json' -}; -const ruleTester = new RuleTester({ - parserOptions, - parser: '@typescript-eslint/parser' -}); - -ruleTester.run('no-for-in-array', rule, { - valid: [ - ` -for (const x of [3, 4, 5]) { - console.log(x); -}`, - ` -for (const x in { a: 1, b: 2, c: 3 }) { - console.log(x); -}` - ], - - invalid: [ - { - code: ` -for (const x in [3, 4, 5]) { - console.log(x); -}`, - errors: [ - { - messageId: 'forInViolation', - type: 'ForInStatement' - } - ] - }, - { - code: ` -const z = [3, 4, 5]; -for (const x in z) { - console.log(x); -}`, - errors: [ - { - messageId: 'forInViolation', - type: 'ForInStatement' - } - ] - } - ] -}); diff --git a/packages/eslint-plugin/tests/lib/rules/no-non-null-assertion.js b/packages/eslint-plugin/tests/lib/rules/no-non-null-assertion.js deleted file mode 100644 index 81b79c6972d..00000000000 --- a/packages/eslint-plugin/tests/lib/rules/no-non-null-assertion.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @fileoverview Disallows non-null assertions using the `!` postfix operator. - * @author Macklin Underdown - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/no-non-null-assertion'), - RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const ruleTester = new RuleTester({ - parser: '@typescript-eslint/parser' -}); - -ruleTester.run('no-non-null-assertion', rule, { - valid: ['const x = { y: 1 }; x.y;'], - invalid: [ - { - code: 'const x = null; x!.y;', - errors: [ - { - message: 'Forbidden non-null assertion.', - line: 1, - column: 17 - } - ] - } - ] -}); diff --git a/packages/eslint-plugin/tests/lib/rules/no-var-requires.js b/packages/eslint-plugin/tests/lib/rules/no-var-requires.js deleted file mode 100644 index 64662f95de2..00000000000 --- a/packages/eslint-plugin/tests/lib/rules/no-var-requires.js +++ /dev/null @@ -1,56 +0,0 @@ -/** - * @fileoverview Disallows the use of require statements except in import statements. - * @author Macklin Underdown - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/no-var-requires'), - RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const ruleTester = new RuleTester({ - parser: '@typescript-eslint/parser' -}); - -ruleTester.run('no-var-requires', rule, { - valid: ["import foo = require('foo')", "require('foo')"], - invalid: [ - { - code: "var foo = require('foo')", - errors: [ - { - message: 'Require statement not part of import statement.', - line: 1, - column: 11 - } - ] - }, - { - code: "const foo = require('foo')", - errors: [ - { - message: 'Require statement not part of import statement.', - line: 1, - column: 13 - } - ] - }, - { - code: "let foo = require('foo')", - errors: [ - { - message: 'Require statement not part of import statement.', - line: 1, - column: 11 - } - ] - } - ] -}); diff --git a/packages/eslint-plugin/tests/lib/rules/prefer-namespace-keyword.js b/packages/eslint-plugin/tests/lib/rules/prefer-namespace-keyword.js deleted file mode 100644 index 6107dc0d2ff..00000000000 --- a/packages/eslint-plugin/tests/lib/rules/prefer-namespace-keyword.js +++ /dev/null @@ -1,83 +0,0 @@ -/** - * @fileoverview Enforces the use of the keyword `namespace` over `module` to declare custom TypeScript modules. - * @author Patricio Trevino - * @author Armano - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/prefer-namespace-keyword'), - RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const ruleTester = new RuleTester({ - parser: '@typescript-eslint/parser' -}); - -ruleTester.run('prefer-namespace-keyword', rule, { - valid: [ - "declare module 'foo'", - "declare module 'foo' { }", - 'namespace foo { }', - 'declare namespace foo { }', - 'declare global { }' - ], - invalid: [ - { - code: 'module foo { }', - output: 'namespace foo { }', - errors: [ - { - message: - "Use 'namespace' instead of 'module' to declare custom TypeScript modules.", - row: 1, - column: 1 - } - ] - }, - { - code: 'declare module foo { }', - output: 'declare namespace foo { }', - errors: [ - { - message: - "Use 'namespace' instead of 'module' to declare custom TypeScript modules.", - row: 1, - column: 1 - } - ] - }, - { - code: ` -declare module foo { - declare module bar { } -} - `, - output: ` -declare namespace foo { - declare namespace bar { } -} - `, - errors: [ - { - message: - "Use 'namespace' instead of 'module' to declare custom TypeScript modules.", - row: 2, - column: 1 - }, - { - message: - "Use 'namespace' instead of 'module' to declare custom TypeScript modules.", - row: 3, - column: 5 - } - ] - } - ] -}); diff --git a/packages/eslint-plugin/tests/lib/rules/adjacent-overload-signatures.js b/packages/eslint-plugin/tests/rules/adjacent-overload-signatures.test.ts similarity index 96% rename from packages/eslint-plugin/tests/lib/rules/adjacent-overload-signatures.js rename to packages/eslint-plugin/tests/rules/adjacent-overload-signatures.test.ts index fe42efd1015..f0bc94f40a2 100644 --- a/packages/eslint-plugin/tests/lib/rules/adjacent-overload-signatures.js +++ b/packages/eslint-plugin/tests/rules/adjacent-overload-signatures.test.ts @@ -1,19 +1,5 @@ -/** - * @fileoverview Enforces member overloads to be consecutive. - * @author Patricio Trevino - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/adjacent-overload-signatures'), - RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +import rule from '../../src/rules/adjacent-overload-signatures'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' @@ -63,15 +49,13 @@ class Foo {} ` export class Foo {} export class Bar {} - -export type FooBar = Foo | Bar; + export type FooBar = Foo | Bar; `, ` export interface Foo {} export class Foo {} export class Bar {} - -export type FooBar = Foo | Bar; + export type FooBar = Foo | Bar; `, ` export function foo(s: string); diff --git a/packages/eslint-plugin/tests/lib/rules/array-type.js b/packages/eslint-plugin/tests/rules/array-type.test.ts similarity index 93% rename from packages/eslint-plugin/tests/lib/rules/array-type.js rename to packages/eslint-plugin/tests/rules/array-type.test.ts index 39716675cf1..042d1613a1e 100644 --- a/packages/eslint-plugin/tests/lib/rules/array-type.js +++ b/packages/eslint-plugin/tests/rules/array-type.test.ts @@ -1,23 +1,8 @@ -/** - * @fileoverview Requires using either `T[]` or `Array` for arrays. - * @author Mackie Underdown - * @author Armano - */ -'use strict'; +import rule from '../../src/rules/array-type'; +import { RuleTester } from '../RuleTester'; +import { Linter } from 'eslint'; -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/array-type'), - eslint = require('eslint'), - assert = require('assert'); - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const ruleTester = new eslint.RuleTester({ +const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' }); @@ -820,11 +805,10 @@ let yyyy: Arr>>> = [[[["2"]]]];`, // https://github.com/eslint/eslint/issues/11187 describe('array-type (nested)', () => { it('should fix correctly', () => { - // eslint-disable-next-line require-jsdoc - function testOutput(option, code, output) { - const linter = new eslint.Linter(); + function testOutput(option: string, code: string, output: string): void { + const linter = new Linter(); - linter.defineRule('array-type', Object.assign({}, rule)); + linter.defineRule('array-type', Object.assign({}, rule) as any); const result = linter.verifyAndFix( code, { @@ -838,7 +822,7 @@ describe('array-type (nested)', () => { } ); - assert.strictEqual(output, result.output); + expect(output).toBe(result.output); } testOutput( @@ -849,23 +833,23 @@ describe('array-type (nested)', () => { testOutput( 'array', ` - class Foo>> extends Bar> implements Baz> { - private s: Array +class Foo>> extends Bar> implements Baz> { + private s: Array - constructor (p: Array) { - return new Array() - } - } - `, + constructor (p: Array) { + return new Array() + } +} + `, ` - class Foo extends Bar implements Baz { - private s: T[] +class Foo extends Bar implements Baz { + private s: T[] - constructor (p: T[]) { - return new Array() - } - } - ` + constructor (p: T[]) { + return new Array() + } +} + ` ); testOutput( 'array-simple', diff --git a/packages/eslint-plugin/tests/lib/rules/ban-types.js b/packages/eslint-plugin/tests/rules/ban-types.test.ts similarity index 74% rename from packages/eslint-plugin/tests/lib/rules/ban-types.js rename to packages/eslint-plugin/tests/rules/ban-types.test.ts index 520640cc206..7f09a379f9e 100644 --- a/packages/eslint-plugin/tests/lib/rules/ban-types.js +++ b/packages/eslint-plugin/tests/rules/ban-types.test.ts @@ -1,25 +1,12 @@ -/** - * @fileoverview Enforces that types will not to be used - * @author Armano - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/ban-types'), - RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +import rule from '../../src/rules/ban-types'; +import { RuleTester } from '../RuleTester'; +import { InferOptionsTypeFromRule } from '../../src/util'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' }); -const options = [ +const options: InferOptionsTypeFromRule = [ { types: { String: { @@ -131,24 +118,22 @@ ruleTester.run('ban-types', rule, { }, { code: ` - class Foo extends Bar implements Baz { - constructor (foo: String | Object) { - } +class Foo extends Bar implements Baz { + constructor (foo: String | Object) {} - exit () : Array { - const foo: String = 1 as String - } - } + exit() : Array { + const foo: String = 1 as String + } +} `, output: ` - class Foo extends Bar implements Baz { - constructor (foo: string | Object) { - } +class Foo extends Bar implements Baz { + constructor (foo: string | Object) {} - exit () : Array { - const foo: string = 1 as string - } - } + exit() : Array { + const foo: string = 1 as string + } +} `, errors: [ { @@ -158,7 +143,7 @@ ruleTester.run('ban-types', rule, { customMessage: ' Use string instead.' }, line: 2, - column: 27 + column: 15 }, { messageId: 'bannedTypeMessage', @@ -167,7 +152,7 @@ ruleTester.run('ban-types', rule, { customMessage: ' Use string instead.' }, line: 2, - column: 47 + column: 35 }, { messageId: 'bannedTypeMessage', @@ -176,7 +161,7 @@ ruleTester.run('ban-types', rule, { customMessage: " Use '{}' instead." }, line: 2, - column: 70 + column: 58 }, { messageId: 'bannedTypeMessage', @@ -185,7 +170,7 @@ ruleTester.run('ban-types', rule, { customMessage: ' Use string instead.' }, line: 3, - column: 35 + column: 21 }, { messageId: 'bannedTypeMessage', @@ -194,13 +179,13 @@ ruleTester.run('ban-types', rule, { customMessage: " Use '{}' instead." }, line: 3, - column: 44 + column: 30 }, { messageId: 'bannedTypeMessage', data: { name: 'Array', customMessage: '' }, - line: 6, - column: 27 + line: 5, + column: 12 }, { messageId: 'bannedTypeMessage', @@ -208,8 +193,8 @@ ruleTester.run('ban-types', rule, { name: 'String', customMessage: ' Use string instead.' }, - line: 6, - column: 33 + line: 5, + column: 18 }, { messageId: 'bannedTypeMessage', @@ -217,8 +202,8 @@ ruleTester.run('ban-types', rule, { name: 'String', customMessage: ' Use string instead.' }, - line: 7, - column: 32 + line: 6, + column: 16 }, { messageId: 'bannedTypeMessage', @@ -226,8 +211,8 @@ ruleTester.run('ban-types', rule, { name: 'String', customMessage: ' Use string instead.' }, - line: 7, - column: 46 + line: 6, + column: 30 } ], options diff --git a/packages/eslint-plugin/tests/lib/rules/camelcase.js b/packages/eslint-plugin/tests/rules/camelcase.test.ts similarity index 76% rename from packages/eslint-plugin/tests/lib/rules/camelcase.js rename to packages/eslint-plugin/tests/rules/camelcase.test.ts index 05efff9af56..64da778f707 100644 --- a/packages/eslint-plugin/tests/lib/rules/camelcase.js +++ b/packages/eslint-plugin/tests/rules/camelcase.test.ts @@ -1,27 +1,11 @@ -/** - * @fileoverview Tests for camelcase rule - * @author Guy Lilian - * @author Shahar Or - * @author Patricio Trevino - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const ruleCamelcase = require('../../../lib/rules/camelcase'); -const RuleTester = require('eslint').RuleTester; +import rule from '../../src/rules/camelcase'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' }); -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -ruleTester.run('camelcase', ruleCamelcase, { +ruleTester.run('camelcase', rule, { valid: [ { code: 'interface Foo { b_ar: number }', @@ -103,7 +87,10 @@ ruleTester.run('camelcase', ruleCamelcase, { options: [{ properties: 'always' }], errors: [ { - message: "Identifier 'b_ar' is not in camel case.", + messageId: 'notCamelCase', + data: { + name: 'b_ar' + }, line: 1, column: 17 } @@ -114,7 +101,10 @@ ruleTester.run('camelcase', ruleCamelcase, { options: [{ properties: 'always' }], errors: [ { - message: "Identifier 'b_ar' is not in camel case.", + messageId: 'notCamelCase', + data: { + name: 'b_ar' + }, line: 1, column: 13 } @@ -125,7 +115,10 @@ ruleTester.run('camelcase', ruleCamelcase, { options: [{ properties: 'always' }], errors: [ { - message: "Identifier 'b_ar' is not in camel case.", + messageId: 'notCamelCase', + data: { + name: 'b_ar' + }, line: 1, column: 33 } @@ -136,7 +129,10 @@ ruleTester.run('camelcase', ruleCamelcase, { options: [{ properties: 'always' }], errors: [ { - message: "Identifier 'b_ar' is not in camel case.", + messageId: 'notCamelCase', + data: { + name: 'b_ar' + }, line: 1, column: 33 } @@ -147,7 +143,10 @@ ruleTester.run('camelcase', ruleCamelcase, { options: [{ properties: 'always' }], errors: [ { - message: "Identifier 'b_ar' is not in camel case.", + messageId: 'notCamelCase', + data: { + name: 'b_ar' + }, line: 1, column: 22 } @@ -158,7 +157,10 @@ ruleTester.run('camelcase', ruleCamelcase, { options: [{ properties: 'always' }], errors: [ { - message: "Identifier 'b_ar' is not in camel case.", + messageId: 'notCamelCase', + data: { + name: 'b_ar' + }, line: 1, column: 22 } @@ -169,7 +171,10 @@ ruleTester.run('camelcase', ruleCamelcase, { options: [{ properties: 'always' }], errors: [ { - message: "Identifier 'b_ar' is not in camel case.", + messageId: 'notCamelCase', + data: { + name: 'b_ar' + }, line: 1, column: 31 } @@ -180,7 +185,10 @@ ruleTester.run('camelcase', ruleCamelcase, { options: [{ properties: 'always' }], errors: [ { - message: "Identifier 'b_ar' is not in camel case.", + messageId: 'notCamelCase', + data: { + name: 'b_ar' + }, line: 1, column: 31 } diff --git a/packages/eslint-plugin/tests/lib/rules/class-name-casing.js b/packages/eslint-plugin/tests/rules/class-name-casing.test.ts similarity index 56% rename from packages/eslint-plugin/tests/lib/rules/class-name-casing.js rename to packages/eslint-plugin/tests/rules/class-name-casing.test.ts index 59c2217cead..91713477338 100644 --- a/packages/eslint-plugin/tests/lib/rules/class-name-casing.js +++ b/packages/eslint-plugin/tests/rules/class-name-casing.test.ts @@ -1,20 +1,5 @@ -/** - * @fileoverview Enforces PascalCased class and interface names. - * @author Jed Fox - * @author Armano - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/class-name-casing'), - RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +import rule from '../../src/rules/class-name-casing'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' @@ -41,7 +26,11 @@ ruleTester.run('class-name-casing', rule, { code: 'class invalidClassName {}', errors: [ { - message: "Class 'invalidClassName' must be PascalCased.", + messageId: 'notPascalCased', + data: { + friendlyName: 'Class', + name: 'invalidClassName' + }, line: 1, column: 7 } @@ -51,7 +40,11 @@ ruleTester.run('class-name-casing', rule, { code: 'class Another_Invalid_Class_Name {}', errors: [ { - message: "Class 'Another_Invalid_Class_Name' must be PascalCased.", + messageId: 'notPascalCased', + data: { + friendlyName: 'Class', + name: 'Another_Invalid_Class_Name' + }, line: 1, column: 7 } @@ -61,7 +54,11 @@ ruleTester.run('class-name-casing', rule, { code: 'var foo = class {};', errors: [ { - message: "Class 'foo' must be PascalCased.", + messageId: 'notPascalCased', + data: { + friendlyName: 'Class', + name: 'foo' + }, line: 1, column: 5 } @@ -71,7 +68,11 @@ ruleTester.run('class-name-casing', rule, { code: 'const foo = class {};', errors: [ { - message: "Class 'foo' must be PascalCased.", + messageId: 'notPascalCased', + data: { + friendlyName: 'Class', + name: 'foo' + }, line: 1, column: 7 } @@ -81,7 +82,11 @@ ruleTester.run('class-name-casing', rule, { code: 'var bar = class invalidName {}', errors: [ { - message: "Class 'invalidName' must be PascalCased.", + messageId: 'notPascalCased', + data: { + friendlyName: 'Class', + name: 'invalidName' + }, line: 1, column: 17 } @@ -91,7 +96,11 @@ ruleTester.run('class-name-casing', rule, { code: 'interface someInterface {}', errors: [ { - message: "Interface 'someInterface' must be PascalCased.", + messageId: 'notPascalCased', + data: { + friendlyName: 'Interface', + name: 'someInterface' + }, line: 1, column: 11 } @@ -101,7 +110,11 @@ ruleTester.run('class-name-casing', rule, { code: 'abstract class invalidClassName {}', errors: [ { - message: "Abstract class 'invalidClassName' must be PascalCased.", + messageId: 'notPascalCased', + data: { + friendlyName: 'Abstract class', + name: 'invalidClassName' + }, line: 1, column: 16 } @@ -111,7 +124,11 @@ ruleTester.run('class-name-casing', rule, { code: 'declare class invalidClassName {}', errors: [ { - message: "Class 'invalidClassName' must be PascalCased.", + messageId: 'notPascalCased', + data: { + friendlyName: 'Class', + name: 'invalidClassName' + }, line: 1, column: 15 } diff --git a/packages/eslint-plugin/tests/lib/rules/explicit-function-return-type.js b/packages/eslint-plugin/tests/rules/explicit-function-return-type.test.ts similarity index 73% rename from packages/eslint-plugin/tests/lib/rules/explicit-function-return-type.js rename to packages/eslint-plugin/tests/rules/explicit-function-return-type.test.ts index e5454b6ccc7..3348942e4d0 100644 --- a/packages/eslint-plugin/tests/lib/rules/explicit-function-return-type.js +++ b/packages/eslint-plugin/tests/rules/explicit-function-return-type.test.ts @@ -1,19 +1,5 @@ -/** - * @fileoverview Enforces explicit return type for functions - * @author Scott O'Hara - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/explicit-function-return-type'), - RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +import rule from '../../src/rules/explicit-function-return-type'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' @@ -117,7 +103,7 @@ function test() { `, errors: [ { - message: 'Missing return type on function.', + messageId: 'missingReturnType', line: 2, column: 1 } @@ -132,7 +118,7 @@ var fn = function() { `, errors: [ { - message: 'Missing return type on function.', + messageId: 'missingReturnType', line: 2, column: 10 } @@ -145,7 +131,7 @@ var arrowFn = () => 'test'; `, errors: [ { - message: 'Missing return type on function.', + messageId: 'missingReturnType', line: 2, column: 15 } @@ -167,12 +153,12 @@ class Test { `, errors: [ { - message: 'Missing return type on function.', + messageId: 'missingReturnType', line: 4, column: 11 }, { - message: 'Missing return type on function.', + messageId: 'missingReturnType', line: 8, column: 9 } @@ -184,7 +170,7 @@ class Test { options: [{ allowExpressions: true }], errors: [ { - message: 'Missing return type on function.', + messageId: 'missingReturnType', line: 1, column: 13 } @@ -196,7 +182,7 @@ class Test { options: [{ allowExpressions: true }], errors: [ { - message: 'Missing return type on function.', + messageId: 'missingReturnType', line: 1, column: 13 } diff --git a/packages/eslint-plugin/tests/lib/rules/explicit-member-accessibility.js b/packages/eslint-plugin/tests/rules/explicit-member-accessibility.test.ts similarity index 59% rename from packages/eslint-plugin/tests/lib/rules/explicit-member-accessibility.js rename to packages/eslint-plugin/tests/rules/explicit-member-accessibility.test.ts index 0ec5f048e1f..126a965ee54 100644 --- a/packages/eslint-plugin/tests/lib/rules/explicit-member-accessibility.js +++ b/packages/eslint-plugin/tests/rules/explicit-member-accessibility.test.ts @@ -1,19 +1,5 @@ -/** - * @fileoverview Enforces explicit accessibility modifiers for class members - * @author Danny Fritz - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/explicit-member-accessibility'), - RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +import rule from '../../src/rules/explicit-member-accessibility'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' @@ -67,7 +53,11 @@ class Test { `, errors: [ { - message: 'Missing accessibility modifier on class property x.', + messageId: 'missingAccessibility', + data: { + type: 'class property', + name: 'x' + }, line: 3, column: 3 } @@ -85,7 +75,11 @@ class Test { `, errors: [ { - message: 'Missing accessibility modifier on method definition getX.', + messageId: 'missingAccessibility', + data: { + type: 'method definition', + name: 'getX' + }, line: 4, column: 3 } @@ -103,12 +97,20 @@ class Test { `, errors: [ { - message: 'Missing accessibility modifier on class property x.', + messageId: 'missingAccessibility', + data: { + type: 'class property', + name: 'x' + }, line: 3, column: 3 }, { - message: 'Missing accessibility modifier on method definition getX.', + messageId: 'missingAccessibility', + data: { + type: 'method definition', + name: 'getX' + }, line: 4, column: 3 } diff --git a/packages/eslint-plugin/tests/lib/rules/generic-type-naming.js b/packages/eslint-plugin/tests/rules/generic-type-naming.test.ts similarity index 88% rename from packages/eslint-plugin/tests/lib/rules/generic-type-naming.js rename to packages/eslint-plugin/tests/rules/generic-type-naming.test.ts index 98b0532ec6c..125d22b56ff 100644 --- a/packages/eslint-plugin/tests/lib/rules/generic-type-naming.js +++ b/packages/eslint-plugin/tests/rules/generic-type-naming.test.ts @@ -1,18 +1,5 @@ -/** - * @fileoverview Enforces naming of generic type variables. - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/generic-type-naming'), - RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +import rule from '../../src/rules/generic-type-naming'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' diff --git a/packages/eslint-plugin/tests/lib/rules/indent.js b/packages/eslint-plugin/tests/rules/indent.test.ts similarity index 65% rename from packages/eslint-plugin/tests/lib/rules/indent.js rename to packages/eslint-plugin/tests/rules/indent.test.ts index c4e069e31f5..10299325659 100644 --- a/packages/eslint-plugin/tests/lib/rules/indent.js +++ b/packages/eslint-plugin/tests/rules/indent.test.ts @@ -1,22 +1,17 @@ -/** - * @fileoverview Check internal rule - * @author Armano - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ +import rule from '../../src/rules/indent'; +import { RuleTester, RunTests, TestCaseError } from '../RuleTester'; +import { + InferMessageIdsTypeFromRule, + InferOptionsTypeFromRule +} from '../../src/util'; -const rule = require('../../../lib/rules/indent'), - RuleTester = require('eslint').RuleTester; +type MessageIds = InferMessageIdsTypeFromRule; +type Options = InferOptionsTypeFromRule; /** * Marks a test case as a plain javascript case which should be indented the same - * @param {string} example the code - * @returns {string} marked code */ -function nonTsTestCase(example) { +function nonTsTestCase(example: TemplateStringsArray): string { return [`// Non-TS Test Case`, example].join('\n'); } @@ -613,7 +608,7 @@ type Foo = string | { ` ] } -].reduce( +].reduce>( (acc, testCase) => { const indent = ' '; @@ -635,7 +630,7 @@ type Foo = string | { output: code, errors: code .split('\n') - .map((line, lineNum) => { + .map | null>((line, lineNum) => { const indentCount = line.split(indent).length - 1; const spaceCount = indentCount * indent.length; @@ -644,12 +639,16 @@ type Foo = string | { } return { - message: `Expected indentation of ${spaceCount} spaces but found 0.`, + messageId: 'wrongIndentation', + data: { + expected: `${spaceCount} spaces`, + actual: 0 + }, line: lineNum + 1, column: 1 }; }) - .filter(error => error !== null) + .filter((error): error is TestCaseError => error !== null) }); }); @@ -759,12 +758,20 @@ type Foo = { `, errors: [ { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 3, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 4, column: 1 } @@ -801,52 +808,92 @@ interface Foo { `, errors: [ { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 3, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 4, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 5, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 6, column: 1 }, { - message: `Expected indentation of 8 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '8 spaces', + actual: 0 + }, line: 7, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 8, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 9, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 10, column: 1 }, { - message: `Expected indentation of 8 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '8 spaces', + actual: 0 + }, line: 11, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 12, column: 1 } @@ -871,22 +918,38 @@ interface Foo { `, errors: [ { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 3, column: 1 }, { - message: `Expected indentation of 8 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '8 spaces', + actual: 0 + }, line: 4, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 5, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 6, column: 1 } @@ -907,12 +970,20 @@ interface Foo extends Bar { `, errors: [ { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 3, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 4, column: 1 } @@ -937,17 +1008,29 @@ class Foo `, errors: [ { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 3, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 4, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 5, column: 1 } @@ -970,17 +1053,29 @@ interface Foo `, errors: [ { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 3, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 4, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 5, column: 1 } @@ -1001,12 +1096,20 @@ const foo : Foo<{ `, errors: [ { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 3, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 4, column: 1 } @@ -1033,22 +1136,38 @@ type T = { `, errors: [ { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 3, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 4, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 6, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 7, column: 1 } @@ -1079,32 +1198,56 @@ type T = `, errors: [ { - message: `Expected indentation of 8 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '8 spaces', + actual: 0 + }, line: 4, column: 1 }, { - message: `Expected indentation of 8 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '8 spaces', + actual: 0 + }, line: 5, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 6, column: 1 }, { - message: `Expected indentation of 8 spaces but found 4.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '8 spaces', + actual: 4 + }, line: 8, column: 1 }, { - message: `Expected indentation of 8 spaces but found 4.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '8 spaces', + actual: 4 + }, line: 9, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 10, column: 1 } @@ -1119,7 +1262,11 @@ import Dialogs = require("widgets/Dialogs"); `, errors: [ { - message: `Expected indentation of 0 spaces but found 4.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '0 spaces', + actual: 4 + }, line: 2, column: 1 } @@ -1160,62 +1307,110 @@ class Foo { `, errors: [ { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 3, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 4, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 5, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 6, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 7, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 8, column: 1 }, { - message: `Expected indentation of 8 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '8 spaces', + actual: 0 + }, line: 9, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 10, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 11, column: 1 }, { - message: `Expected indentation of 8 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '8 spaces', + actual: 0 + }, line: 12, column: 1 }, { - message: `Expected indentation of 8 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '8 spaces', + actual: 0 + }, line: 13, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 14, column: 1 } @@ -1232,12 +1427,20 @@ class Foo {} `, errors: [ { - message: `Expected indentation of 0 spaces but found 4.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '0 spaces', + actual: 4 + }, line: 2, column: 1 }, { - message: `Expected indentation of 0 spaces but found 4.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '0 spaces', + actual: 4 + }, line: 3, column: 1 } @@ -1260,17 +1463,29 @@ enum Foo { `, errors: [ { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 3, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 4, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 5, column: 1 } @@ -1293,17 +1508,29 @@ const enum Foo { `, errors: [ { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 3, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 4, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 5, column: 1 } @@ -1318,7 +1545,11 @@ export = Foo; `, errors: [ { - message: `Expected indentation of 0 spaces but found 4.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '0 spaces', + actual: 4 + }, line: 2, column: 1 } @@ -1333,7 +1564,11 @@ declare function h(x: number): number; `, errors: [ { - message: `Expected indentation of 0 spaces but found 4.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '0 spaces', + actual: 4 + }, line: 2, column: 1 } @@ -1352,7 +1587,11 @@ declare function h( `, errors: [ { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 3, column: 1 } @@ -1375,17 +1614,29 @@ namespace Validation { `, errors: [ { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 3, column: 1 }, { - message: `Expected indentation of 8 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '8 spaces', + actual: 0 + }, line: 4, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 5, column: 1 } @@ -1408,17 +1659,29 @@ declare module "Validation" { `, errors: [ { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 3, column: 1 }, { - message: `Expected indentation of 8 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '8 spaces', + actual: 0 + }, line: 4, column: 1 }, { - message: `Expected indentation of 4 spaces but found 0.`, + messageId: 'wrongIndentation' as 'wrongIndentation', + data: { + expected: '4 spaces', + actual: 0 + }, line: 5, column: 1 } diff --git a/packages/eslint-plugin/tests/lib/rules/interface-name-prefix.js b/packages/eslint-plugin/tests/rules/interface-name-prefix.test.ts similarity index 65% rename from packages/eslint-plugin/tests/lib/rules/interface-name-prefix.js rename to packages/eslint-plugin/tests/rules/interface-name-prefix.test.ts index 6243d5a6d11..919376676b7 100644 --- a/packages/eslint-plugin/tests/lib/rules/interface-name-prefix.js +++ b/packages/eslint-plugin/tests/rules/interface-name-prefix.test.ts @@ -1,19 +1,5 @@ -/** - * @fileoverview Enforces interface names are prefixed with "I" - * @author Danny Fritz - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/interface-name-prefix'), - RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +import rule from '../../src/rules/interface-name-prefix'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' @@ -76,7 +62,7 @@ interface IAnimal { `, errors: [ { - message: 'Interface name must not be prefixed with "I".', + messageId: 'noPrefix', line: 2, column: 11 } @@ -91,7 +77,7 @@ interface Animal { options: ['always'], errors: [ { - message: 'Interface name must be prefixed with "I".', + messageId: 'noPrefix', line: 2, column: 11 } @@ -106,7 +92,7 @@ interface Iguana { options: ['always'], errors: [ { - message: 'Interface name must be prefixed with "I".', + messageId: 'noPrefix', line: 2, column: 11 } @@ -121,7 +107,7 @@ interface IIguana { options: ['never'], errors: [ { - message: 'Interface name must not be prefixed with "I".', + messageId: 'noPrefix', line: 2, column: 11 } @@ -136,7 +122,7 @@ interface IAnimal { options: ['never'], errors: [ { - message: 'Interface name must not be prefixed with "I".', + messageId: 'noPrefix', line: 2, column: 11 } diff --git a/packages/eslint-plugin/tests/lib/rules/member-delimiter-style.js b/packages/eslint-plugin/tests/rules/member-delimiter-style.test.ts similarity index 98% rename from packages/eslint-plugin/tests/lib/rules/member-delimiter-style.js rename to packages/eslint-plugin/tests/rules/member-delimiter-style.test.ts index 288269243af..e2e8562a430 100644 --- a/packages/eslint-plugin/tests/lib/rules/member-delimiter-style.js +++ b/packages/eslint-plugin/tests/rules/member-delimiter-style.test.ts @@ -1,20 +1,5 @@ -/** - * @fileoverview Enforces a member delimiter style in interfaces and type literals. - * @author Patricio Trevino - * @author Brad Zacher - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/member-delimiter-style'), - RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +import rule from '../../src/rules/member-delimiter-style'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' diff --git a/packages/eslint-plugin/tests/lib/rules/member-naming.js b/packages/eslint-plugin/tests/rules/member-naming.test.ts similarity index 58% rename from packages/eslint-plugin/tests/lib/rules/member-naming.js rename to packages/eslint-plugin/tests/rules/member-naming.test.ts index c3e0d8a6920..95cf21f3c62 100644 --- a/packages/eslint-plugin/tests/lib/rules/member-naming.js +++ b/packages/eslint-plugin/tests/rules/member-naming.test.ts @@ -1,19 +1,5 @@ -/** - * @fileoverview Enforces naming conventions for class members by visibility. - * @author Ian MacLeod - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/member-naming'), - RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +import rule from '../../src/rules/member-naming'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' @@ -99,7 +85,12 @@ class Class { options: [{ public: '^_' }], errors: [ { - message: 'public property fooBar should match /^_/.', + messageId: 'incorrectName', + data: { + accessibility: 'public', + convention: '/^_/', + name: 'fooBar' + }, line: 1, column: 15 } @@ -110,7 +101,12 @@ class Class { options: [{ public: '^_' }], errors: [ { - message: 'public property fooBar should match /^_/.', + messageId: 'incorrectName', + data: { + accessibility: 'public', + convention: '/^_/', + name: 'fooBar' + }, line: 1, column: 22 } @@ -121,7 +117,12 @@ class Class { options: [{ protected: '^_' }], errors: [ { - message: 'protected property fooBar should match /^_/.', + messageId: 'incorrectName', + data: { + accessibility: 'protected', + convention: '/^_/', + name: 'fooBar' + }, line: 1, column: 25 } @@ -132,7 +133,12 @@ class Class { options: [{ private: '^_' }], errors: [ { - message: 'private property fooBar should match /^_/.', + messageId: 'incorrectName', + data: { + accessibility: 'private', + convention: '/^_/', + name: 'fooBar' + }, line: 1, column: 23 } @@ -156,22 +162,42 @@ class Class { ], errors: [ { - message: 'public property one should match /^pub[A-Z]/.', + messageId: 'incorrectName', + data: { + accessibility: 'public', + convention: '/^pub[A-Z]/', + name: 'one' + }, line: 3, column: 5 }, { - message: 'public property two should match /^pub[A-Z]/.', + messageId: 'incorrectName', + data: { + accessibility: 'public', + convention: '/^pub[A-Z]/', + name: 'two' + }, line: 4, column: 12 }, { - message: 'protected property three should match /^prot[A-Z]/.', + messageId: 'incorrectName', + data: { + accessibility: 'protected', + convention: '/^prot[A-Z]/', + name: 'three' + }, line: 5, column: 15 }, { - message: 'private property four should match /^priv[A-Z]/.', + messageId: 'incorrectName', + data: { + accessibility: 'private', + convention: '/^priv[A-Z]/', + name: 'four' + }, line: 6, column: 13 } @@ -195,22 +221,42 @@ class Class { ], errors: [ { - message: 'public property one should match /^pub[A-Z]/.', + messageId: 'incorrectName', + data: { + accessibility: 'public', + convention: '/^pub[A-Z]/', + name: 'one' + }, line: 3, column: 5 }, { - message: 'public property two should match /^pub[A-Z]/.', + messageId: 'incorrectName', + data: { + accessibility: 'public', + convention: '/^pub[A-Z]/', + name: 'two' + }, line: 4, column: 12 }, { - message: 'protected property three should match /^prot[A-Z]/.', + messageId: 'incorrectName', + data: { + accessibility: 'protected', + convention: '/^prot[A-Z]/', + name: 'three' + }, line: 5, column: 15 }, { - message: 'private property four should match /^priv[A-Z]/.', + messageId: 'incorrectName', + data: { + accessibility: 'private', + convention: '/^priv[A-Z]/', + name: 'four' + }, line: 6, column: 13 } @@ -234,22 +280,42 @@ class Class { ], errors: [ { - message: 'public property one should match /^pub[A-Z]/.', + messageId: 'incorrectName', + data: { + accessibility: 'public', + convention: '/^pub[A-Z]/', + name: 'one' + }, line: 3, column: 5 }, { - message: 'public property two should match /^pub[A-Z]/.', + messageId: 'incorrectName', + data: { + accessibility: 'public', + convention: '/^pub[A-Z]/', + name: 'two' + }, line: 4, column: 12 }, { - message: 'protected property three should match /^prot[A-Z]/.', + messageId: 'incorrectName', + data: { + accessibility: 'protected', + convention: '/^prot[A-Z]/', + name: 'three' + }, line: 5, column: 15 }, { - message: 'private property four should match /^priv[A-Z]/.', + messageId: 'incorrectName', + data: { + accessibility: 'private', + convention: '/^priv[A-Z]/', + name: 'four' + }, line: 6, column: 13 } diff --git a/packages/eslint-plugin/tests/lib/rules/member-ordering.js b/packages/eslint-plugin/tests/rules/member-ordering.test.ts similarity index 73% rename from packages/eslint-plugin/tests/lib/rules/member-ordering.js rename to packages/eslint-plugin/tests/rules/member-ordering.test.ts index b74bcce3200..29fb131d2a9 100644 --- a/packages/eslint-plugin/tests/lib/rules/member-ordering.js +++ b/packages/eslint-plugin/tests/rules/member-ordering.test.ts @@ -1,19 +1,5 @@ -/** - * @fileoverview Enforces a standard member declaration order. - * @author Patricio Trevino - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/member-ordering'), - RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +import rule from '../../src/rules/member-ordering'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' @@ -1245,8 +1231,11 @@ interface Foo { `, errors: [ { - message: - 'Member new should be declared before all method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'new', + rank: 'method' + }, line: 16, column: 5 } @@ -1274,38 +1263,65 @@ interface Foo { options: [{ default: ['method', 'constructor', 'field'] }], errors: [ { - message: 'Member G should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'G', + rank: 'field' + }, line: 10, column: 5 }, { - message: 'Member H should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'H', + rank: 'field' + }, line: 11, column: 5 }, { - message: 'Member I should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'I', + rank: 'field' + }, line: 12, column: 5 }, { - message: 'Member J should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'J', + rank: 'field' + }, line: 13, column: 5 }, { - message: 'Member K should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'K', + rank: 'field' + }, line: 14, column: 5 }, { - message: 'Member L should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'L', + rank: 'field' + }, line: 15, column: 5 }, { - message: - 'Member new should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'new', + rank: 'field' + }, line: 16, column: 5 } @@ -1333,38 +1349,65 @@ interface Foo { options: [{ interfaces: ['method', 'constructor', 'field'] }], errors: [ { - message: 'Member G should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'G', + rank: 'field' + }, line: 10, column: 5 }, { - message: 'Member H should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'H', + rank: 'field' + }, line: 11, column: 5 }, { - message: 'Member I should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'I', + rank: 'field' + }, line: 12, column: 5 }, { - message: 'Member J should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'J', + rank: 'field' + }, line: 13, column: 5 }, { - message: 'Member K should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'K', + rank: 'field' + }, line: 14, column: 5 }, { - message: 'Member L should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'L', + rank: 'field' + }, line: 15, column: 5 }, { - message: - 'Member new should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'new', + rank: 'field' + }, line: 16, column: 5 } @@ -1397,38 +1440,65 @@ interface Foo { ], errors: [ { - message: 'Member G should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'G', + rank: 'field' + }, line: 10, column: 5 }, { - message: 'Member H should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'H', + rank: 'field' + }, line: 11, column: 5 }, { - message: 'Member I should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'I', + rank: 'field' + }, line: 12, column: 5 }, { - message: 'Member J should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'J', + rank: 'field' + }, line: 13, column: 5 }, { - message: 'Member K should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'K', + rank: 'field' + }, line: 14, column: 5 }, { - message: 'Member L should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'L', + rank: 'field' + }, line: 15, column: 5 }, { - message: - 'Member new should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'new', + rank: 'field' + }, line: 16, column: 5 } @@ -1460,27 +1530,47 @@ interface Foo { ], errors: [ { - message: 'Member B should be declared before all method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'B', + rank: 'method' + }, line: 7, column: 5 }, { - message: 'Member C should be declared before all method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'C', + rank: 'method' + }, line: 9, column: 5 }, { - message: 'Member D should be declared before all method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'D', + rank: 'method' + }, line: 11, column: 5 }, { - message: 'Member E should be declared before all method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'E', + rank: 'method' + }, line: 13, column: 5 }, { - message: 'Member F should be declared before all method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'F', + rank: 'method' + }, line: 15, column: 5 } @@ -1507,8 +1597,11 @@ type Foo = { `, errors: [ { - message: - 'Member new should be declared before all method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'new', + rank: 'method' + }, line: 16, column: 5 } @@ -1536,38 +1629,65 @@ type Foo = { options: [{ default: ['method', 'constructor', 'field'] }], errors: [ { - message: 'Member G should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'G', + rank: 'field' + }, line: 10, column: 5 }, { - message: 'Member H should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'H', + rank: 'field' + }, line: 11, column: 5 }, { - message: 'Member I should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'I', + rank: 'field' + }, line: 12, column: 5 }, { - message: 'Member J should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'J', + rank: 'field' + }, line: 13, column: 5 }, { - message: 'Member K should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'K', + rank: 'field' + }, line: 14, column: 5 }, { - message: 'Member L should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'L', + rank: 'field' + }, line: 15, column: 5 }, { - message: - 'Member new should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'new', + rank: 'field' + }, line: 16, column: 5 } @@ -1595,38 +1715,65 @@ type Foo = { options: [{ typeLiterals: ['method', 'constructor', 'field'] }], errors: [ { - message: 'Member G should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'G', + rank: 'field' + }, line: 10, column: 5 }, { - message: 'Member H should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'H', + rank: 'field' + }, line: 11, column: 5 }, { - message: 'Member I should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'I', + rank: 'field' + }, line: 12, column: 5 }, { - message: 'Member J should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'J', + rank: 'field' + }, line: 13, column: 5 }, { - message: 'Member K should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'K', + rank: 'field' + }, line: 14, column: 5 }, { - message: 'Member L should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'L', + rank: 'field' + }, line: 15, column: 5 }, { - message: - 'Member new should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'new', + rank: 'field' + }, line: 16, column: 5 } @@ -1659,38 +1806,65 @@ type Foo = { ], errors: [ { - message: 'Member G should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'G', + rank: 'field' + }, line: 10, column: 5 }, { - message: 'Member H should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'H', + rank: 'field' + }, line: 11, column: 5 }, { - message: 'Member I should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'I', + rank: 'field' + }, line: 12, column: 5 }, { - message: 'Member J should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'J', + rank: 'field' + }, line: 13, column: 5 }, { - message: 'Member K should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'K', + rank: 'field' + }, line: 14, column: 5 }, { - message: 'Member L should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'L', + rank: 'field' + }, line: 15, column: 5 }, { - message: - 'Member new should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'new', + rank: 'field' + }, line: 16, column: 5 } @@ -1722,27 +1896,47 @@ type Foo = { ], errors: [ { - message: 'Member B should be declared before all method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'B', + rank: 'method' + }, line: 7, column: 5 }, { - message: 'Member C should be declared before all method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'C', + rank: 'method' + }, line: 9, column: 5 }, { - message: 'Member D should be declared before all method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'D', + rank: 'method' + }, line: 11, column: 5 }, { - message: 'Member E should be declared before all method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'E', + rank: 'method' + }, line: 13, column: 5 }, { - message: 'Member F should be declared before all method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'F', + rank: 'method' + }, line: 15, column: 5 } @@ -1768,20 +1962,29 @@ class Foo { `, errors: [ { - message: - 'Member G should be declared before all public instance method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'G', + rank: 'public instance method' + }, line: 13, column: 5 }, { - message: - 'Member H should be declared before all public instance method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'H', + rank: 'public instance method' + }, line: 14, column: 5 }, { - message: - 'Member I should be declared before all public instance method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'I', + rank: 'public instance method' + }, line: 15, column: 5 } @@ -1808,38 +2011,56 @@ class Foo { options: [{ default: ['field', 'constructor', 'method'] }], errors: [ { - message: - 'Member A should be declared before all constructor definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'A', + rank: 'constructor' + }, line: 4, column: 5 }, { - message: - 'Member B should be declared before all constructor definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'B', + rank: 'constructor' + }, line: 5, column: 5 }, { - message: - 'Member C should be declared before all constructor definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'C', + rank: 'constructor' + }, line: 6, column: 5 }, { - message: - 'Member D should be declared before all constructor definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'D', + rank: 'constructor' + }, line: 7, column: 5 }, { - message: - 'Member E should be declared before all constructor definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'E', + rank: 'constructor' + }, line: 8, column: 5 }, { - message: - 'Member F should be declared before all constructor definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'F', + rank: 'constructor' + }, line: 9, column: 5 } @@ -1866,7 +2087,11 @@ class Foo { options: [{ default: ['field', 'method'] }], errors: [ { - message: 'Member A should be declared before all method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'A', + rank: 'method' + }, line: 10, column: 5 } @@ -1893,7 +2118,11 @@ class Foo { options: [{ default: ['method', 'field'] }], errors: [ { - message: 'Member G should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'G', + rank: 'field' + }, line: 9, column: 5 } @@ -1920,28 +2149,47 @@ class Foo { options: [{ classes: ['method', 'constructor', 'field'] }], errors: [ { - message: 'Member I should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'I', + rank: 'field' + }, line: 6, column: 5 }, { - message: 'Member J should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'J', + rank: 'field' + }, line: 7, column: 5 }, { - message: 'Member K should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'K', + rank: 'field' + }, line: 8, column: 5 }, { - message: 'Member L should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'L', + rank: 'field' + }, line: 9, column: 5 }, { - message: - 'Member constructor should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'constructor', + rank: 'field' + }, line: 11, column: 5 } @@ -1973,38 +2221,65 @@ class Foo { ], errors: [ { - message: 'Member G should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'G', + rank: 'field' + }, line: 4, column: 5 }, { - message: 'Member H should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'H', + rank: 'field' + }, line: 5, column: 5 }, { - message: 'Member I should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'I', + rank: 'field' + }, line: 6, column: 5 }, { - message: 'Member J should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'J', + rank: 'field' + }, line: 7, column: 5 }, { - message: 'Member K should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'K', + rank: 'field' + }, line: 8, column: 5 }, { - message: 'Member L should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'L', + rank: 'field' + }, line: 9, column: 5 }, { - message: - 'Member constructor should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'constructor', + rank: 'field' + }, line: 10, column: 5 } @@ -2043,14 +2318,20 @@ class Foo { ], errors: [ { - message: - 'Member A should be declared before all private field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'A', + rank: 'private field' + }, line: 12, column: 5 }, { - message: - 'Member F should be declared before all protected field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'F', + rank: 'protected field' + }, line: 15, column: 5 } @@ -2090,14 +2371,20 @@ class Foo { ], errors: [ { - message: - 'Member H should be declared before all public instance method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'H', + rank: 'public instance method' + }, line: 6, column: 5 }, { - message: - 'Member constructor should be declared before all public field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'constructor', + rank: 'public field' + }, line: 10, column: 5 } @@ -2134,8 +2421,11 @@ class Foo { ], errors: [ { - message: - 'Member constructor should be declared before all method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'constructor', + rank: 'method' + }, line: 8, column: 5 } @@ -2174,14 +2464,20 @@ class Foo { ], errors: [ { - message: - 'Member G should be declared before all private static method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'G', + rank: 'private static method' + }, line: 5, column: 5 }, { - message: - 'Member H should be declared before all private static method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'H', + rank: 'private static method' + }, line: 6, column: 5 } @@ -2212,8 +2508,11 @@ class Foo { ], errors: [ { - message: - 'Member L should be declared before all protected static field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'L', + rank: 'protected static field' + }, line: 10, column: 5 } @@ -2245,8 +2544,11 @@ class Foo { ], errors: [ { - message: - 'Member J should be declared before all protected static field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'J', + rank: 'protected static field' + }, line: 8, column: 5 } @@ -2272,20 +2574,29 @@ const foo = class Foo { `, errors: [ { - message: - 'Member G should be declared before all public instance method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'G', + rank: 'public instance method' + }, line: 13, column: 5 }, { - message: - 'Member H should be declared before all public instance method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'H', + rank: 'public instance method' + }, line: 14, column: 5 }, { - message: - 'Member I should be declared before all public instance method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'I', + rank: 'public instance method' + }, line: 15, column: 5 } @@ -2312,38 +2623,56 @@ const foo = class { options: [{ default: ['field', 'constructor', 'method'] }], errors: [ { - message: - 'Member A should be declared before all constructor definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'A', + rank: 'constructor' + }, line: 4, column: 5 }, { - message: - 'Member B should be declared before all constructor definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'B', + rank: 'constructor' + }, line: 5, column: 5 }, { - message: - 'Member C should be declared before all constructor definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'C', + rank: 'constructor' + }, line: 6, column: 5 }, { - message: - 'Member D should be declared before all constructor definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'D', + rank: 'constructor' + }, line: 7, column: 5 }, { - message: - 'Member E should be declared before all constructor definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'E', + rank: 'constructor' + }, line: 8, column: 5 }, { - message: - 'Member F should be declared before all constructor definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'F', + rank: 'constructor' + }, line: 9, column: 5 } @@ -2370,7 +2699,11 @@ const foo = class { options: [{ default: ['field', 'method'] }], errors: [ { - message: 'Member A should be declared before all method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'A', + rank: 'method' + }, line: 10, column: 5 } @@ -2397,7 +2730,11 @@ const foo = class { options: [{ default: ['method', 'field'] }], errors: [ { - message: 'Member G should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'G', + rank: 'field' + }, line: 9, column: 5 } @@ -2424,28 +2761,47 @@ const foo = class { options: [{ classExpressions: ['method', 'constructor', 'field'] }], errors: [ { - message: 'Member I should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'I', + rank: 'field' + }, line: 6, column: 5 }, { - message: 'Member J should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'J', + rank: 'field' + }, line: 7, column: 5 }, { - message: 'Member K should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'K', + rank: 'field' + }, line: 8, column: 5 }, { - message: 'Member L should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'L', + rank: 'field' + }, line: 9, column: 5 }, { - message: - 'Member constructor should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'constructor', + rank: 'field' + }, line: 11, column: 5 } @@ -2477,38 +2833,65 @@ const foo = class { ], errors: [ { - message: 'Member G should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'G', + rank: 'field' + }, line: 4, column: 5 }, { - message: 'Member H should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'H', + rank: 'field' + }, line: 5, column: 5 }, { - message: 'Member I should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'I', + rank: 'field' + }, line: 6, column: 5 }, { - message: 'Member J should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'J', + rank: 'field' + }, line: 7, column: 5 }, { - message: 'Member K should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'K', + rank: 'field' + }, line: 8, column: 5 }, { - message: 'Member L should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'L', + rank: 'field' + }, line: 9, column: 5 }, { - message: - 'Member constructor should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'constructor', + rank: 'field' + }, line: 10, column: 5 } @@ -2547,14 +2930,20 @@ const foo = class { ], errors: [ { - message: - 'Member A should be declared before all private field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'A', + rank: 'private field' + }, line: 12, column: 5 }, { - message: - 'Member F should be declared before all protected field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'F', + rank: 'protected field' + }, line: 15, column: 5 } @@ -2594,14 +2983,20 @@ const foo = class { ], errors: [ { - message: - 'Member H should be declared before all public instance method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'H', + rank: 'public instance method' + }, line: 6, column: 5 }, { - message: - 'Member constructor should be declared before all public field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'constructor', + rank: 'public field' + }, line: 10, column: 5 } @@ -2638,8 +3033,11 @@ const foo = class { ], errors: [ { - message: - 'Member constructor should be declared before all method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'constructor', + rank: 'method' + }, line: 8, column: 5 } @@ -2678,14 +3076,20 @@ const foo = class { ], errors: [ { - message: - 'Member G should be declared before all private static method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'G', + rank: 'private static method' + }, line: 5, column: 5 }, { - message: - 'Member H should be declared before all private static method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'H', + rank: 'private static method' + }, line: 6, column: 5 } @@ -2720,8 +3124,11 @@ const foo = class { ], errors: [ { - message: - 'Member L should be declared before all protected static field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'L', + rank: 'protected static field' + }, line: 10, column: 5 } @@ -2753,8 +3160,11 @@ const foo = class { ], errors: [ { - message: - 'Member J should be declared before all protected static field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'J', + rank: 'protected static field' + }, line: 8, column: 5 } @@ -2772,14 +3182,20 @@ class Foo { `, errors: [ { - message: - 'Member A should be declared before all public instance method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'A', + rank: 'public instance method' + }, line: 4, column: 5 }, { - message: - 'Member constructor should be declared before all public instance method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'constructor', + rank: 'public instance method' + }, line: 5, column: 5 } @@ -2797,8 +3213,11 @@ class Foo { options: [{ default: ['method', 'constructor', 'field'] }], errors: [ { - message: - 'Member K should be declared before all constructor definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'K', + rank: 'constructor' + }, line: 5, column: 5 } @@ -2817,8 +3236,11 @@ class Foo { options: [{ default: ['method', 'constructor', 'field'] }], errors: [ { - message: - 'Member K should be declared before all constructor definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'K', + rank: 'constructor' + }, line: 5, column: 5 } @@ -2834,7 +3256,11 @@ interface Foo { `, errors: [ { - message: 'Member A should be declared before all method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'A', + rank: 'method' + }, line: 5, column: 5 } @@ -2850,7 +3276,11 @@ type Foo = { `, errors: [ { - message: 'Member A should be declared before all method definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'A', + rank: 'method' + }, line: 5, column: 5 } @@ -2867,7 +3297,11 @@ type Foo = { options: [{ default: ['method', 'constructor', 'field'] }], errors: [ { - message: 'Member J should be declared before all field definitions.', + messageId: 'incorrectOrder' as 'incorrectOrder', + data: { + name: 'J', + rank: 'field' + }, line: 5, column: 5 } diff --git a/packages/eslint-plugin/tests/lib/rules/no-angle-bracket-type-assertion.js b/packages/eslint-plugin/tests/rules/no-angle-bracket-type-assertion.test.ts similarity index 56% rename from packages/eslint-plugin/tests/lib/rules/no-angle-bracket-type-assertion.js rename to packages/eslint-plugin/tests/rules/no-angle-bracket-type-assertion.test.ts index f4aa7b36274..0a251d75982 100644 --- a/packages/eslint-plugin/tests/lib/rules/no-angle-bracket-type-assertion.js +++ b/packages/eslint-plugin/tests/rules/no-angle-bracket-type-assertion.test.ts @@ -1,19 +1,5 @@ -/** - * @fileoverview Requires the use of as Type for type assertions instead of - * @author Patricio Trevino - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/no-angle-bracket-type-assertion'), - RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +import rule from '../../src/rules/no-angle-bracket-type-assertion'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' @@ -73,15 +59,19 @@ const bar = new Generic(); `, errors: [ { - message: - "Prefer 'as Foo' instead of '' when doing type assertions.", - row: 8, + messageId: 'preferAs', + data: { + cast: 'Foo' + }, + line: 9, column: 13 }, { - message: - "Prefer 'as Foo' instead of '' when doing type assertions.", - row: 9, + messageId: 'preferAs', + data: { + cast: 'Foo' + }, + line: 10, column: 13 } ] @@ -90,9 +80,11 @@ const bar = new Generic(); code: 'const a : number = 5', errors: [ { - message: - "Prefer 'as number' instead of '' when doing type assertions.", - row: 1, + messageId: 'preferAs', + data: { + cast: 'number' + }, + line: 1, column: 20 } ] @@ -104,9 +96,11 @@ const b : number = a; `, errors: [ { - message: - "Prefer 'as number' instead of '' when doing type assertions.", - row: 3, + messageId: 'preferAs', + data: { + cast: 'number' + }, + line: 3, column: 20 } ] @@ -115,9 +109,11 @@ const b : number = a; code: 'const a : Array = >[1];', errors: [ { - message: - "Prefer 'as Array' instead of '>' when doing type assertions.", - row: 1, + messageId: 'preferAs', + data: { + cast: 'Array' + }, + line: 1, column: 27 } ] @@ -132,8 +128,11 @@ const a : A = b; `, errors: [ { - message: "Prefer 'as A' instead of '' when doing type assertions.", - row: 6, + messageId: 'preferAs', + data: { + cast: 'A' + }, + line: 6, column: 15 } ] @@ -152,8 +151,11 @@ const a: A = b; `, errors: [ { - message: "Prefer 'as A' instead of '' when doing type assertions.", - row: 9, + messageId: 'preferAs', + data: { + cast: 'A' + }, + line: 10, column: 14 } ] diff --git a/packages/eslint-plugin/tests/rules/no-array-constructor.test.ts b/packages/eslint-plugin/tests/rules/no-array-constructor.test.ts new file mode 100644 index 00000000000..93e99cb618e --- /dev/null +++ b/packages/eslint-plugin/tests/rules/no-array-constructor.test.ts @@ -0,0 +1,119 @@ +import { AST_NODE_TYPES } from '@typescript-eslint/typescript-estree'; +import rule from '../../src/rules/no-array-constructor'; +import { RuleTester } from '../RuleTester'; + +const ruleTester = new RuleTester({ + parser: '@typescript-eslint/parser' +}); + +const messageId = 'useLiteral' as 'useLiteral'; + +ruleTester.run('no-array-constructor', rule, { + valid: [ + 'new Array(x)', + 'Array(x)', + 'new Array(9)', + 'Array(9)', + 'new foo.Array()', + 'foo.Array()', + 'new Array.foo', + 'Array.foo()', + + // TypeScript + 'new Array(1, 2, 3)', + 'new Array()', + 'Array(1, 2, 3)', + 'Array()' + ], + + invalid: [ + { + code: 'new Array()', + output: '[]', + errors: [ + { + messageId, + type: AST_NODE_TYPES.NewExpression + } + ] + }, + { + code: 'Array()', + output: '[]', + errors: [ + { + messageId, + type: AST_NODE_TYPES.CallExpression + } + ] + }, + { + code: 'new Array', + output: '[]', + errors: [ + { + messageId, + type: AST_NODE_TYPES.NewExpression + } + ] + }, + { + code: 'new Array(x, y)', + output: '[x, y]', + errors: [ + { + messageId, + type: AST_NODE_TYPES.NewExpression + } + ] + }, + { + code: 'Array(x, y)', + output: '[x, y]', + errors: [ + { + messageId, + type: AST_NODE_TYPES.CallExpression + } + ] + }, + { + code: 'new Array(0, 1, 2)', + output: '[0, 1, 2]', + errors: [ + { + messageId, + type: AST_NODE_TYPES.NewExpression + } + ] + }, + { + code: 'Array(0, 1, 2)', + output: '[0, 1, 2]', + errors: [ + { + messageId, + type: AST_NODE_TYPES.CallExpression + } + ] + }, + { + code: `new Array( + 0, + 1, + 2 + )`, + output: `[ + 0, + 1, + 2 + ]`, + errors: [ + { + messageId, + type: AST_NODE_TYPES.NewExpression + } + ] + } + ] +}); diff --git a/packages/eslint-plugin/tests/lib/rules/no-empty-interface.js b/packages/eslint-plugin/tests/rules/no-empty-interface.test.ts similarity index 52% rename from packages/eslint-plugin/tests/lib/rules/no-empty-interface.js rename to packages/eslint-plugin/tests/rules/no-empty-interface.test.ts index a16d875a366..2410509a1fc 100644 --- a/packages/eslint-plugin/tests/lib/rules/no-empty-interface.js +++ b/packages/eslint-plugin/tests/rules/no-empty-interface.test.ts @@ -1,19 +1,5 @@ -/** - * @fileoverview Disallows the declaration of empty interfaces. - * @author Patricio Trevino - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/no-empty-interface'), - RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +import rule from '../../src/rules/no-empty-interface'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' @@ -44,7 +30,7 @@ interface Baz extends Foo, Bar {} code: 'interface Foo {}', errors: [ { - message: 'An empty interface is equivalent to `{}`.', + messageId: 'noEmpty', line: 1, column: 11 } @@ -54,7 +40,7 @@ interface Baz extends Foo, Bar {} code: 'interface Foo extends {}', errors: [ { - message: 'An empty interface is equivalent to `{}`.', + messageId: 'noEmpty', line: 1, column: 11 } @@ -70,8 +56,7 @@ interface Bar extends Foo {} `, errors: [ { - message: - 'An interface declaring no members is equivalent to its supertype.', + messageId: 'noEmptyWithSuper', line: 6, column: 11 } diff --git a/packages/eslint-plugin/tests/lib/rules/no-explicit-any.js b/packages/eslint-plugin/tests/rules/no-explicit-any.test.ts similarity index 71% rename from packages/eslint-plugin/tests/lib/rules/no-explicit-any.js rename to packages/eslint-plugin/tests/rules/no-explicit-any.test.ts index 2f6e0935005..50fc0f95fc3 100644 --- a/packages/eslint-plugin/tests/lib/rules/no-explicit-any.js +++ b/packages/eslint-plugin/tests/rules/no-explicit-any.test.ts @@ -1,19 +1,5 @@ -/** - * @fileoverview Enforces the any type is not used - * @author Danny Fritz - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/no-explicit-any'), - RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +import rule from '../../src/rules/no-explicit-any'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' @@ -149,7 +135,7 @@ type obj = { code: 'const number: any = 1', errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 1, column: 15 } @@ -159,7 +145,7 @@ type obj = { code: 'function generic(): any {}', errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 1, column: 21 } @@ -169,7 +155,7 @@ type obj = { code: 'function generic(): Array {}', errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 1, column: 27 } @@ -179,7 +165,7 @@ type obj = { code: 'function generic(): any[] {}', errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 1, column: 21 } @@ -189,7 +175,7 @@ type obj = { code: 'function generic(param: Array): number {}', errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 1, column: 31 } @@ -199,7 +185,7 @@ type obj = { code: 'function generic(param: any[]): number {}', errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 1, column: 25 } @@ -209,12 +195,12 @@ type obj = { code: 'function generic(param: Array): Array {}', errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 1, column: 31 }, { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 1, column: 44 } @@ -224,7 +210,7 @@ type obj = { code: 'function generic(): Array> {}', errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 1, column: 33 } @@ -234,7 +220,7 @@ type obj = { code: 'function generic(): Array {}', errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 1, column: 27 } @@ -248,7 +234,7 @@ class Greeter { `, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 3, column: 30 } @@ -262,7 +248,7 @@ class Greeter { `, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 3, column: 14 } @@ -276,7 +262,7 @@ class Greeter { `, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 3, column: 20 } @@ -290,7 +276,7 @@ class Greeter { `, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 3, column: 14 } @@ -304,7 +290,7 @@ class Greeter { `, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 3, column: 26 } @@ -318,7 +304,7 @@ class Greeter { `, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 3, column: 20 } @@ -332,7 +318,7 @@ interface Greeter { `, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 3, column: 14 } @@ -346,7 +332,7 @@ interface Greeter { `, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 3, column: 20 } @@ -360,7 +346,7 @@ interface Greeter { `, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 3, column: 14 } @@ -374,7 +360,7 @@ interface Greeter { `, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 3, column: 26 } @@ -388,7 +374,7 @@ interface Greeter { `, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 3, column: 20 } @@ -402,7 +388,7 @@ type obj = { `, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 3, column: 14 } @@ -416,7 +402,7 @@ type obj = { `, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 3, column: 20 } @@ -430,7 +416,7 @@ type obj = { `, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 3, column: 14 } @@ -444,7 +430,7 @@ type obj = { `, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 3, column: 26 } @@ -458,7 +444,7 @@ type obj = { `, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 3, column: 20 } @@ -472,7 +458,7 @@ type obj = { `, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 3, column: 23 } @@ -486,7 +472,7 @@ type obj = { `, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 3, column: 29 } @@ -500,7 +486,7 @@ type obj = { `, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 3, column: 23 } @@ -514,7 +500,7 @@ type obj = { `, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 3, column: 35 } @@ -528,7 +514,7 @@ type obj = { `, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 3, column: 29 } @@ -542,7 +528,7 @@ type obj = { `, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 3, column: 23 } @@ -556,7 +542,7 @@ type obj = { `, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 3, column: 29 } @@ -570,7 +556,7 @@ type obj = { `, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 3, column: 23 } @@ -584,7 +570,7 @@ type obj = { `, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 3, column: 35 } @@ -598,7 +584,7 @@ type obj = { `, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 3, column: 29 } @@ -608,12 +594,12 @@ type obj = { code: `class Foo extends Bar {}`, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 1, column: 15 }, { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 1, column: 32 } @@ -623,12 +609,12 @@ type obj = { code: `abstract class Foo extends Bar {}`, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 1, column: 24 }, { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 1, column: 41 } @@ -638,17 +624,17 @@ type obj = { code: `abstract class Foo implements Bar, Baz {}`, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 1, column: 24 }, { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 1, column: 44 }, { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 1, column: 54 } @@ -658,7 +644,7 @@ type obj = { code: `new Foo()`, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 1, column: 9 } @@ -668,7 +654,7 @@ type obj = { code: `Foo()`, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 1, column: 5 } @@ -682,12 +668,12 @@ type obj = { `, errors: [ { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 2, column: 41 }, { - message: 'Unexpected any. Specify a different type.', + messageId: 'unexpectedAny', line: 3, column: 41 } diff --git a/packages/eslint-plugin/tests/lib/rules/no-extraneous-class.js b/packages/eslint-plugin/tests/rules/no-extraneous-class.test.ts similarity index 64% rename from packages/eslint-plugin/tests/lib/rules/no-extraneous-class.js rename to packages/eslint-plugin/tests/rules/no-extraneous-class.test.ts index 713e5224ccb..ded0672e9d4 100644 --- a/packages/eslint-plugin/tests/lib/rules/no-extraneous-class.js +++ b/packages/eslint-plugin/tests/rules/no-extraneous-class.test.ts @@ -1,24 +1,16 @@ -/** - * @fileoverview Forbids the use of classes as namespaces - * Some tests adapted from https://github.com/palantir/tslint/tree/c7fc99b5/test/rules/no-unnecessary-class - * @author Jed Fox - */ -'use strict'; +import { AST_NODE_TYPES } from '@typescript-eslint/typescript-estree'; +import rule from '../../src/rules/no-extraneous-class'; +import { RuleTester } from '../RuleTester'; -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/no-extraneous-class'), - RuleTester = require('eslint').RuleTester; - -const empty = { messageId: 'empty', type: 'Identifier' }; -const onlyStatic = { messageId: 'onlyStatic', type: 'Identifier' }; -const onlyConstructor = { messageId: 'onlyConstructor', type: 'Identifier' }; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +const empty = { + messageId: 'empty' as 'empty' +}; +const onlyStatic = { + messageId: 'onlyStatic' as 'onlyStatic' +}; +const onlyConstructor = { + messageId: 'onlyConstructor' as 'onlyConstructor' +}; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' @@ -31,7 +23,7 @@ class Foo { public prop = 1; constructor() {} } -`.trim(), +`, ` export class CClass extends BaseClass { public static helper(): void {} @@ -40,14 +32,14 @@ export class CClass extends BaseClass { } constructor() {} } -`.trim(), +`, ` class Foo { constructor( public bar: string ) {} } -`.trim(), +`, { code: 'class Foo {}', options: [{ allowEmpty: true }] @@ -57,7 +49,7 @@ class Foo { class Foo { constructor() {} } -`.trim(), +`, options: [{ allowConstructorOnly: true }] }, { @@ -68,7 +60,7 @@ export class Bar { return true; } } -`.trim(), +`, options: [{ allowStaticOnly: true }] }, // https://github.com/typescript-eslint/typescript-eslint/issues/170 @@ -96,7 +88,7 @@ export class Bar { return true; } } -`.trim(), +`, errors: [onlyStatic, onlyStatic] }, { @@ -104,7 +96,7 @@ export class Bar { class Foo { constructor() {} } -`.trim(), +`, errors: [onlyConstructor] }, { @@ -119,13 +111,19 @@ export class AClass { } } } -`.trim(), + +`, errors: [onlyStatic, empty] }, { // https://github.com/typescript-eslint/typescript-eslint/issues/170 code: 'export default class { static hello() {} }', - errors: [{ messageId: 'onlyStatic', type: 'ClassDeclaration' }] + errors: [ + { + ...onlyStatic, + type: AST_NODE_TYPES.ClassDeclaration + } + ] } ] }); diff --git a/packages/eslint-plugin/tests/rules/no-for-in-array.test.ts b/packages/eslint-plugin/tests/rules/no-for-in-array.test.ts new file mode 100644 index 00000000000..d79ad4757c8 --- /dev/null +++ b/packages/eslint-plugin/tests/rules/no-for-in-array.test.ts @@ -0,0 +1,54 @@ +import { AST_NODE_TYPES } from '@typescript-eslint/typescript-estree'; +import rule from '../../src/rules/no-for-in-array'; +import { RuleTester, getFixturesRootDir } from '../RuleTester'; + +const rootDir = getFixturesRootDir(); +const ruleTester = new RuleTester({ + parserOptions: { + ecmaVersion: 2015, + tsconfigRootDir: rootDir, + project: './tsconfig.json' + }, + parser: '@typescript-eslint/parser' +}); + +ruleTester.run('no-for-in-array', rule, { + valid: [ + ` +for (const x of [3, 4, 5]) { + console.log(x); +}`, + ` +for (const x in { a: 1, b: 2, c: 3 }) { + console.log(x); +}` + ], + + invalid: [ + { + code: ` +for (const x in [3, 4, 5]) { + console.log(x); +}`, + errors: [ + { + messageId: 'forInViolation', + type: AST_NODE_TYPES.ForInStatement + } + ] + }, + { + code: ` +const z = [3, 4, 5]; +for (const x in z) { + console.log(x); +}`, + errors: [ + { + messageId: 'forInViolation', + type: AST_NODE_TYPES.ForInStatement + } + ] + } + ] +}); diff --git a/packages/eslint-plugin/tests/lib/rules/no-inferrable-types.js b/packages/eslint-plugin/tests/rules/no-inferrable-types.test.ts similarity index 62% rename from packages/eslint-plugin/tests/lib/rules/no-inferrable-types.js rename to packages/eslint-plugin/tests/rules/no-inferrable-types.test.ts index 955647e27f3..ab76c5ef0e3 100644 --- a/packages/eslint-plugin/tests/lib/rules/no-inferrable-types.js +++ b/packages/eslint-plugin/tests/rules/no-inferrable-types.test.ts @@ -1,19 +1,5 @@ -/** - * @fileoverview Disallows explicit type declarations for inferrable types - * @author James Garbutt - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/no-inferrable-types'); -const RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +import rule from '../../src/rules/no-inferrable-types'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' @@ -64,8 +50,10 @@ ruleTester.run('no-inferrable-types', rule, { output: 'const a = 5', errors: [ { - message: - 'Type number trivially inferred from a number literal, remove type annotation.', + messageId: 'noInferrableType', + data: { + type: 'number' + }, line: 1, column: 7 } @@ -76,8 +64,10 @@ ruleTester.run('no-inferrable-types', rule, { output: 'const a = Infinity', errors: [ { - message: - 'Type number trivially inferred from a number literal, remove type annotation.', + messageId: 'noInferrableType', + data: { + type: 'number' + }, line: 1, column: 7 } @@ -88,8 +78,10 @@ ruleTester.run('no-inferrable-types', rule, { output: 'const a = true', errors: [ { - message: - 'Type boolean trivially inferred from a boolean literal, remove type annotation.', + messageId: 'noInferrableType', + data: { + type: 'boolean' + }, line: 1, column: 7 } @@ -100,8 +92,10 @@ ruleTester.run('no-inferrable-types', rule, { output: "const a = 'foo'", errors: [ { - message: - 'Type string trivially inferred from a string literal, remove type annotation.', + messageId: 'noInferrableType', + data: { + type: 'string' + }, line: 1, column: 7 } @@ -119,20 +113,26 @@ ruleTester.run('no-inferrable-types', rule, { ], errors: [ { - message: - 'Type number trivially inferred from a number literal, remove type annotation.', + messageId: 'noInferrableType', + data: { + type: 'number' + }, line: 1, column: 13 }, { - message: - 'Type boolean trivially inferred from a boolean literal, remove type annotation.', + messageId: 'noInferrableType', + data: { + type: 'boolean' + }, line: 1, column: 28 }, { - message: - 'Type string trivially inferred from a string literal, remove type annotation.', + messageId: 'noInferrableType', + data: { + type: 'string' + }, line: 1, column: 47 } @@ -150,20 +150,26 @@ ruleTester.run('no-inferrable-types', rule, { ], errors: [ { - message: - 'Type number trivially inferred from a number literal, remove type annotation.', + messageId: 'noInferrableType', + data: { + type: 'number' + }, line: 1, column: 13 }, { - message: - 'Type boolean trivially inferred from a boolean literal, remove type annotation.', + messageId: 'noInferrableType', + data: { + type: 'boolean' + }, line: 1, column: 28 }, { - message: - 'Type string trivially inferred from a string literal, remove type annotation.', + messageId: 'noInferrableType', + data: { + type: 'string' + }, line: 1, column: 47 } diff --git a/packages/eslint-plugin/tests/lib/rules/no-misused-new.js b/packages/eslint-plugin/tests/rules/no-misused-new.test.ts similarity index 74% rename from packages/eslint-plugin/tests/lib/rules/no-misused-new.js rename to packages/eslint-plugin/tests/rules/no-misused-new.test.ts index f770f509872..37a16cf406a 100644 --- a/packages/eslint-plugin/tests/lib/rules/no-misused-new.js +++ b/packages/eslint-plugin/tests/rules/no-misused-new.test.ts @@ -1,19 +1,5 @@ -/** - * @fileoverview Enforce valid definition of `new` and `constructor`. - * @author Armano - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/no-misused-new'), - RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +import rule from '../../src/rules/no-misused-new'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' @@ -55,6 +41,11 @@ interface I { ` type T = { new(): T; +} + `, + ` +export default class { + constructor(); } ` ], diff --git a/packages/eslint-plugin/tests/lib/rules/no-namespace.js b/packages/eslint-plugin/tests/rules/no-namespace.test.ts similarity index 76% rename from packages/eslint-plugin/tests/lib/rules/no-namespace.js rename to packages/eslint-plugin/tests/rules/no-namespace.test.ts index cadabf185d0..8488dcc27b1 100644 --- a/packages/eslint-plugin/tests/lib/rules/no-namespace.js +++ b/packages/eslint-plugin/tests/rules/no-namespace.test.ts @@ -1,19 +1,5 @@ -/** - * @fileoverview Disallows the use of custom TypeScript modules and namespaces. - * @author Patricio Trevino - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/no-namespace'), - RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +import rule from '../../src/rules/no-namespace'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' @@ -48,7 +34,7 @@ ruleTester.run('no-namespace', rule, { errors: [ { messageId: 'moduleSyntaxIsPreferred', - row: 1, + line: 1, column: 1 } ] @@ -58,7 +44,7 @@ ruleTester.run('no-namespace', rule, { errors: [ { messageId: 'moduleSyntaxIsPreferred', - row: 1, + line: 1, column: 1 } ] @@ -69,7 +55,7 @@ ruleTester.run('no-namespace', rule, { errors: [ { messageId: 'moduleSyntaxIsPreferred', - row: 1, + line: 1, column: 1 } ] @@ -80,7 +66,7 @@ ruleTester.run('no-namespace', rule, { errors: [ { messageId: 'moduleSyntaxIsPreferred', - row: 1, + line: 1, column: 1 } ] @@ -90,7 +76,7 @@ ruleTester.run('no-namespace', rule, { errors: [ { messageId: 'moduleSyntaxIsPreferred', - row: 1, + line: 1, column: 1 } ] @@ -100,7 +86,7 @@ ruleTester.run('no-namespace', rule, { errors: [ { messageId: 'moduleSyntaxIsPreferred', - row: 1, + line: 1, column: 1 } ] @@ -111,7 +97,7 @@ ruleTester.run('no-namespace', rule, { errors: [ { messageId: 'moduleSyntaxIsPreferred', - row: 1, + line: 1, column: 1 } ] @@ -122,7 +108,7 @@ ruleTester.run('no-namespace', rule, { errors: [ { messageId: 'moduleSyntaxIsPreferred', - row: 1, + line: 1, column: 1 } ] @@ -134,7 +120,7 @@ ruleTester.run('no-namespace', rule, { errors: [ { messageId: 'moduleSyntaxIsPreferred', - row: 1, + line: 1, column: 1 } ] @@ -146,7 +132,7 @@ ruleTester.run('no-namespace', rule, { errors: [ { messageId: 'moduleSyntaxIsPreferred', - row: 1, + line: 1, column: 1 } ] @@ -158,7 +144,7 @@ ruleTester.run('no-namespace', rule, { errors: [ { messageId: 'moduleSyntaxIsPreferred', - row: 1, + line: 1, column: 1 } ] @@ -170,7 +156,7 @@ ruleTester.run('no-namespace', rule, { errors: [ { messageId: 'moduleSyntaxIsPreferred', - row: 1, + line: 1, column: 1 } ] @@ -181,29 +167,29 @@ ruleTester.run('no-namespace', rule, { errors: [ { messageId: 'moduleSyntaxIsPreferred', - row: 1, + line: 1, column: 1 } ] }, { code: ` - namespace Foo.Bar { - namespace Baz.Bas { - interface X {} - } - } +namespace Foo.Bar { + namespace Baz.Bas { + interface X {} + } +} `, errors: [ { messageId: 'moduleSyntaxIsPreferred', - row: 1, - column: 17 + line: 2, + column: 1 }, { messageId: 'moduleSyntaxIsPreferred', - row: 2, - column: 21 + line: 3, + column: 3 } ] } diff --git a/packages/eslint-plugin/tests/rules/no-non-null-assertion.test.ts b/packages/eslint-plugin/tests/rules/no-non-null-assertion.test.ts new file mode 100644 index 00000000000..84e8ef695b9 --- /dev/null +++ b/packages/eslint-plugin/tests/rules/no-non-null-assertion.test.ts @@ -0,0 +1,22 @@ +import rule from '../../src/rules/no-non-null-assertion'; +import { RuleTester } from '../RuleTester'; + +const ruleTester = new RuleTester({ + parser: '@typescript-eslint/parser' +}); + +ruleTester.run('no-non-null-assertion', rule, { + valid: ['const x = { y: 1 }; x.y;'], + invalid: [ + { + code: 'const x = null; x!.y;', + errors: [ + { + messageId: 'noNonNull', + line: 1, + column: 17 + } + ] + } + ] +}); diff --git a/packages/eslint-plugin/tests/lib/rules/no-object-literal-type-assertion.js b/packages/eslint-plugin/tests/rules/no-object-literal-type-assertion.test.ts similarity index 73% rename from packages/eslint-plugin/tests/lib/rules/no-object-literal-type-assertion.js rename to packages/eslint-plugin/tests/rules/no-object-literal-type-assertion.test.ts index aa19b09e860..23ced3296c6 100644 --- a/packages/eslint-plugin/tests/lib/rules/no-object-literal-type-assertion.js +++ b/packages/eslint-plugin/tests/rules/no-object-literal-type-assertion.test.ts @@ -1,19 +1,5 @@ -/** - * @fileoverview Forbids an object literal to appear in a type assertion expression - * @author Armano - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/no-object-literal-type-assertion'), - RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +import rule from '../../src/rules/no-object-literal-type-assertion'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser', diff --git a/packages/eslint-plugin/tests/lib/rules/no-parameter-properties.js b/packages/eslint-plugin/tests/rules/no-parameter-properties.test.ts similarity index 69% rename from packages/eslint-plugin/tests/lib/rules/no-parameter-properties.js rename to packages/eslint-plugin/tests/rules/no-parameter-properties.test.ts index 80129de18cc..a4dc1668978 100644 --- a/packages/eslint-plugin/tests/lib/rules/no-parameter-properties.js +++ b/packages/eslint-plugin/tests/rules/no-parameter-properties.test.ts @@ -1,19 +1,5 @@ -/** - * @fileoverview Disallows parameter properties in class constructors. - * @author Patricio Trevino - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/no-parameter-properties'), - RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +import rule from '../../src/rules/no-parameter-properties'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' @@ -119,7 +105,10 @@ class Foo { `, errors: [ { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 3, column: 17 } @@ -133,7 +122,10 @@ class Foo { `, errors: [ { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 3, column: 17 } @@ -147,7 +139,10 @@ class Foo { `, errors: [ { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 3, column: 17 } @@ -161,7 +156,10 @@ class Foo { `, errors: [ { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 3, column: 17 } @@ -175,7 +173,10 @@ class Foo { `, errors: [ { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 3, column: 17 } @@ -189,7 +190,10 @@ class Foo { `, errors: [ { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 3, column: 17 } @@ -203,7 +207,10 @@ class Foo { `, errors: [ { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 3, column: 17 } @@ -217,7 +224,10 @@ class Foo { `, errors: [ { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 3, column: 17 } @@ -231,12 +241,18 @@ class Foo { `, errors: [ { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 3, column: 17 }, { - message: 'Property age cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'age' + }, line: 3, column: 39 } @@ -250,12 +266,18 @@ class Foo { `, errors: [ { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 3, column: 17 }, { - message: 'Property age cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'age' + }, line: 3, column: 41 } @@ -269,12 +291,18 @@ class Foo { `, errors: [ { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 3, column: 17 }, { - message: 'Property age cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'age' + }, line: 3, column: 38 } @@ -289,7 +317,10 @@ class Foo { `, errors: [ { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 4, column: 17 } @@ -304,12 +335,18 @@ class Foo { `, errors: [ { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 3, column: 17 }, { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 4, column: 17 } @@ -324,17 +361,26 @@ class Foo { `, errors: [ { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 3, column: 17 }, { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 4, column: 17 }, { - message: 'Property age cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'age' + }, line: 4, column: 39 } @@ -349,7 +395,10 @@ class Foo { `, errors: [ { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 4, column: 17 } @@ -364,12 +413,18 @@ class Foo { `, errors: [ { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 3, column: 17 }, { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 4, column: 17 } @@ -384,17 +439,26 @@ class Foo { `, errors: [ { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 3, column: 17 }, { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 4, column: 17 }, { - message: 'Property age cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'age' + }, line: 4, column: 41 } @@ -409,7 +473,10 @@ class Foo { `, errors: [ { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 4, column: 17 } @@ -424,12 +491,18 @@ class Foo { `, errors: [ { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 3, column: 17 }, { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 4, column: 17 } @@ -444,17 +517,26 @@ class Foo { `, errors: [ { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 3, column: 17 }, { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 4, column: 17 }, { - message: 'Property age cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'age' + }, line: 4, column: 38 } @@ -470,7 +552,10 @@ class Foo { options: [{ allows: ['private'] }], errors: [ { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 3, column: 17 } @@ -485,7 +570,10 @@ class Foo { options: [{ allows: ['readonly'] }], errors: [ { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 3, column: 17 } @@ -504,7 +592,10 @@ class Foo { ], errors: [ { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 3, column: 17 } @@ -529,7 +620,10 @@ class Foo { ], errors: [ { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 3, column: 17 } @@ -544,7 +638,10 @@ class Foo { options: [{ allows: ['readonly', 'private'] }], errors: [ { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 3, column: 17 } @@ -568,7 +665,10 @@ class Foo { ], errors: [ { - message: 'Property name cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'name' + }, line: 3, column: 17 } @@ -584,7 +684,10 @@ class Foo { options: [{ allows: ['private'] }], errors: [ { - message: 'Property age cannot be declared in the constructor.', + messageId: 'noParamProp', + data: { + parameter: 'age' + }, line: 4, column: 39 } diff --git a/packages/eslint-plugin/tests/lib/rules/no-require-imports.js b/packages/eslint-plugin/tests/rules/no-require-imports.test.ts similarity index 54% rename from packages/eslint-plugin/tests/lib/rules/no-require-imports.js rename to packages/eslint-plugin/tests/rules/no-require-imports.test.ts index 1eb40a59c34..8e8e310bd61 100644 --- a/packages/eslint-plugin/tests/lib/rules/no-require-imports.js +++ b/packages/eslint-plugin/tests/rules/no-require-imports.test.ts @@ -1,19 +1,5 @@ -/** - * @fileoverview Disallows invocation of `require()`. - * @author Kanitkorn Sujautra - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/no-require-imports'), - RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +import rule from '../../src/rules/no-require-imports'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parserOptions: { @@ -36,7 +22,7 @@ ruleTester.run('no-require-imports', rule, { code: "var lib = require('lib')", errors: [ { - message: 'A `require()` style import is forbidden.', + messageId: 'noRequireImports', line: 1, column: 11 } @@ -46,7 +32,7 @@ ruleTester.run('no-require-imports', rule, { code: "let lib2 = require('lib2')", errors: [ { - message: 'A `require()` style import is forbidden.', + messageId: 'noRequireImports', line: 1, column: 12 } @@ -56,12 +42,12 @@ ruleTester.run('no-require-imports', rule, { code: "var lib5 = require('lib5'), lib6 = require('lib6')", errors: [ { - message: 'A `require()` style import is forbidden.', + messageId: 'noRequireImports', line: 1, column: 12 }, { - message: 'A `require()` style import is forbidden.', + messageId: 'noRequireImports', line: 1, column: 36 } @@ -71,7 +57,7 @@ ruleTester.run('no-require-imports', rule, { code: "import lib8 = require('lib8')", errors: [ { - message: 'A `require()` style import is forbidden.', + messageId: 'noRequireImports', line: 1, column: 15 } diff --git a/packages/eslint-plugin/tests/lib/rules/no-this-alias.js b/packages/eslint-plugin/tests/rules/no-this-alias.test.ts similarity index 69% rename from packages/eslint-plugin/tests/lib/rules/no-this-alias.js rename to packages/eslint-plugin/tests/rules/no-this-alias.test.ts index e2f76005da4..25338a5158f 100644 --- a/packages/eslint-plugin/tests/lib/rules/no-this-alias.js +++ b/packages/eslint-plugin/tests/rules/no-this-alias.test.ts @@ -1,31 +1,20 @@ -/** - * @fileoverview Disallow aliasing `this` - * Some tests taken from TSLint: https://github.com/palantir/tslint/tree/c7fc99b5/test/rules/no-this-assignment - * @author Jed Fox - */ -'use strict'; +import { AST_NODE_TYPES } from '@typescript-eslint/typescript-estree'; +import rule from '../../src/rules/no-this-alias'; +import { RuleTester } from '../RuleTester'; -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/no-this-alias'), - RuleTester = require('eslint').RuleTester; - -const idError = { messageId: 'thisAssignment', type: 'Identifier' }; +const idError = { + messageId: 'thisAssignment' as 'thisAssignment', + type: AST_NODE_TYPES.Identifier +}; const destructureError = { - messageId: 'thisDestructure', - type: 'ObjectPattern' + messageId: 'thisDestructure' as 'thisDestructure', + type: AST_NODE_TYPES.ObjectPattern }; const arrayDestructureError = { - messageId: 'thisDestructure', - type: 'ArrayPattern' + messageId: 'thisDestructure' as 'thisDestructure', + type: AST_NODE_TYPES.ArrayPattern }; -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' }); @@ -40,7 +29,7 @@ const { length } = this; const { length, toString } = this; const [foo] = this; const [foo, bar] = this; -`.trim(), +`, options: [ { allowDestructuring: true @@ -91,7 +80,7 @@ function testFunction() { const testLambda = () => { const inLambda = this; }; -`.trim(), +`, errors: [idError, idError, idError] }, { @@ -114,7 +103,7 @@ class TestClass { const [foo, bar] = this; } } -`.trim(), +`, errors: [ idError, idError, diff --git a/packages/eslint-plugin/tests/lib/rules/no-triple-slash-reference.js b/packages/eslint-plugin/tests/rules/no-triple-slash-reference.test.ts similarity index 60% rename from packages/eslint-plugin/tests/lib/rules/no-triple-slash-reference.js rename to packages/eslint-plugin/tests/rules/no-triple-slash-reference.test.ts index 5e61a3736c0..4fddd596aa5 100644 --- a/packages/eslint-plugin/tests/lib/rules/no-triple-slash-reference.js +++ b/packages/eslint-plugin/tests/rules/no-triple-slash-reference.test.ts @@ -1,19 +1,5 @@ -/** - * @fileoverview Enforces triple slash references are not used - * @author Danny Fritz - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/no-triple-slash-reference'), - RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +import rule from '../../src/rules/no-triple-slash-reference'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' diff --git a/packages/eslint-plugin/tests/lib/rules/no-type-alias.js b/packages/eslint-plugin/tests/rules/no-type-alias.test.ts similarity index 93% rename from packages/eslint-plugin/tests/lib/rules/no-type-alias.js rename to packages/eslint-plugin/tests/rules/no-type-alias.test.ts index fc3c91f7fa1..29670540cc1 100644 --- a/packages/eslint-plugin/tests/lib/rules/no-type-alias.js +++ b/packages/eslint-plugin/tests/rules/no-type-alias.test.ts @@ -1,19 +1,5 @@ -/** - * @fileoverview Disallows the use of type aliases. - * @author Patricio Trevino - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/no-type-alias'), - RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +import rule from '../../src/rules/no-type-alias'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' @@ -388,7 +374,7 @@ type Foo = { data: { alias: 'aliases' }, - row: 1, + line: 1, column: 12 } ] @@ -402,7 +388,7 @@ type Foo = { data: { alias: 'aliases' }, - row: 1, + line: 1, column: 12 } ] @@ -416,7 +402,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -425,7 +411,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 18 } ] @@ -440,7 +426,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -449,7 +435,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 18 } ] @@ -464,7 +450,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -473,7 +459,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 18 } ] @@ -488,7 +474,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -497,7 +483,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 18 } ] @@ -517,7 +503,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -526,7 +512,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 18 } ] @@ -540,7 +526,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -549,7 +535,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 18 }, { @@ -558,7 +544,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 24 } ] @@ -573,7 +559,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -582,7 +568,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 18 }, { @@ -591,7 +577,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 24 } ] @@ -606,7 +592,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -615,7 +601,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 18 }, { @@ -624,7 +610,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 24 } ] @@ -639,7 +625,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -648,7 +634,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 18 }, { @@ -657,7 +643,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 24 } ] @@ -672,7 +658,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -681,7 +667,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 18 }, { @@ -690,7 +676,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 24 } ] @@ -710,7 +696,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -719,7 +705,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 18 }, { @@ -728,7 +714,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 24 } ] @@ -742,7 +728,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 12 }, { @@ -751,7 +737,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 18 } ] @@ -766,7 +752,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 12 }, { @@ -775,7 +761,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 18 } ] @@ -790,7 +776,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 12 }, { @@ -799,7 +785,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 18 } ] @@ -814,7 +800,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 12 }, { @@ -823,7 +809,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 18 } ] @@ -838,7 +824,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 12 }, { @@ -847,7 +833,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 18 } ] @@ -867,7 +853,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 12 }, { @@ -876,7 +862,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 18 } ] @@ -890,7 +876,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 12 }, { @@ -899,7 +885,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 18 }, { @@ -908,7 +894,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 24 } ] @@ -923,7 +909,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 12 }, { @@ -932,7 +918,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 18 }, { @@ -941,7 +927,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 24 } ] @@ -956,7 +942,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 12 }, { @@ -965,7 +951,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 18 }, { @@ -974,7 +960,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 24 } ] @@ -989,7 +975,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 12 }, { @@ -998,7 +984,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 18 }, { @@ -1007,7 +993,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 24 } ] @@ -1022,7 +1008,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 12 }, { @@ -1031,7 +1017,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 18 }, { @@ -1040,7 +1026,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 24 } ] @@ -1060,7 +1046,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 12 }, { @@ -1069,7 +1055,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 18 }, { @@ -1078,7 +1064,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 24 } ] @@ -1092,7 +1078,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -1101,7 +1087,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 18 }, { @@ -1110,7 +1096,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 24 } ] @@ -1125,7 +1111,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -1134,7 +1120,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 18 }, { @@ -1143,7 +1129,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 24 } ] @@ -1158,7 +1144,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -1167,7 +1153,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 18 }, { @@ -1176,7 +1162,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 24 } ] @@ -1191,7 +1177,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -1200,7 +1186,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 18 }, { @@ -1209,7 +1195,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 24 } ] @@ -1224,7 +1210,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 18 }, { @@ -1233,7 +1219,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 24 } ] @@ -1253,7 +1239,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 18 }, { @@ -1262,7 +1248,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 24 } ] @@ -1277,7 +1263,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 } ] @@ -1297,7 +1283,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 } ] @@ -1310,7 +1296,7 @@ type Foo = { data: { alias: 'aliases' }, - row: 1, + line: 1, column: 12 } ] @@ -1324,7 +1310,7 @@ type Foo = { data: { alias: 'aliases' }, - row: 1, + line: 1, column: 12 } ] @@ -1338,7 +1324,7 @@ type Foo = { data: { alias: 'aliases' }, - row: 1, + line: 1, column: 12 } ] @@ -1352,7 +1338,7 @@ type Foo = { data: { alias: 'aliases' }, - row: 1, + line: 1, column: 12 } ] @@ -1366,7 +1352,7 @@ type Foo = { data: { alias: 'aliases' }, - row: 1, + line: 1, column: 12 } ] @@ -1380,7 +1366,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -1389,7 +1375,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 21 } ] @@ -1404,7 +1390,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -1413,7 +1399,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 21 } ] @@ -1428,7 +1414,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -1437,7 +1423,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 21 } ] @@ -1452,7 +1438,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -1461,7 +1447,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 21 } ] @@ -1476,7 +1462,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -1485,7 +1471,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 21 } ] @@ -1505,7 +1491,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -1514,7 +1500,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 21 } ] @@ -1528,7 +1514,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -1537,7 +1523,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 21 }, { @@ -1546,7 +1532,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 32 } ] @@ -1561,7 +1547,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -1570,7 +1556,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 21 }, { @@ -1579,7 +1565,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 32 } ] @@ -1594,7 +1580,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -1603,7 +1589,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 21 }, { @@ -1612,7 +1598,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 32 } ] @@ -1627,7 +1613,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -1636,7 +1622,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 21 }, { @@ -1645,7 +1631,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 32 } ] @@ -1660,7 +1646,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -1669,7 +1655,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 21 }, { @@ -1678,7 +1664,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 32 } ] @@ -1698,7 +1684,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -1707,7 +1693,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 21 }, { @@ -1716,7 +1702,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 32 } ] @@ -1730,7 +1716,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -1739,7 +1725,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 21 }, { @@ -1748,7 +1734,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 32 } ] @@ -1763,7 +1749,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -1772,7 +1758,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 21 }, { @@ -1781,7 +1767,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 32 } ] @@ -1796,7 +1782,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -1805,7 +1791,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 21 }, { @@ -1814,7 +1800,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 32 } ] @@ -1829,7 +1815,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -1838,7 +1824,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 21 }, { @@ -1847,7 +1833,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 32 } ] @@ -1862,7 +1848,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 21 }, { @@ -1871,7 +1857,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 32 } ] @@ -1886,7 +1872,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 21 }, { @@ -1895,7 +1881,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 32 } ] @@ -1910,7 +1896,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 } ] @@ -1930,7 +1916,7 @@ type Foo = { typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 12 } ] @@ -1946,7 +1932,7 @@ type Foo = Bar; data: { alias: 'aliases' }, - row: 1, + line: 3, column: 12 } ] @@ -1963,7 +1949,7 @@ type Foo = Bar; data: { alias: 'aliases' }, - row: 1, + line: 3, column: 12 } ] @@ -1980,7 +1966,7 @@ type Foo = Bar; data: { alias: 'aliases' }, - row: 1, + line: 3, column: 12 } ] @@ -1997,7 +1983,7 @@ type Foo = Bar; data: { alias: 'aliases' }, - row: 1, + line: 3, column: 12 } ] @@ -2014,7 +2000,7 @@ type Foo = Bar; data: { alias: 'aliases' }, - row: 1, + line: 3, column: 12 } ] @@ -2031,7 +2017,7 @@ type Foo = Bar | {}; typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 3, column: 12 }, { @@ -2040,7 +2026,7 @@ type Foo = Bar | {}; typeName: 'Literals', compositionType: 'union' }, - row: 1, + line: 3, column: 18 } ] @@ -2058,7 +2044,7 @@ type Foo = Bar | {}; typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 3, column: 12 }, { @@ -2067,7 +2053,7 @@ type Foo = Bar | {}; typeName: 'Literals', compositionType: 'union' }, - row: 1, + line: 3, column: 18 } ] @@ -2085,7 +2071,7 @@ type Foo = Bar | {}; typeName: 'Literals', compositionType: 'union' }, - row: 1, + line: 3, column: 18 } ] @@ -2103,7 +2089,7 @@ type Foo = Bar | {}; typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 3, column: 12 }, { @@ -2112,7 +2098,7 @@ type Foo = Bar | {}; typeName: 'Literals', compositionType: 'union' }, - row: 1, + line: 3, column: 18 } ] @@ -2130,7 +2116,7 @@ type Foo = Bar | {}; typeName: 'Literals', compositionType: 'union' }, - row: 1, + line: 3, column: 18 } ] @@ -2147,7 +2133,7 @@ type Foo = Bar & {}; typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 3, column: 12 }, { @@ -2156,7 +2142,7 @@ type Foo = Bar & {}; typeName: 'Literals', compositionType: 'intersection' }, - row: 1, + line: 3, column: 18 } ] @@ -2174,7 +2160,7 @@ type Foo = Bar & {}; typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 3, column: 12 }, { @@ -2183,7 +2169,7 @@ type Foo = Bar & {}; typeName: 'Literals', compositionType: 'intersection' }, - row: 1, + line: 3, column: 18 } ] @@ -2201,7 +2187,7 @@ type Foo = Bar & {}; typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 3, column: 12 }, { @@ -2210,7 +2196,7 @@ type Foo = Bar & {}; typeName: 'Literals', compositionType: 'intersection' }, - row: 1, + line: 3, column: 18 } ] @@ -2228,7 +2214,7 @@ type Foo = Bar & {}; typeName: 'Literals', compositionType: 'intersection' }, - row: 1, + line: 3, column: 18 } ] @@ -2246,7 +2232,7 @@ type Foo = Bar & {}; typeName: 'Literals', compositionType: 'intersection' }, - row: 1, + line: 3, column: 18 } ] @@ -2259,7 +2245,7 @@ type Foo = Bar & {}; data: { alias: 'callbacks' }, - row: 1, + line: 1, column: 12 } ] @@ -2273,7 +2259,7 @@ type Foo = Bar & {}; data: { alias: 'callbacks' }, - row: 1, + line: 1, column: 12 } ] @@ -2286,7 +2272,7 @@ type Foo = Bar & {}; data: { alias: 'literals' }, - row: 1, + line: 1, column: 12 } ] @@ -2300,7 +2286,7 @@ type Foo = Bar & {}; data: { alias: 'literals' }, - row: 1, + line: 1, column: 12 } ] @@ -2314,7 +2300,7 @@ type Foo = Bar & {}; data: { alias: 'literals' }, - row: 1, + line: 1, column: 12 } ] @@ -2328,7 +2314,7 @@ type Foo = Bar & {}; data: { alias: 'literals' }, - row: 1, + line: 1, column: 12 } ] @@ -2342,7 +2328,7 @@ type Foo = Bar & {}; data: { alias: 'literals' }, - row: 1, + line: 1, column: 12 } ] @@ -2356,7 +2342,7 @@ type Foo = Bar & {}; data: { alias: 'literals' }, - row: 1, + line: 1, column: 12 } ] @@ -2370,7 +2356,7 @@ type Foo = Bar & {}; data: { alias: 'literals' }, - row: 1, + line: 1, column: 12 } ] @@ -2389,7 +2375,7 @@ type Foo = Bar & {}; data: { alias: 'literals' }, - row: 1, + line: 1, column: 12 } ] @@ -2408,7 +2394,7 @@ type Foo = Bar & {}; data: { alias: 'literals' }, - row: 1, + line: 1, column: 12 } ] @@ -2427,7 +2413,7 @@ type Foo = Bar & {}; data: { alias: 'literals' }, - row: 1, + line: 1, column: 12 } ] @@ -2441,7 +2427,7 @@ type Foo = Bar & {}; typeName: 'Literals', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -2450,7 +2436,7 @@ type Foo = Bar & {}; typeName: 'Literals', compositionType: 'union' }, - row: 1, + line: 1, column: 17 } ] @@ -2465,7 +2451,7 @@ type Foo = Bar & {}; typeName: 'Literals', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -2474,7 +2460,7 @@ type Foo = Bar & {}; typeName: 'Literals', compositionType: 'union' }, - row: 1, + line: 1, column: 17 } ] @@ -2489,7 +2475,7 @@ type Foo = Bar & {}; typeName: 'Literals', compositionType: 'union' }, - row: 1, + line: 1, column: 12 }, { @@ -2498,7 +2484,7 @@ type Foo = Bar & {}; typeName: 'Literals', compositionType: 'union' }, - row: 1, + line: 1, column: 17 } ] @@ -2512,7 +2498,7 @@ type Foo = Bar & {}; typeName: 'Literals', compositionType: 'intersection' }, - row: 1, + line: 1, column: 12 }, { @@ -2521,7 +2507,7 @@ type Foo = Bar & {}; typeName: 'Literals', compositionType: 'intersection' }, - row: 1, + line: 1, column: 17 } ] @@ -2536,7 +2522,7 @@ type Foo = Bar & {}; typeName: 'Literals', compositionType: 'intersection' }, - row: 1, + line: 1, column: 12 }, { @@ -2545,7 +2531,7 @@ type Foo = Bar & {}; typeName: 'Literals', compositionType: 'intersection' }, - row: 1, + line: 1, column: 17 } ] @@ -2560,7 +2546,7 @@ type Foo = Bar & {}; typeName: 'Literals', compositionType: 'intersection' }, - row: 1, + line: 1, column: 12 }, { @@ -2569,7 +2555,7 @@ type Foo = Bar & {}; typeName: 'Literals', compositionType: 'intersection' }, - row: 1, + line: 1, column: 17 } ] @@ -2584,7 +2570,7 @@ type Foo = Bar & {}; typeName: 'Aliases', compositionType: 'intersection' }, - row: 1, + line: 1, column: 12 }, { @@ -2593,7 +2579,7 @@ type Foo = Bar & {}; typeName: 'Literals', compositionType: 'intersection' }, - row: 1, + line: 1, column: 21 } ] @@ -2613,7 +2599,7 @@ type Foo = Bar & {}; typeName: 'Literals', compositionType: 'intersection' }, - row: 1, + line: 1, column: 21 }, { @@ -2622,7 +2608,7 @@ type Foo = Bar & {}; typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 26 }, { @@ -2631,7 +2617,7 @@ type Foo = Bar & {}; typeName: 'Aliases', compositionType: 'union' }, - row: 1, + line: 1, column: 32 } ] @@ -2648,7 +2634,7 @@ type Foo = { data: { alias: 'mapped types' }, - row: 1, + line: 2, column: 15 } ] @@ -2666,7 +2652,7 @@ type Foo = { data: { alias: 'mapped types' }, - row: 1, + line: 2, column: 15 } ] @@ -2684,7 +2670,7 @@ type Foo = { data: { alias: 'mapped types' }, - row: 1, + line: 2, column: 15 } ] @@ -2702,7 +2688,7 @@ type Foo = { data: { alias: 'mapped types' }, - row: 1, + line: 2, column: 15 } ] @@ -2720,7 +2706,7 @@ type Foo = { data: { alias: 'mapped types' }, - row: 1, + line: 2, column: 15 } ] @@ -2740,7 +2726,7 @@ type Foo = { typeName: 'Mapped types', compositionType: 'union' }, - row: 1, + line: 2, column: 15 }, { @@ -2749,7 +2735,7 @@ type Foo = { typeName: 'Mapped types', compositionType: 'union' }, - row: 4, + line: 4, column: 5 } ] @@ -2770,7 +2756,7 @@ type Foo = { typeName: 'Mapped types', compositionType: 'union' }, - row: 1, + line: 2, column: 15 }, { @@ -2779,7 +2765,7 @@ type Foo = { typeName: 'Mapped types', compositionType: 'union' }, - row: 4, + line: 4, column: 5 } ] @@ -2800,7 +2786,7 @@ type Foo = { typeName: 'Mapped types', compositionType: 'union' }, - row: 1, + line: 2, column: 15 }, { @@ -2809,7 +2795,7 @@ type Foo = { typeName: 'Mapped types', compositionType: 'union' }, - row: 4, + line: 4, column: 5 } ] @@ -2829,7 +2815,7 @@ type Foo = { typeName: 'Mapped types', compositionType: 'intersection' }, - row: 1, + line: 2, column: 15 }, { @@ -2838,7 +2824,7 @@ type Foo = { typeName: 'Mapped types', compositionType: 'intersection' }, - row: 4, + line: 4, column: 5 } ] @@ -2859,7 +2845,7 @@ type Foo = { typeName: 'Mapped types', compositionType: 'intersection' }, - row: 1, + line: 2, column: 15 }, { @@ -2868,7 +2854,7 @@ type Foo = { typeName: 'Mapped types', compositionType: 'intersection' }, - row: 4, + line: 4, column: 5 } ] @@ -2889,7 +2875,7 @@ type Foo = { typeName: 'Mapped types', compositionType: 'intersection' }, - row: 1, + line: 2, column: 15 }, { @@ -2898,7 +2884,7 @@ type Foo = { typeName: 'Mapped types', compositionType: 'intersection' }, - row: 4, + line: 4, column: 5 } ] diff --git a/packages/eslint-plugin/tests/lib/rules/no-unnecessary-type-assertion.js b/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts similarity index 78% rename from packages/eslint-plugin/tests/lib/rules/no-unnecessary-type-assertion.js rename to packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts index 8795cff89c5..e223fc4ac0e 100644 --- a/packages/eslint-plugin/tests/lib/rules/no-unnecessary-type-assertion.js +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts @@ -1,20 +1,6 @@ -/** - * @fileoverview Warns if a type assertion does not change the type of an expression. - * @author Benjamin Lichtman - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/no-unnecessary-type-assertion'), - RuleTester = require('eslint').RuleTester, - path = require('path'); - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +import path from 'path'; +import rule from '../../src/rules/no-unnecessary-type-assertion'; +import { RuleTester } from '../RuleTester'; const rootDir = path.join(process.cwd(), 'tests/fixtures'); const parserOptions = { diff --git a/packages/eslint-plugin/tests/lib/rules/no-unused-vars.js b/packages/eslint-plugin/tests/rules/no-unused-vars.test.ts similarity index 94% rename from packages/eslint-plugin/tests/lib/rules/no-unused-vars.js rename to packages/eslint-plugin/tests/rules/no-unused-vars.test.ts index 6aef8a1e50a..2e188c870a0 100644 --- a/packages/eslint-plugin/tests/lib/rules/no-unused-vars.js +++ b/packages/eslint-plugin/tests/rules/no-unused-vars.test.ts @@ -1,15 +1,5 @@ -/** - * @fileoverview Prevent variables used in TypeScript being marked as unused - * @author James Henry - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/no-unused-vars'); -const RuleTester = require('eslint').RuleTester; +import rule from '../../src/rules/no-unused-vars'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parserOptions: { @@ -20,9 +10,12 @@ const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' }); -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +// the base rule doesn't have messageIds +function error( + messages: { message: string; line: number; column: number }[] +): any[] { + return messages; +} ruleTester.run('no-unused-vars', rule, { valid: [ @@ -593,13 +586,13 @@ export function Foo() { import { ClassDecoratorFactory } from 'decorators'; export class Foo {} `, - errors: [ + errors: error([ { message: "'ClassDecoratorFactory' is defined but never used.", line: 2, column: 10 } - ] + ]) }, { code: ` @@ -607,13 +600,13 @@ import { Foo, Bar } from 'foo'; function baz() {} baz() `, - errors: [ + errors: error([ { message: "'Foo' is defined but never used.", line: 2, column: 10 } - ] + ]) }, { code: ` @@ -621,13 +614,13 @@ import { Nullable } from 'nullable'; const a: string = 'hello'; console.log(a); `, - errors: [ + errors: error([ { message: "'Nullable' is defined but never used.", line: 2, column: 10 } - ] + ]) }, { code: ` @@ -636,13 +629,13 @@ import { SomeOther } from 'other'; const a: Nullable = 'hello'; console.log(a); `, - errors: [ + errors: error([ { message: "'SomeOther' is defined but never used.", line: 3, column: 10 } - ] + ]) }, { @@ -654,13 +647,13 @@ class A { } new A(); `, - errors: [ + errors: error([ { message: "'Another' is defined but never used.", line: 3, column: 10 } - ] + ]) }, { code: ` @@ -671,13 +664,13 @@ class A { } new A(); `, - errors: [ + errors: error([ { message: "'Another' is defined but never used.", line: 3, column: 10 } - ] + ]) }, { code: ` @@ -688,13 +681,13 @@ class A { } new A(); `, - errors: [ + errors: error([ { message: "'Another' is defined but never used.", line: 3, column: 10 } - ] + ]) }, { code: ` @@ -704,13 +697,13 @@ interface A { do(a: Nullable); } `, - errors: [ + errors: error([ { message: "'Another' is defined but never used.", line: 3, column: 10 } - ] + ]) }, { code: ` @@ -720,13 +713,13 @@ interface A { other: Nullable; } `, - errors: [ + errors: error([ { message: "'Another' is defined but never used.", line: 3, column: 10 } - ] + ]) }, { code: ` @@ -734,13 +727,13 @@ import { Nullable } from 'nullable'; function foo(a: string) { console.log(a); } foo(); `, - errors: [ + errors: error([ { message: "'Nullable' is defined but never used.", line: 2, column: 10 } - ] + ]) }, { code: ` @@ -748,13 +741,13 @@ import { Nullable } from 'nullable'; function foo(): string | null { return null; } foo(); `, - errors: [ + errors: error([ { message: "'Nullable' is defined but never used.", line: 2, column: 10 } - ] + ]) }, { code: ` @@ -766,13 +759,13 @@ class A extends Nullable { } new A(); `, - errors: [ + errors: error([ { message: "'SomeOther' is defined but never used.", line: 3, column: 10 } - ] + ]) }, { code: ` @@ -784,13 +777,13 @@ abstract class A extends Nullable { } new A(); `, - errors: [ + errors: error([ { message: "'SomeOther' is defined but never used.", line: 3, column: 10 } - ] + ]) }, { code: ` @@ -799,13 +792,13 @@ enum FormFieldIds { EMAIL = 'email', } `, - errors: [ + errors: error([ { message: "'FormFieldIds' is defined but never used.", line: 2, column: 6 } - ] + ]) }, { code: ` @@ -813,13 +806,13 @@ import test from 'test'; import baz from 'baz'; export interface Bar extends baz.test {} `, - errors: [ + errors: error([ { message: "'test' is defined but never used.", line: 2, column: 8 } - ] + ]) }, { code: ` @@ -827,13 +820,13 @@ import test from 'test'; import baz from 'baz'; export interface Bar extends baz().test {} `, - errors: [ + errors: error([ { message: "'test' is defined but never used.", line: 2, column: 8 } - ] + ]) }, { code: ` @@ -841,13 +834,13 @@ import test from 'test'; import baz from 'baz'; export class Bar implements baz.test {} `, - errors: [ + errors: error([ { message: "'test' is defined but never used.", line: 2, column: 8 } - ] + ]) }, { code: ` @@ -855,13 +848,13 @@ import test from 'test'; import baz from 'baz'; export class Bar implements baz().test {} `, - errors: [ + errors: error([ { message: "'test' is defined but never used.", line: 2, column: 8 } - ] + ]) } ] }); diff --git a/packages/eslint-plugin/tests/lib/rules/no-use-before-define.js b/packages/eslint-plugin/tests/rules/no-use-before-define.test.ts similarity index 53% rename from packages/eslint-plugin/tests/lib/rules/no-use-before-define.js rename to packages/eslint-plugin/tests/rules/no-use-before-define.test.ts index b74645208d6..dc56199e588 100644 --- a/packages/eslint-plugin/tests/lib/rules/no-use-before-define.js +++ b/packages/eslint-plugin/tests/rules/no-use-before-define.test.ts @@ -1,28 +1,21 @@ -/** - * @fileoverview Tests for no-use-before-define rule. - * @author Ilya Volodin - * @author Jed Fox - */ - -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/no-use-before-define'), - RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +import rule from '../../src/rules/no-use-before-define'; +import { RuleTester } from '../RuleTester'; +import { AST_NODE_TYPES } from '@typescript-eslint/typescript-estree'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' }); +const parserOptions = { ecmaVersion: 6 as 6 }; + ruleTester.run('no-use-before-define', rule, { valid: [ + 'type foo = 1; const x: foo = 1;', + 'type foo = 1; type bar = foo;', + ` +interface Foo {} +const x: Foo = {}; + `, 'var a=10; alert(a);', 'function b(a) { alert(a); }', 'Object.hasOwnProperty.call(a);', @@ -38,7 +31,7 @@ function a() { alert(arguments); } }, { code: '(() => { var a = 42; alert(a); })();', - parserOptions: { ecmaVersion: 6 } + parserOptions }, ` a(); @@ -51,11 +44,11 @@ try { class A {} new A(); `, - parserOptions: { ecmaVersion: 6 } + parserOptions }, 'var a = 0, b = a;', - { code: 'var {a = 0, b = a} = {};', parserOptions: { ecmaVersion: 6 } }, - { code: 'var [a = 0, b = a] = {};', parserOptions: { ecmaVersion: 6 } }, + { code: 'var {a = 0, b = a} = {};', parserOptions }, + { code: 'var [a = 0, b = a] = {};', parserOptions }, ` function foo() { foo(); @@ -75,7 +68,7 @@ for (a in a) {} var a; for (a of a) {} `, - parserOptions: { ecmaVersion: 6 } + parserOptions }, // Block-level bindings @@ -87,7 +80,7 @@ a(); function a() {} } `, - parserOptions: { ecmaVersion: 6 } + parserOptions }, { code: ` @@ -98,7 +91,7 @@ a(); } `, options: ['nofunc'], - parserOptions: { ecmaVersion: 6 } + parserOptions }, { code: ` @@ -111,7 +104,7 @@ switch (foo) { } } `, - parserOptions: { ecmaVersion: 6 } + parserOptions }, { code: ` @@ -120,7 +113,7 @@ a(); let a = function () {}; } `, - parserOptions: { ecmaVersion: 6 } + parserOptions }, // object style options @@ -142,7 +135,7 @@ function a() { } `, options: [{ functions: false }], - parserOptions: { ecmaVersion: 6 } + parserOptions }, { code: ` @@ -152,7 +145,7 @@ function foo() { class A {}; `, options: [{ classes: false }], - parserOptions: { ecmaVersion: 6 } + parserOptions }, // "variables" option @@ -171,7 +164,7 @@ var foo = () => bar; var bar; `, options: [{ variables: false }], - parserOptions: { ecmaVersion: 6 } + parserOptions }, // "typedefs" option @@ -190,7 +183,7 @@ var alias = Test; class Test {} `, - parserOptions: { ecmaVersion: 6 }, + parserOptions, options: [{ classes: false }] }, { @@ -232,8 +225,11 @@ var a=19; parserOptions: { sourceType: 'module' }, errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier } ] }, @@ -242,11 +238,14 @@ var a=19; a++; var a=19; `, - parserOptions: { parserOptions: { ecmaVersion: 6 } }, + parserOptions, errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier } ] }, @@ -257,8 +256,11 @@ var a=19; `, errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier } ] }, @@ -269,8 +271,11 @@ var a=function() {}; `, errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier } ] }, @@ -281,8 +286,11 @@ var a=[1,3]; `, errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier } ] }, @@ -297,12 +305,18 @@ function a() { `, errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier }, { - message: "'b' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'b' + }, + type: AST_NODE_TYPES.Identifier } ] }, @@ -315,8 +329,11 @@ var a=function() {}; options: ['nofunc'], errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier } ] }, @@ -329,11 +346,14 @@ var a=function() {}; } )(); `, - parserOptions: { ecmaVersion: 6 }, + parserOptions, errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier } ] }, @@ -344,11 +364,14 @@ var a=function() {}; )(); function a() { } `, - parserOptions: { ecmaVersion: 6 }, + parserOptions, errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier } ] }, @@ -363,8 +386,11 @@ a(); parser: 'espree', errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier } ] }, @@ -379,8 +405,11 @@ try { `, errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier } ] }, @@ -389,11 +418,14 @@ try { var f = () => a; var a; `, - parserOptions: { ecmaVersion: 6 }, + parserOptions, errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier } ] }, @@ -402,11 +434,14 @@ var a; new A(); class A {}; `, - parserOptions: { ecmaVersion: 6 }, + parserOptions, errors: [ { - message: "'A' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'A' + }, + type: AST_NODE_TYPES.Identifier } ] }, @@ -417,11 +452,14 @@ function foo() { } class A {}; `, - parserOptions: { ecmaVersion: 6 }, + parserOptions, errors: [ { - message: "'A' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'A' + }, + type: AST_NODE_TYPES.Identifier } ] }, @@ -430,11 +468,14 @@ class A {}; new A(); var A = class {}; `, - parserOptions: { ecmaVersion: 6 }, + parserOptions, errors: [ { - message: "'A' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'A' + }, + type: AST_NODE_TYPES.Identifier } ] }, @@ -445,11 +486,14 @@ function foo() { } var A = class {}; `, - parserOptions: { ecmaVersion: 6 }, + parserOptions, errors: [ { - message: "'A' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'A' + }, + type: AST_NODE_TYPES.Identifier } ] }, @@ -462,11 +506,14 @@ a++; var a; } `, - parserOptions: { ecmaVersion: 6 }, + parserOptions, errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier } ] }, @@ -478,11 +525,14 @@ a++; function a() {} } `, - parserOptions: { ecmaVersion: 6 }, + parserOptions, errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier } ] }, @@ -493,11 +543,14 @@ a++; let a = 1 } `, - parserOptions: { ecmaVersion: 6 }, + parserOptions, errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier } ] }, @@ -510,11 +563,14 @@ switch (foo) { let a; } `, - parserOptions: { ecmaVersion: 6 }, + parserOptions, errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier } ] }, @@ -527,11 +583,14 @@ if (true) { let a; } `, - parserOptions: { ecmaVersion: 6 }, + parserOptions, errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier } ] }, @@ -545,8 +604,11 @@ var a=function() {}; options: [{ functions: false, classes: false }], errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier } ] }, @@ -556,11 +618,14 @@ new A(); var A = class {}; `, options: [{ classes: false }], - parserOptions: { ecmaVersion: 6 }, + parserOptions, errors: [ { - message: "'A' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'A' + }, + type: AST_NODE_TYPES.Identifier } ] }, @@ -572,11 +637,14 @@ function foo() { var A = class {}; `, options: [{ classes: false }], - parserOptions: { ecmaVersion: 6 }, + parserOptions, errors: [ { - message: "'A' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'A' + }, + type: AST_NODE_TYPES.Identifier } ] }, @@ -586,98 +654,128 @@ var A = class {}; code: 'var a = a;', errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier } ] }, { code: 'let a = a + b;', - parserOptions: { ecmaVersion: 6 }, + parserOptions, errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier } ] }, { code: 'const a = foo(a);', - parserOptions: { ecmaVersion: 6 }, + parserOptions, errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier } ] }, { code: 'function foo(a = a) {}', - parserOptions: { ecmaVersion: 6 }, + parserOptions, errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier } ] }, { code: 'var {a = a} = [];', - parserOptions: { ecmaVersion: 6 }, + parserOptions, errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier } ] }, { code: 'var [a = a] = [];', - parserOptions: { ecmaVersion: 6 }, + parserOptions, errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier } ] }, { code: 'var {b = a, a} = {};', - parserOptions: { ecmaVersion: 6 }, + parserOptions, errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier } ] }, { code: 'var [b = a, a] = {};', - parserOptions: { ecmaVersion: 6 }, + parserOptions, errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier } ] }, { code: 'var {a = 0} = a;', - parserOptions: { ecmaVersion: 6 }, + parserOptions, errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier } ] }, { code: 'var [a = 0] = a;', - parserOptions: { ecmaVersion: 6 }, + parserOptions, errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier } ] }, @@ -685,18 +783,24 @@ var A = class {}; code: 'for (var a in a) {}', errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier } ] }, { code: 'for (var a of a) {}', - parserOptions: { ecmaVersion: 6 }, + parserOptions, errors: [ { - message: "'a' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'a' + }, + type: AST_NODE_TYPES.Identifier } ] }, @@ -710,12 +814,15 @@ function foo() { } var bar; `, - parserOptions: { ecmaVersion: 6 }, + parserOptions, options: [{ variables: false }], errors: [ { - message: "'bar' was used before it was defined.", - type: 'Identifier' + messageId: 'noUseBeforeDefine', + data: { + name: 'bar' + }, + type: AST_NODE_TYPES.Identifier } ] } diff --git a/packages/eslint-plugin/tests/lib/rules/no-useless-constructor.js b/packages/eslint-plugin/tests/rules/no-useless-constructor.test.ts similarity index 89% rename from packages/eslint-plugin/tests/lib/rules/no-useless-constructor.js rename to packages/eslint-plugin/tests/rules/no-useless-constructor.test.ts index c73c5034f12..631e8e23cc4 100644 --- a/packages/eslint-plugin/tests/lib/rules/no-useless-constructor.js +++ b/packages/eslint-plugin/tests/rules/no-useless-constructor.test.ts @@ -1,11 +1,5 @@ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/no-useless-constructor'), - RuleTester = require('eslint').RuleTester; +import rule from '../../src/rules/no-useless-constructor'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parserOptions: { @@ -15,7 +9,11 @@ const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' }); -const error = { message: 'Useless constructor.', type: 'MethodDefinition' }; +// the base rule doesn't use a message id... +const error: any = { + message: 'Useless constructor.', + type: 'MethodDefinition' +}; ruleTester.run('no-useless-constructor', rule, { valid: [ diff --git a/packages/eslint-plugin/tests/rules/no-var-requires.test.ts b/packages/eslint-plugin/tests/rules/no-var-requires.test.ts new file mode 100644 index 00000000000..adcce58d920 --- /dev/null +++ b/packages/eslint-plugin/tests/rules/no-var-requires.test.ts @@ -0,0 +1,42 @@ +import rule from '../../src/rules/no-var-requires'; +import { RuleTester } from '../RuleTester'; + +const ruleTester = new RuleTester({ + parser: '@typescript-eslint/parser' +}); + +ruleTester.run('no-var-requires', rule, { + valid: ["import foo = require('foo')", "require('foo')"], + invalid: [ + { + code: "var foo = require('foo')", + errors: [ + { + messageId: 'noVarReqs', + line: 1, + column: 11 + } + ] + }, + { + code: "const foo = require('foo')", + errors: [ + { + messageId: 'noVarReqs', + line: 1, + column: 13 + } + ] + }, + { + code: "let foo = require('foo')", + errors: [ + { + messageId: 'noVarReqs', + line: 1, + column: 11 + } + ] + } + ] +}); diff --git a/packages/eslint-plugin/tests/lib/rules/prefer-function-type.js b/packages/eslint-plugin/tests/rules/prefer-function-type.test.ts similarity index 68% rename from packages/eslint-plugin/tests/lib/rules/prefer-function-type.js rename to packages/eslint-plugin/tests/rules/prefer-function-type.test.ts index d0587afcaf7..376f687188a 100644 --- a/packages/eslint-plugin/tests/lib/rules/prefer-function-type.js +++ b/packages/eslint-plugin/tests/rules/prefer-function-type.test.ts @@ -1,25 +1,11 @@ -/** - * @fileoverview Use function types instead of interfaces with call signatures - * @author Benjamin Lichtman - */ -'use strict'; +import { AST_NODE_TYPES } from '@typescript-eslint/typescript-estree'; +import rule from '../../src/rules/prefer-function-type'; +import { RuleTester } from '../RuleTester'; -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -var rule = require('../../../lib/rules/prefer-function-type'), - RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const parserOptions = { - ecmaVersion: 2015 -}; var ruleTester = new RuleTester({ - parserOptions, + parserOptions: { + ecmaVersion: 2015 + }, parser: '@typescript-eslint/parser' }); ruleTester.run('prefer-function-type', rule, { @@ -63,7 +49,7 @@ interface Foo { errors: [ { messageId: 'functionTypeOverCallableType', - type: 'TSCallSignatureDeclaration' + type: AST_NODE_TYPES.TSCallSignatureDeclaration } ], output: ` @@ -77,7 +63,7 @@ type Foo = { errors: [ { messageId: 'functionTypeOverCallableType', - type: 'TSCallSignatureDeclaration' + type: AST_NODE_TYPES.TSCallSignatureDeclaration } ], output: ` @@ -91,7 +77,7 @@ function foo(bar: { (s: string): number }): number { errors: [ { messageId: 'functionTypeOverCallableType', - type: 'TSCallSignatureDeclaration' + type: AST_NODE_TYPES.TSCallSignatureDeclaration } ], output: ` @@ -107,7 +93,7 @@ function foo(bar: { (s: string): number } | undefined): number { errors: [ { messageId: 'functionTypeOverCallableType', - type: 'TSCallSignatureDeclaration' + type: AST_NODE_TYPES.TSCallSignatureDeclaration } ], output: ` @@ -123,7 +109,7 @@ interface Foo extends Function { errors: [ { messageId: 'functionTypeOverCallableType', - type: 'TSCallSignatureDeclaration' + type: AST_NODE_TYPES.TSCallSignatureDeclaration } ], output: ` @@ -137,7 +123,7 @@ interface Foo { errors: [ { messageId: 'functionTypeOverCallableType', - type: 'TSCallSignatureDeclaration' + type: AST_NODE_TYPES.TSCallSignatureDeclaration } ], output: ` diff --git a/packages/eslint-plugin/tests/lib/rules/prefer-interface.js b/packages/eslint-plugin/tests/rules/prefer-interface.test.ts similarity index 67% rename from packages/eslint-plugin/tests/lib/rules/prefer-interface.js rename to packages/eslint-plugin/tests/rules/prefer-interface.test.ts index 1617cdbe687..6519b92d3d2 100644 --- a/packages/eslint-plugin/tests/lib/rules/prefer-interface.js +++ b/packages/eslint-plugin/tests/rules/prefer-interface.test.ts @@ -1,19 +1,5 @@ -/** - * @fileoverview Prefer an interface declaration over a type literal (type T = { ... }) - * @author Armano - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/prefer-interface'), - RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +import rule from '../../src/rules/prefer-interface'; +import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' diff --git a/packages/eslint-plugin/tests/rules/prefer-namespace-keyword.test.ts b/packages/eslint-plugin/tests/rules/prefer-namespace-keyword.test.ts new file mode 100644 index 00000000000..a68c19de2e3 --- /dev/null +++ b/packages/eslint-plugin/tests/rules/prefer-namespace-keyword.test.ts @@ -0,0 +1,64 @@ +import rule from '../../src/rules/prefer-namespace-keyword'; +import { RuleTester } from '../RuleTester'; + +const ruleTester = new RuleTester({ + parser: '@typescript-eslint/parser' +}); + +ruleTester.run('prefer-namespace-keyword', rule, { + valid: [ + "declare module 'foo'", + "declare module 'foo' { }", + 'namespace foo { }', + 'declare namespace foo { }', + 'declare global { }' + ], + invalid: [ + { + code: 'module foo { }', + output: 'namespace foo { }', + errors: [ + { + messageId: 'useNamespace', + line: 1, + column: 1 + } + ] + }, + { + code: 'declare module foo { }', + output: 'declare namespace foo { }', + errors: [ + { + messageId: 'useNamespace', + line: 1, + column: 1 + } + ] + }, + { + code: ` +declare module foo { + declare module bar { } +} + `, + output: ` +declare namespace foo { + declare namespace bar { } +} + `, + errors: [ + { + messageId: 'useNamespace', + line: 2, + column: 1 + }, + { + messageId: 'useNamespace', + line: 3, + column: 5 + } + ] + } + ] +}); diff --git a/packages/eslint-plugin/tests/lib/rules/promise-function-async.js b/packages/eslint-plugin/tests/rules/promise-function-async.test.ts similarity index 82% rename from packages/eslint-plugin/tests/lib/rules/promise-function-async.js rename to packages/eslint-plugin/tests/rules/promise-function-async.test.ts index dddb2604643..71f93d669bf 100644 --- a/packages/eslint-plugin/tests/lib/rules/promise-function-async.js +++ b/packages/eslint-plugin/tests/rules/promise-function-async.test.ts @@ -1,22 +1,7 @@ -/** - * @fileoverview Requires any function or method that returns a Promise to be marked async - * @author Josh Goldberg - */ -'use strict'; +import rule from '../../src/rules/promise-function-async'; +import { RuleTester, getFixturesRootDir } from '../RuleTester'; -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/promise-function-async'), - RuleTester = require('eslint').RuleTester, - path = require('path'); - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const rootDir = path.join(process.cwd(), 'tests/fixtures/'); +const rootDir = getFixturesRootDir(); const parserOptions = { ecmaVersion: 2018, tsconfigRootDir: rootDir, @@ -34,12 +19,17 @@ ruleTester.run('promise-function-async', rule, { valid: [ ` const nonAsyncNonPromiseArrowFunction = (n: number) => n; - + `, + ` function nonAsyncNonPromiseFunctionDeclaration(n: number) { return n; } - + `, + ` const asyncPromiseFunctionExpressionA = async function(p: Promise) { return p; }; + `, + ` const asyncPromiseFunctionExpressionB = async function() { return new Promise(); }; - + `, + ` class Test { public nonAsyncNonPromiseArrowFunction = (n: number) => n; @@ -55,23 +45,71 @@ class Test { return new Promise(); } } -` + ` ], invalid: [ { code: ` const nonAsyncPromiseFunctionExpressionA = function(p: Promise) { return p; }; - + `, + errors: [ + { + messageId + } + ] + }, + { + code: ` const nonAsyncPromiseFunctionExpressionB = function() { return new Promise(); }; - + `, + errors: [ + { + messageId + } + ] + }, + { + code: ` function nonAsyncPromiseFunctionDeclarationA(p: Promise) { return p; } - + `, + errors: [ + { + messageId + } + ] + }, + { + code: ` function nonAsyncPromiseFunctionDeclarationB() { return new Promise(); } - + `, + errors: [ + { + messageId + } + ] + }, + { + code: ` const nonAsyncPromiseArrowFunctionA = (p: Promise) => p; - + `, + errors: [ + { + messageId + } + ] + }, + { + code: ` const nonAsyncPromiseArrowFunctionB = () => new Promise(); - + `, + errors: [ + { + messageId + } + ] + }, + { + code: ` class Test { public nonAsyncPromiseMethodA(p: Promise) { return p; @@ -81,38 +119,14 @@ class Test { return new Promise(); } } -`, + `, errors: [ { - line: 2, - messageId - }, - { - line: 4, - messageId - }, - { - line: 6, - messageId - }, - { - line: 8, - messageId - }, - { - line: 10, - messageId - }, - { - line: 12, - messageId - }, - { - line: 15, + line: 3, messageId }, { - line: 19, + line: 7, messageId } ] diff --git a/packages/eslint-plugin/tests/lib/rules/restrict-plus-operands.js b/packages/eslint-plugin/tests/rules/restrict-plus-operands.test.ts similarity index 87% rename from packages/eslint-plugin/tests/lib/rules/restrict-plus-operands.js rename to packages/eslint-plugin/tests/rules/restrict-plus-operands.test.ts index d5a07e59698..44850866618 100644 --- a/packages/eslint-plugin/tests/lib/rules/restrict-plus-operands.js +++ b/packages/eslint-plugin/tests/rules/restrict-plus-operands.test.ts @@ -1,21 +1,6 @@ -/** - * @fileoverview When adding two variables, operands must both be of type number or of type string. - * @author James Henry - * @author Armano - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ -const path = require('path'); - -const rule = require('../../../lib/rules/restrict-plus-operands'), - RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +import path from 'path'; +import rule from '../../src/rules/restrict-plus-operands'; +import { RuleTester } from '../RuleTester'; const rootPath = path.join(process.cwd(), 'tests/fixtures/'); diff --git a/packages/eslint-plugin/tests/lib/rules/type-annotation-spacing.js b/packages/eslint-plugin/tests/rules/type-annotation-spacing.test.ts similarity index 77% rename from packages/eslint-plugin/tests/lib/rules/type-annotation-spacing.js rename to packages/eslint-plugin/tests/rules/type-annotation-spacing.test.ts index b5826a54e29..df0de30eaa0 100644 --- a/packages/eslint-plugin/tests/lib/rules/type-annotation-spacing.js +++ b/packages/eslint-plugin/tests/rules/type-annotation-spacing.test.ts @@ -1,19 +1,12 @@ -/** - * @fileoverview Enforces spacing around type annotations - * @author Nicholas C. Zakas - */ -'use strict'; +import rule from '../../src/rules/type-annotation-spacing'; +import { RuleTester, InvalidTestCase, ValidTestCase } from '../RuleTester'; +import { + InferMessageIdsTypeFromRule, + InferOptionsTypeFromRule +} from '../../src/util'; -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/type-annotation-spacing'), - RuleTester = require('eslint').RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ +type MessageIds = InferMessageIdsTypeFromRule; +type Options = InferOptionsTypeFromRule; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' @@ -1044,7 +1037,8 @@ type Bar = Record output: 'let foo: string;', errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 1, column: 9 } @@ -1055,7 +1049,8 @@ type Bar = Record output: 'function foo(): string {}', errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 1, column: 16 } @@ -1066,7 +1061,8 @@ type Bar = Record output: 'function foo(a: string) {}', errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 1, column: 16 } @@ -1085,7 +1081,8 @@ class Foo { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 10 } @@ -1104,7 +1101,8 @@ class Foo { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 25 } @@ -1123,7 +1121,8 @@ class Foo { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 13 } @@ -1142,12 +1141,14 @@ class Foo { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 16 }, { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 26 } @@ -1166,7 +1167,8 @@ interface Foo { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 10 } @@ -1185,7 +1187,8 @@ interface Foo { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 13 } @@ -1204,12 +1207,14 @@ interface Foo { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 16 }, { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 26 } @@ -1228,7 +1233,8 @@ type Foo = { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 10 } @@ -1247,7 +1253,8 @@ type Foo = { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 13 } @@ -1266,12 +1273,14 @@ type Foo = { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 16 }, { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 26 } @@ -1282,7 +1291,8 @@ type Foo = { output: 'type Foo = (name: string) => string;', errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 1, column: 18 } @@ -1293,12 +1303,14 @@ type Foo = { output: 'type Foo = (name: string) => string;', errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 1, column: 18 }, { - message: "Expected a space before the '=>'.", + messageId: 'expectedSpaceBefore', + data: { type: '=>' }, line: 1, column: 27 } @@ -1317,7 +1329,8 @@ type Foo = { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 18 } @@ -1336,12 +1349,14 @@ type Foo = { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 18 }, { - message: "Expected a space before the '=>'.", + messageId: 'expectedSpaceBefore', + data: { type: '=>' }, line: 3, column: 27 } @@ -1353,7 +1368,8 @@ type Foo = { output: 'let foo: string;', errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 1, column: 9 } @@ -1365,7 +1381,8 @@ type Foo = { output: 'function foo(): string {}', errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 1, column: 16 } @@ -1377,7 +1394,8 @@ type Foo = { output: 'function foo(a: string) {}', errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 1, column: 16 } @@ -1397,7 +1415,8 @@ class Foo { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 10 } @@ -1417,7 +1436,8 @@ class Foo { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 25 } @@ -1437,7 +1457,8 @@ class Foo { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 13 } @@ -1457,12 +1478,14 @@ class Foo { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 16 }, { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 26 } @@ -1482,7 +1505,8 @@ interface Foo { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 10 } @@ -1502,7 +1526,8 @@ interface Foo { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 13 } @@ -1522,12 +1547,14 @@ interface Foo { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 16 }, { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 26 } @@ -1547,7 +1574,8 @@ type Foo = { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 10 } @@ -1567,7 +1595,8 @@ type Foo = { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 13 } @@ -1587,12 +1616,14 @@ type Foo = { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 16 }, { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 26 } @@ -1604,7 +1635,8 @@ type Foo = { output: 'type Foo = (name: string) => string;', errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 1, column: 18 } @@ -1616,12 +1648,14 @@ type Foo = { output: 'type Foo = (name: string) => string;', errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 1, column: 18 }, { - message: "Expected a space before the '=>'.", + messageId: 'expectedSpaceBefore', + data: { type: '=>' }, line: 1, column: 27 } @@ -1641,7 +1675,8 @@ type Foo = { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 18 } @@ -1661,12 +1696,14 @@ type Foo = { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 18 }, { - message: "Expected a space before the '=>'.", + messageId: 'expectedSpaceBefore', + data: { type: '=>' }, line: 3, column: 27 } @@ -1678,7 +1715,8 @@ type Foo = { output: 'let foo: string;', errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 1, column: 9 } @@ -1690,7 +1728,8 @@ type Foo = { output: 'function foo(): string {}', errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 1, column: 16 } @@ -1702,7 +1741,8 @@ type Foo = { output: 'function foo(a: string) {}', errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 1, column: 16 } @@ -1722,7 +1762,8 @@ class Foo { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 10 } @@ -1742,7 +1783,8 @@ class Foo { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 25 } @@ -1762,7 +1804,8 @@ class Foo { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 13 } @@ -1782,12 +1825,14 @@ class Foo { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 16 }, { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 26 } @@ -1807,7 +1852,8 @@ interface Foo { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 10 } @@ -1827,7 +1873,8 @@ interface Foo { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 13 } @@ -1847,12 +1894,14 @@ interface Foo { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 16 }, { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 26 } @@ -1872,7 +1921,8 @@ type Foo = { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 10 } @@ -1892,7 +1942,8 @@ type Foo = { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 13 } @@ -1912,12 +1963,14 @@ type Foo = { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 16 }, { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 26 } @@ -1929,12 +1982,14 @@ type Foo = { output: 'type Foo = (name: string)=> string;', errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 1, column: 18 }, { - message: "Unexpected space before the '=>'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '=>' }, line: 1, column: 28 } @@ -1946,7 +2001,8 @@ type Foo = { output: 'type Foo = (name: string)=> string;', errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 1, column: 18 } @@ -1966,12 +2022,14 @@ type Foo = { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 18 }, { - message: "Unexpected space before the '=>'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '=>' }, line: 3, column: 28 } @@ -1991,7 +2049,8 @@ type Foo = { `, errors: [ { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 18 } @@ -2003,12 +2062,14 @@ type Foo = { output: 'let foo : string;', errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 1, column: 8 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 1, column: 8 } @@ -2020,12 +2081,14 @@ type Foo = { output: 'function foo() : string {}', errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 1, column: 15 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 1, column: 15 } @@ -2037,12 +2100,14 @@ type Foo = { output: 'function foo(a : string) {}', errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 1, column: 15 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 1, column: 15 } @@ -2062,12 +2127,14 @@ class Foo { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 9 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 9 } @@ -2087,12 +2154,14 @@ class Foo { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 24 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 24 } @@ -2112,12 +2181,14 @@ class Foo { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 12 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 12 } @@ -2137,22 +2208,26 @@ class Foo { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 15 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 15 }, { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 23 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 23 } @@ -2172,12 +2247,14 @@ interface Foo { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 9 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 9 } @@ -2197,12 +2274,14 @@ interface Foo { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 12 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 12 } @@ -2222,22 +2301,26 @@ interface Foo { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 15 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 15 }, { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 23 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 23 } @@ -2257,12 +2340,14 @@ type Foo = { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 9 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 9 } @@ -2282,12 +2367,14 @@ type Foo = { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 12 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 12 } @@ -2307,22 +2394,26 @@ type Foo = { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 15 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 15 }, { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 23 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 23 } @@ -2334,12 +2425,14 @@ type Foo = { output: 'type Foo = (name : string) => string;', errors: [ { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 1, column: 17 }, { - message: "Expected a space before the '=>'.", + messageId: 'expectedSpaceBefore', + data: { type: '=>' }, line: 1, column: 26 } @@ -2351,7 +2444,8 @@ type Foo = { output: 'type Foo = (name : string) => string;', errors: [ { - message: "Expected a space before the '=>'.", + messageId: 'expectedSpaceBefore', + data: { type: '=>' }, line: 1, column: 27 } @@ -2371,17 +2465,20 @@ type Foo = { `, errors: [ { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 10 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 17 }, { - message: "Expected a space before the '=>'.", + messageId: 'expectedSpaceBefore', + data: { type: '=>' }, line: 3, column: 26 } @@ -2401,7 +2498,8 @@ type Foo = { `, errors: [ { - message: "Expected a space before the '=>'.", + messageId: 'expectedSpaceBefore', + data: { type: '=>' }, line: 3, column: 28 } @@ -2413,12 +2511,14 @@ type Foo = { output: 'let foo : string;', errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 1, column: 8 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 1, column: 8 } @@ -2430,12 +2530,14 @@ type Foo = { output: 'function foo() : string {}', errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 1, column: 15 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 1, column: 15 } @@ -2447,12 +2549,14 @@ type Foo = { output: 'function foo(a : string) {}', errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 1, column: 15 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 1, column: 15 } @@ -2472,12 +2576,14 @@ class Foo { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 9 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 9 } @@ -2497,12 +2603,14 @@ class Foo { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 24 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 24 } @@ -2522,12 +2630,14 @@ class Foo { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 12 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 12 } @@ -2547,22 +2657,26 @@ class Foo { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 15 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 15 }, { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 23 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 23 } @@ -2582,12 +2696,14 @@ interface Foo { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 9 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 9 } @@ -2607,12 +2723,14 @@ interface Foo { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 12 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 12 } @@ -2632,22 +2750,26 @@ interface Foo { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 15 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 15 }, { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 23 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 23 } @@ -2667,12 +2789,14 @@ type Foo = { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 9 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 9 } @@ -2692,12 +2816,14 @@ type Foo = { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 12 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 12 } @@ -2717,22 +2843,26 @@ type Foo = { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 15 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 15 }, { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 23 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 23 } @@ -2744,12 +2874,14 @@ type Foo = { output: 'type Foo = (name : string) => string;', errors: [ { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 1, column: 17 }, { - message: "Expected a space before the '=>'.", + messageId: 'expectedSpaceBefore', + data: { type: '=>' }, line: 1, column: 26 } @@ -2761,7 +2893,8 @@ type Foo = { output: 'type Foo = (name : string) => string;', errors: [ { - message: "Expected a space before the '=>'.", + messageId: 'expectedSpaceBefore', + data: { type: '=>' }, line: 1, column: 27 } @@ -2781,17 +2914,20 @@ type Foo = { `, errors: [ { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 10 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 17 }, { - message: "Expected a space before the '=>'.", + messageId: 'expectedSpaceBefore', + data: { type: '=>' }, line: 3, column: 26 } @@ -2811,7 +2947,8 @@ type Foo = { `, errors: [ { - message: "Expected a space before the '=>'.", + messageId: 'expectedSpaceBefore', + data: { type: '=>' }, line: 3, column: 28 } @@ -2829,12 +2966,14 @@ type Foo = { output: 'let foo : string;', errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 1, column: 8 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 1, column: 8 } @@ -2852,12 +2991,14 @@ type Foo = { output: 'function foo() : string {}', errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 1, column: 15 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 1, column: 15 } @@ -2875,12 +3016,14 @@ type Foo = { output: 'function foo(a : string) {}', errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 1, column: 15 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 1, column: 15 } @@ -2906,12 +3049,14 @@ class Foo { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 9 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 9 } @@ -2937,12 +3082,14 @@ class Foo { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 24 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 24 } @@ -2968,12 +3115,14 @@ class Foo { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 12 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 12 } @@ -2999,22 +3148,26 @@ class Foo { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 15 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 15 }, { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 23 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 23 } @@ -3040,12 +3193,14 @@ interface Foo { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 9 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 9 } @@ -3071,12 +3226,14 @@ interface Foo { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 12 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 12 } @@ -3102,22 +3259,26 @@ interface Foo { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 15 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 15 }, { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 23 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 23 } @@ -3143,12 +3304,14 @@ type Foo = { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 9 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 9 } @@ -3174,12 +3337,14 @@ type Foo = { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 12 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 12 } @@ -3205,22 +3370,26 @@ type Foo = { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 15 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 15 }, { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 23 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 23 } @@ -3238,12 +3407,14 @@ type Foo = { output: 'type Foo = (name : string)=>string;', errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 1, column: 17 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 1, column: 17 } @@ -3269,12 +3440,14 @@ type Foo = { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 18 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 18 } @@ -3301,22 +3474,26 @@ type Foo = { output: 'type Foo = (name : string) => string;', errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 1, column: 17 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 1, column: 17 }, { - message: "Expected a space after the '=>'.", + messageId: 'expectedSpaceAfter', + data: { type: '=>' }, line: 1, column: 25 }, { - message: "Expected a space before the '=>'.", + messageId: 'expectedSpaceBefore', + data: { type: '=>' }, line: 1, column: 25 } @@ -3351,22 +3528,26 @@ type Foo = { `, errors: [ { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 18 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 18 }, { - message: "Expected a space after the '=>'.", + messageId: 'expectedSpaceAfter', + data: { type: '=>' }, line: 3, column: 26 }, { - message: "Expected a space before the '=>'.", + messageId: 'expectedSpaceBefore', + data: { type: '=>' }, line: 3, column: 26 } @@ -3391,47 +3572,74 @@ type Foo = { `, errors: [ { - message: `Expected a space before the ':'.`, + messageId: 'expectedSpaceBefore', + data: { + type: ':' + }, line: 3, column: 30 }, { - message: `Expected a space before the ':'.`, + messageId: 'expectedSpaceBefore', + data: { + type: ':' + }, line: 3, column: 45 }, { - message: `Expected a space before the ':'.`, + messageId: 'expectedSpaceBefore', + data: { + type: ':' + }, line: 3, column: 52 }, { - message: `Expected a space before the ':'.`, + messageId: 'expectedSpaceBefore', + data: { + type: ':' + }, line: 4, column: 47 }, { - message: `Expected a space before the ':'.`, + messageId: 'expectedSpaceBefore', + data: { + type: ':' + }, line: 4, column: 62 }, { - message: `Expected a space before the ':'.`, + messageId: 'expectedSpaceBefore', + data: { + type: ':' + }, line: 4, column: 69 }, { - message: `Expected a space before the ':'.`, + messageId: 'expectedSpaceBefore', + data: { + type: ':' + }, line: 5, column: 45 }, { - message: `Expected a space before the ':'.`, + messageId: 'expectedSpaceBefore', + data: { + type: ':' + }, line: 5, column: 60 }, { - message: `Expected a space before the ':'.`, + messageId: 'expectedSpaceBefore', + data: { + type: ':' + }, line: 5, column: 67 } @@ -4228,7 +4436,8 @@ type Bar = Record output: 'function foo(a?: string) {}', errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 1, column: 16 } @@ -4247,7 +4456,8 @@ class Foo { `, errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 10 } @@ -4266,7 +4476,8 @@ class Foo { `, errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 25 } @@ -4285,12 +4496,14 @@ class Foo { `, errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 16 }, { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 27 } @@ -4309,7 +4522,8 @@ interface Foo { `, errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 10 } @@ -4328,12 +4542,14 @@ interface Foo { `, errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 16 }, { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 27 } @@ -4352,7 +4568,8 @@ type Foo = { `, errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 10 } @@ -4371,12 +4588,14 @@ type Foo = { `, errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 16 }, { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 27 } @@ -4387,7 +4606,8 @@ type Foo = { output: 'type Foo = (name?: string) => string;', errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 1, column: 18 } @@ -4398,12 +4618,14 @@ type Foo = { output: 'type Foo = (name?: string) => string;', errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 1, column: 18 }, { - message: "Expected a space before the '=>'.", + messageId: 'expectedSpaceBefore', + data: { type: '=>' }, line: 1, column: 28 } @@ -4422,7 +4644,8 @@ type Foo = { `, errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 18 } @@ -4441,12 +4664,14 @@ type Foo = { `, errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 18 }, { - message: "Expected a space before the '=>'.", + messageId: 'expectedSpaceBefore', + data: { type: '=>' }, line: 3, column: 28 } @@ -4458,7 +4683,8 @@ type Foo = { output: 'function foo(a?: string) {}', errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 1, column: 16 } @@ -4478,7 +4704,8 @@ class Foo { `, errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 10 } @@ -4498,7 +4725,8 @@ class Foo { `, errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 25 } @@ -4518,12 +4746,14 @@ class Foo { `, errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 16 }, { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 27 } @@ -4543,7 +4773,8 @@ interface Foo { `, errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 10 } @@ -4563,12 +4794,14 @@ interface Foo { `, errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 16 }, { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 27 } @@ -4588,7 +4821,8 @@ type Foo = { `, errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 10 } @@ -4608,12 +4842,14 @@ type Foo = { `, errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 16 }, { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 27 } @@ -4625,7 +4861,8 @@ type Foo = { output: 'type Foo = (name?: string) => string;', errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 1, column: 18 } @@ -4637,12 +4874,14 @@ type Foo = { output: 'type Foo = (name?: string) => string;', errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 1, column: 18 }, { - message: "Expected a space before the '=>'.", + messageId: 'expectedSpaceBefore', + data: { type: '=>' }, line: 1, column: 28 } @@ -4662,7 +4901,8 @@ type Foo = { `, errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 18 } @@ -4682,12 +4922,14 @@ type Foo = { `, errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 18 }, { - message: "Expected a space before the '=>'.", + messageId: 'expectedSpaceBefore', + data: { type: '=>' }, line: 3, column: 28 } @@ -4699,7 +4941,8 @@ type Foo = { output: 'function foo(a?: string) {}', errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 1, column: 16 } @@ -4719,7 +4962,8 @@ class Foo { `, errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 10 } @@ -4739,7 +4983,8 @@ class Foo { `, errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 25 } @@ -4759,12 +5004,14 @@ class Foo { `, errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 16 }, { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 27 } @@ -4784,7 +5031,8 @@ interface Foo { `, errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 10 } @@ -4804,12 +5052,14 @@ interface Foo { `, errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 16 }, { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 27 } @@ -4829,7 +5079,8 @@ type Foo = { `, errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 10 } @@ -4849,12 +5100,14 @@ type Foo = { `, errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 16 }, { - message: "Unexpected space before the ':'.", + messageId: 'unexpectedSpaceBefore', + data: { type: ':' }, line: 3, column: 27 } @@ -4866,12 +5119,14 @@ type Foo = { output: 'type Foo = (name?: string)=> string;', errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 1, column: 18 }, { - message: "Unexpected space before the '=>'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '=>' }, line: 1, column: 29 } @@ -4883,7 +5138,8 @@ type Foo = { output: 'type Foo = (name?: string)=> string;', errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 1, column: 18 } @@ -4903,12 +5159,14 @@ type Foo = { `, errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 18 }, { - message: "Unexpected space before the '=>'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '=>' }, line: 3, column: 29 } @@ -4928,7 +5186,8 @@ type Foo = { `, errors: [ { - message: "Unexpected space before the '?:'.", + messageId: 'unexpectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 18 } @@ -4940,12 +5199,14 @@ type Foo = { output: 'function foo(a ?: string) {}', errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 1, column: 15 }, { - message: "Expected a space after the '?:'.", + messageId: 'expectedSpaceAfter', + data: { type: '?:' }, line: 1, column: 16 } @@ -4965,12 +5226,14 @@ class Foo { `, errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 9 }, { - message: "Expected a space after the '?:'.", + messageId: 'expectedSpaceAfter', + data: { type: '?:' }, line: 3, column: 10 } @@ -4990,12 +5253,14 @@ class Foo { `, errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 24 }, { - message: "Expected a space after the '?:'.", + messageId: 'expectedSpaceAfter', + data: { type: '?:' }, line: 3, column: 25 } @@ -5015,22 +5280,26 @@ class Foo { `, errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 15 }, { - message: "Expected a space after the '?:'.", + messageId: 'expectedSpaceAfter', + data: { type: '?:' }, line: 3, column: 16 }, { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 24 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 24 } @@ -5050,12 +5319,14 @@ interface Foo { `, errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 9 }, { - message: "Expected a space after the '?:'.", + messageId: 'expectedSpaceAfter', + data: { type: '?:' }, line: 3, column: 10 } @@ -5075,22 +5346,26 @@ interface Foo { `, errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 15 }, { - message: "Expected a space after the '?:'.", + messageId: 'expectedSpaceAfter', + data: { type: '?:' }, line: 3, column: 16 }, { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 24 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 24 } @@ -5110,12 +5385,14 @@ type Foo = { `, errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 9 }, { - message: "Expected a space after the '?:'.", + messageId: 'expectedSpaceAfter', + data: { type: '?:' }, line: 3, column: 10 } @@ -5135,22 +5412,26 @@ type Foo = { `, errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 15 }, { - message: "Expected a space after the '?:'.", + messageId: 'expectedSpaceAfter', + data: { type: '?:' }, line: 3, column: 16 }, { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 24 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 24 } @@ -5162,12 +5443,14 @@ type Foo = { output: 'type Foo = (name ?: string) => string;', errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 1, column: 17 }, { - message: "Expected a space before the '=>'.", + messageId: 'expectedSpaceBefore', + data: { type: '=>' }, line: 1, column: 27 } @@ -5179,7 +5462,8 @@ type Foo = { output: 'type Foo = (name ?: string) => string;', errors: [ { - message: "Expected a space before the '=>'.", + messageId: 'expectedSpaceBefore', + data: { type: '=>' }, line: 1, column: 28 } @@ -5199,17 +5483,20 @@ type Foo = { `, errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 10 }, { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 18 }, { - message: "Expected a space before the '=>'.", + messageId: 'expectedSpaceBefore', + data: { type: '=>' }, line: 3, column: 28 } @@ -5229,7 +5516,8 @@ type Foo = { `, errors: [ { - message: "Expected a space before the '=>'.", + messageId: 'expectedSpaceBefore', + data: { type: '=>' }, line: 3, column: 30 } @@ -5241,12 +5529,14 @@ type Foo = { output: 'function foo(a ?: string) {}', errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 1, column: 15 }, { - message: "Expected a space after the '?:'.", + messageId: 'expectedSpaceAfter', + data: { type: '?:' }, line: 1, column: 16 } @@ -5266,12 +5556,14 @@ class Foo { `, errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 9 }, { - message: "Expected a space after the '?:'.", + messageId: 'expectedSpaceAfter', + data: { type: '?:' }, line: 3, column: 10 } @@ -5291,12 +5583,14 @@ class Foo { `, errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 24 }, { - message: "Expected a space after the '?:'.", + messageId: 'expectedSpaceAfter', + data: { type: '?:' }, line: 3, column: 25 } @@ -5316,22 +5610,26 @@ class Foo { `, errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 15 }, { - message: "Expected a space after the '?:'.", + messageId: 'expectedSpaceAfter', + data: { type: '?:' }, line: 3, column: 16 }, { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 24 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 24 } @@ -5351,12 +5649,14 @@ interface Foo { `, errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 9 }, { - message: "Expected a space after the '?:'.", + messageId: 'expectedSpaceAfter', + data: { type: '?:' }, line: 3, column: 10 } @@ -5376,22 +5676,26 @@ interface Foo { `, errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 15 }, { - message: "Expected a space after the '?:'.", + messageId: 'expectedSpaceAfter', + data: { type: '?:' }, line: 3, column: 16 }, { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 24 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 24 } @@ -5411,12 +5715,14 @@ type Foo = { `, errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 9 }, { - message: "Expected a space after the '?:'.", + messageId: 'expectedSpaceAfter', + data: { type: '?:' }, line: 3, column: 10 } @@ -5436,22 +5742,26 @@ type Foo = { `, errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 15 }, { - message: "Expected a space after the '?:'.", + messageId: 'expectedSpaceAfter', + data: { type: '?:' }, line: 3, column: 16 }, { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 24 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 24 } @@ -5463,12 +5773,14 @@ type Foo = { output: 'type Foo = (name ?: string) => string;', errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 1, column: 17 }, { - message: "Expected a space before the '=>'.", + messageId: 'expectedSpaceBefore', + data: { type: '=>' }, line: 1, column: 27 } @@ -5480,7 +5792,8 @@ type Foo = { output: 'type Foo = (name : string) => string;', errors: [ { - message: "Expected a space before the '=>'.", + messageId: 'expectedSpaceBefore', + data: { type: '=>' }, line: 1, column: 27 } @@ -5500,17 +5813,20 @@ type Foo = { `, errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 10 }, { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 18 }, { - message: "Expected a space before the '=>'.", + messageId: 'expectedSpaceBefore', + data: { type: '=>' }, line: 3, column: 28 } @@ -5530,7 +5846,8 @@ type Foo = { `, errors: [ { - message: "Expected a space before the '=>'.", + messageId: 'expectedSpaceBefore', + data: { type: '=>' }, line: 3, column: 30 } @@ -5548,12 +5865,14 @@ type Foo = { output: 'function foo(a ?: string) {}', errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 1, column: 15 }, { - message: "Expected a space after the '?:'.", + messageId: 'expectedSpaceAfter', + data: { type: '?:' }, line: 1, column: 16 } @@ -5579,12 +5898,14 @@ class Foo { `, errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 9 }, { - message: "Expected a space after the '?:'.", + messageId: 'expectedSpaceAfter', + data: { type: '?:' }, line: 3, column: 10 } @@ -5610,12 +5931,14 @@ class Foo { `, errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 24 }, { - message: "Expected a space after the '?:'.", + messageId: 'expectedSpaceAfter', + data: { type: '?:' }, line: 3, column: 25 } @@ -5641,22 +5964,26 @@ class Foo { `, errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 15 }, { - message: "Expected a space after the '?:'.", + messageId: 'expectedSpaceAfter', + data: { type: '?:' }, line: 3, column: 16 }, { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 24 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 24 } @@ -5682,12 +6009,14 @@ interface Foo { `, errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 9 }, { - message: "Expected a space after the '?:'.", + messageId: 'expectedSpaceAfter', + data: { type: '?:' }, line: 3, column: 10 } @@ -5713,22 +6042,26 @@ interface Foo { `, errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 15 }, { - message: "Expected a space after the '?:'.", + messageId: 'expectedSpaceAfter', + data: { type: '?:' }, line: 3, column: 16 }, { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 24 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 24 } @@ -5754,12 +6087,14 @@ type Foo = { `, errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 9 }, { - message: "Expected a space after the '?:'.", + messageId: 'expectedSpaceAfter', + data: { type: '?:' }, line: 3, column: 10 } @@ -5785,22 +6120,26 @@ type Foo = { `, errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 15 }, { - message: "Expected a space after the '?:'.", + messageId: 'expectedSpaceAfter', + data: { type: '?:' }, line: 3, column: 16 }, { - message: "Expected a space after the ':'.", + messageId: 'expectedSpaceAfter', + data: { type: ':' }, line: 3, column: 24 }, { - message: "Expected a space before the ':'.", + messageId: 'expectedSpaceBefore', + data: { type: ':' }, line: 3, column: 24 } @@ -5818,12 +6157,14 @@ type Foo = { output: 'type Foo = (name ?: string)=>string;', errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 1, column: 17 }, { - message: "Expected a space after the '?:'.", + messageId: 'expectedSpaceAfter', + data: { type: '?:' }, line: 1, column: 18 } @@ -5849,12 +6190,14 @@ type Foo = { `, errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 19 }, { - message: "Expected a space after the '?:'.", + messageId: 'expectedSpaceAfter', + data: { type: '?:' }, line: 3, column: 20 } @@ -5881,22 +6224,26 @@ type Foo = { output: 'type Foo = (name ?: string) => string;', errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 1, column: 17 }, { - message: "Expected a space after the '?:'.", + messageId: 'expectedSpaceAfter', + data: { type: '?:' }, line: 1, column: 18 }, { - message: "Expected a space after the '=>'.", + messageId: 'expectedSpaceAfter', + data: { type: '=>' }, line: 1, column: 26 }, { - message: "Expected a space before the '=>'.", + messageId: 'expectedSpaceBefore', + data: { type: '=>' }, line: 1, column: 26 } @@ -5931,22 +6278,26 @@ type Foo = { `, errors: [ { - message: "Expected a space before the '?:'.", + messageId: 'expectedSpaceBefore', + data: { type: '?:' }, line: 3, column: 19 }, { - message: "Expected a space after the '?:'.", + messageId: 'expectedSpaceAfter', + data: { type: '?:' }, line: 3, column: 20 }, { - message: "Expected a space after the '=>'.", + messageId: 'expectedSpaceAfter', + data: { type: '=>' }, line: 3, column: 28 }, { - message: "Expected a space before the '=>'.", + messageId: 'expectedSpaceBefore', + data: { type: '=>' }, line: 3, column: 28 } @@ -5962,7 +6313,7 @@ type Foo = { const operators = ['+?:', '-?:']; ruleTester.run('type-annotation-spacing', rule, { - valid: operators.reduce( + valid: operators.reduce[]>( (validCases, operator) => validCases.concat([ { @@ -6004,9 +6355,9 @@ ruleTester.run('type-annotation-spacing', rule, { ]), [] ), - invalid: operators.reduce( - (validCases, operator) => - validCases.concat([ + invalid: operators.reduce[]>( + (invalidCases, operator) => + invalidCases.concat([ // space before + after cases { code: `type Foo = { [P in keyof T] ${operator} T[P] }`, @@ -6014,7 +6365,10 @@ ruleTester.run('type-annotation-spacing', rule, { output: `type Foo = { [P in keyof T]${operator} T[P] }`, errors: [ { - message: `Unexpected space before the '${operator}'.`, + messageId: 'unexpectedSpaceBefore', + data: { + type: operator + }, line: 1, column: 32 } @@ -6026,7 +6380,10 @@ ruleTester.run('type-annotation-spacing', rule, { output: `type Foo = { [P in keyof T]${operator} T[P] }`, errors: [ { - message: `Unexpected space before the '${operator}'.`, + messageId: 'unexpectedSpaceBefore', + data: { + type: operator + }, line: 1, column: 32 } @@ -6038,7 +6395,10 @@ ruleTester.run('type-annotation-spacing', rule, { output: `type Foo = { [P in keyof T]${operator} T[P] }`, errors: [ { - message: `Unexpected space before the '${operator}'.`, + messageId: 'unexpectedSpaceBefore', + data: { + type: operator + }, line: 1, column: 32 } @@ -6050,12 +6410,18 @@ ruleTester.run('type-annotation-spacing', rule, { output: `type Foo = { [P in keyof T]${operator}T[P] }`, errors: [ { - message: `Unexpected space before the '${operator}'.`, + messageId: 'unexpectedSpaceBefore', + data: { + type: operator + }, line: 1, column: 32 }, { - message: `Unexpected space after the '${operator}'.`, + messageId: 'unexpectedSpaceAfter', + data: { + type: operator + }, line: 1, column: 34 } @@ -6067,12 +6433,18 @@ ruleTester.run('type-annotation-spacing', rule, { output: `type Foo = { [P in keyof T]${operator}T[P] }`, errors: [ { - message: `Unexpected space before the '${operator}'.`, + messageId: 'unexpectedSpaceBefore', + data: { + type: operator + }, line: 1, column: 32 }, { - message: `Unexpected space after the '${operator}'.`, + messageId: 'unexpectedSpaceAfter', + data: { + type: operator + }, line: 1, column: 34 } @@ -6084,7 +6456,10 @@ ruleTester.run('type-annotation-spacing', rule, { output: `type Foo = { [P in keyof T] ${operator}T[P] }`, errors: [ { - message: `Unexpected space after the '${operator}'.`, + messageId: 'unexpectedSpaceAfter', + data: { + type: operator + }, line: 1, column: 34 } @@ -6097,7 +6472,10 @@ ruleTester.run('type-annotation-spacing', rule, { output: `type Foo = { [P in keyof T]${operator} T[P] }`, errors: [ { - message: `Expected a space after the '${operator}'.`, + messageId: 'expectedSpaceAfter', + data: { + type: operator + }, line: 1, column: 33 } @@ -6109,12 +6487,18 @@ ruleTester.run('type-annotation-spacing', rule, { output: `type Foo = { [P in keyof T] ${operator} T[P] }`, errors: [ { - message: `Expected a space before the '${operator}'.`, + messageId: 'expectedSpaceBefore', + data: { + type: operator + }, line: 1, column: 31 }, { - message: `Expected a space after the '${operator}'.`, + messageId: 'expectedSpaceAfter', + data: { + type: operator + }, line: 1, column: 33 } @@ -6126,12 +6510,18 @@ ruleTester.run('type-annotation-spacing', rule, { output: `type Foo = { [P in keyof T] ${operator} T[P] }`, errors: [ { - message: `Expected a space before the '${operator}'.`, + messageId: 'expectedSpaceBefore', + data: { + type: operator + }, line: 1, column: 31 }, { - message: `Expected a space after the '${operator}'.`, + messageId: 'expectedSpaceAfter', + data: { + type: operator + }, line: 1, column: 33 } @@ -6143,7 +6533,10 @@ ruleTester.run('type-annotation-spacing', rule, { output: `type Foo = { [P in keyof T]${operator} T[P] }`, errors: [ { - message: `Expected a space after the '${operator}'.`, + messageId: 'expectedSpaceAfter', + data: { + type: operator + }, line: 1, column: 33 } @@ -6155,7 +6548,10 @@ ruleTester.run('type-annotation-spacing', rule, { output: `type Foo = { [P in keyof T] ${operator}T[P] }`, errors: [ { - message: `Expected a space before the '${operator}'.`, + messageId: 'expectedSpaceBefore', + data: { + type: operator + }, line: 1, column: 31 } @@ -6168,12 +6564,18 @@ ruleTester.run('type-annotation-spacing', rule, { output: `type Foo = { [P in keyof T]${operator} T[P] }`, errors: [ { - message: `Unexpected space before the '${operator}'.`, + messageId: 'unexpectedSpaceBefore', + data: { + type: operator + }, line: 1, column: 32 }, { - message: `Expected a space after the '${operator}'.`, + messageId: 'expectedSpaceAfter', + data: { + type: operator + }, line: 1, column: 34 } @@ -6185,12 +6587,18 @@ ruleTester.run('type-annotation-spacing', rule, { output: `type Foo = { [P in keyof T]${operator} T[P] }`, errors: [ { - message: `Unexpected space before the '${operator}'.`, + messageId: 'unexpectedSpaceBefore', + data: { + type: operator + }, line: 1, column: 32 }, { - message: `Expected a space after the '${operator}'.`, + messageId: 'expectedSpaceAfter', + data: { + type: operator + }, line: 1, column: 34 } @@ -6202,7 +6610,10 @@ ruleTester.run('type-annotation-spacing', rule, { output: `type Foo = { [P in keyof T] ${operator} T[P] }`, errors: [ { - message: `Expected a space after the '${operator}'.`, + messageId: 'expectedSpaceAfter', + data: { + type: operator + }, line: 1, column: 34 } @@ -6214,12 +6625,18 @@ ruleTester.run('type-annotation-spacing', rule, { output: `type Foo = { [P in keyof T]${operator} T[P] }`, errors: [ { - message: `Unexpected space before the '${operator}'.`, + messageId: 'unexpectedSpaceBefore', + data: { + type: operator + }, line: 1, column: 32 }, { - message: `Expected a space after the '${operator}'.`, + messageId: 'expectedSpaceAfter', + data: { + type: operator + }, line: 1, column: 34 } @@ -6231,7 +6648,10 @@ ruleTester.run('type-annotation-spacing', rule, { output: `type Foo = { [P in keyof T] ${operator} T[P] }`, errors: [ { - message: `Expected a space after the '${operator}'.`, + messageId: 'expectedSpaceAfter', + data: { + type: operator + }, line: 1, column: 34 } @@ -6244,7 +6664,10 @@ ruleTester.run('type-annotation-spacing', rule, { output: `type Foo = { [P in keyof T]${operator}T[P] }`, errors: [ { - message: `Unexpected space after the '${operator}'.`, + messageId: 'unexpectedSpaceAfter', + data: { + type: operator + }, line: 1, column: 33 } @@ -6256,7 +6679,10 @@ ruleTester.run('type-annotation-spacing', rule, { output: `type Foo = { [P in keyof T] ${operator} T[P] }`, errors: [ { - message: `Expected a space before the '${operator}'.`, + messageId: 'expectedSpaceBefore', + data: { + type: operator + }, line: 1, column: 31 } @@ -6268,7 +6694,10 @@ ruleTester.run('type-annotation-spacing', rule, { output: `type Foo = { [P in keyof T] ${operator} T[P] }`, errors: [ { - message: `Expected a space before the '${operator}'.`, + messageId: 'expectedSpaceBefore', + data: { + type: operator + }, line: 1, column: 31 } @@ -6280,12 +6709,18 @@ ruleTester.run('type-annotation-spacing', rule, { output: `type Foo = { [P in keyof T] ${operator}T[P] }`, errors: [ { - message: `Expected a space before the '${operator}'.`, + messageId: 'expectedSpaceBefore', + data: { + type: operator + }, line: 1, column: 31 }, { - message: `Unexpected space after the '${operator}'.`, + messageId: 'unexpectedSpaceAfter', + data: { + type: operator + }, line: 1, column: 33 } @@ -6297,7 +6732,10 @@ ruleTester.run('type-annotation-spacing', rule, { output: `type Foo = { [P in keyof T]${operator}T[P] }`, errors: [ { - message: `Unexpected space after the '${operator}'.`, + messageId: 'unexpectedSpaceAfter', + data: { + type: operator + }, line: 1, column: 33 } diff --git a/packages/eslint-plugin/tests/lib/util.js b/packages/eslint-plugin/tests/util.test.ts similarity index 90% rename from packages/eslint-plugin/tests/lib/util.js rename to packages/eslint-plugin/tests/util.test.ts index 4f627c5dc11..b21f59a945a 100644 --- a/packages/eslint-plugin/tests/lib/util.js +++ b/packages/eslint-plugin/tests/util.test.ts @@ -1,7 +1,6 @@ -'use strict'; -const assert = require('assert'); +import assert from 'assert'; -const util = require('../../lib/util'); +import * as util from '../src/util'; describe('isTypescript', () => { it('returns false for non-typescript files', () => { @@ -17,7 +16,7 @@ describe('isTypescript', () => { ]; invalid.forEach(f => { - assert.strictEqual(util.isTypescript(f), false); + assert.strictEqual(util.isTypeScriptFile(f), false); }); }); @@ -34,7 +33,7 @@ describe('isTypescript', () => { ]; valid.forEach(f => { - assert.strictEqual(util.isTypescript(f), true); + assert.strictEqual(util.isTypeScriptFile(f), true); }); }); }); @@ -144,18 +143,19 @@ describe('applyDefault', () => { it('returns applies a deepMerge to each element in the array', () => { const defaults = [ { - prop: 'setting1' + prop: 'setting1', + other: 'other' }, { prop: 'setting2' } - ]; + ] as Record[]; const user = [ { prop: 'new', other: 'something' } - ]; + ] as Record[]; const result = util.applyDefault(defaults, user); assert.deepStrictEqual(result, [ @@ -172,8 +172,8 @@ describe('applyDefault', () => { }); it('returns a brand new array', () => { - const defaults = []; - const user = []; + const defaults: undefined[] = []; + const user: undefined[] = []; const result = util.applyDefault(defaults, user); assert.notStrictEqual(result, defaults); diff --git a/packages/eslint-plugin/tools/update-recommended.js b/packages/eslint-plugin/tools/update-recommended.ts similarity index 64% rename from packages/eslint-plugin/tools/update-recommended.js rename to packages/eslint-plugin/tools/update-recommended.ts index ce402c3933b..45e286dce7f 100644 --- a/packages/eslint-plugin/tools/update-recommended.js +++ b/packages/eslint-plugin/tools/update-recommended.ts @@ -1,9 +1,8 @@ /* eslint-disable no-console */ -'use strict'; -const path = require('path'); -const fs = require('fs'); -const requireIndex = require('requireindex'); +import path from 'path'; +import fs from 'fs'; +import requireIndex from 'requireindex'; const bannedRecommendedRules = new Set([ 'camelcase', @@ -13,24 +12,30 @@ const bannedRecommendedRules = new Set([ ]); const MAX_RULE_NAME_LENGTH = 32 + 'typescript/'.length; +function padEnd(str: string, length: number): string { + while (str.length < length) { + str += ' '; + } + return str; +} + /** * Generate recommended configuration - * @returns {void} */ -function generate() { +function generate(): void { // replace this with Object.entries when node > 8 - const allRules = requireIndex(path.resolve(__dirname, '../lib/rules')); + const allRules = requireIndex(path.resolve(__dirname, '../dist/lib/rules')); const rules = Object.keys(allRules) .filter(key => !!allRules[key].meta.docs.recommended) - .reduce((config, key) => { + .reduce>((config, key) => { // having this here is just for output niceness (the keys will be ordered) if (bannedRecommendedRules.has(key)) { - console.log(key.padEnd(MAX_RULE_NAME_LENGTH), '= off'); + console.log(padEnd(key, MAX_RULE_NAME_LENGTH), '= off'); config[key] = 'off'; } - const ruleName = `typescript/${key}`; + const ruleName = `@typescript-eslint/${key}`; const setting = allRules[key].meta.docs.recommended; if (!['error', 'warn'].includes(setting)) { @@ -40,20 +45,20 @@ function generate() { process.exit(1); } - console.log(ruleName.padEnd(MAX_RULE_NAME_LENGTH), '=', setting); + console.log(padEnd(ruleName, MAX_RULE_NAME_LENGTH), '=', setting); config[ruleName] = setting; return config; }, {}); - const filePath = path.resolve(__dirname, '../lib/configs/recommended.json'); + const filePath = path.resolve(__dirname, '../src/configs/recommended.json'); const recommendedConfig = { parser: '@typescript-eslint/parser', parserOptions: { sourceType: 'module' }, - plugins: ['typescript'], + plugins: ['@typescript-eslint'], rules }; diff --git a/packages/eslint-plugin/tsconfig.build.json b/packages/eslint-plugin/tsconfig.build.json new file mode 100644 index 00000000000..1ab98da19ab --- /dev/null +++ b/packages/eslint-plugin/tsconfig.build.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + // specifically disable declarations for the plugin + "declaration": false, + "declarationMap": false, + "outDir": "./dist", + "resolveJsonModule": true + }, + "include": [ + "src", + "typings", + // include the parser's ambient typings because the parser exports them in its type defs + "../parser/typings" + ] +} diff --git a/packages/eslint-plugin/tsconfig.json b/packages/eslint-plugin/tsconfig.json new file mode 100644 index 00000000000..fc93e91c26d --- /dev/null +++ b/packages/eslint-plugin/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.build.json", + "include": [ + "src", + "typings", + // include the parser's ambient typings because the parser exports them in its type defs + "../parser/typings", + "tests", + "tools" + ] +} diff --git a/packages/eslint-plugin/typings/eslint-rules.d.ts b/packages/eslint-plugin/typings/eslint-rules.d.ts new file mode 100644 index 00000000000..c619f319e71 --- /dev/null +++ b/packages/eslint-plugin/typings/eslint-rules.d.ts @@ -0,0 +1,332 @@ +// don't provide a general import case so that people have to strictly type out a declaration +// declare module 'eslint/lib/rules/*' { +// import RuleModule from 'ts-eslint'; +// const rule: RuleModule; +// export = rule; +// } + +declare module 'eslint/lib/rules/arrow-parens' { + import { TSESTree } from '@typescript-eslint/typescript-estree'; + import RuleModule from 'ts-eslint'; + + const rule: RuleModule< + | 'unexpectedParens' + | 'expectedParens' + | 'unexpectedParensInline' + | 'expectedParensBlock', + [ + 'always' | 'as-needed', + { + requireForBlockBody?: boolean; + }? + ], + { + ArrowFunctionExpression(node: TSESTree.ArrowFunctionExpression): void; + } + >; + export = rule; +} + +declare module 'eslint/lib/rules/camelcase' { + import { TSESTree } from '@typescript-eslint/typescript-estree'; + import RuleModule from 'ts-eslint'; + + const rule: RuleModule< + 'notCamelCase', + [ + { + allow?: string[]; + ignoreDestructuring?: boolean; + properties?: 'always' | 'never'; + } + ], + { + Identifier(node: TSESTree.Identifier): void; + } + >; + export = rule; +} + +declare module 'eslint/lib/rules/indent' { + import { TSESTree } from '@typescript-eslint/typescript-estree'; + import RuleModule from 'ts-eslint'; + + type Listener = (node: TSESTree.Node) => void; + type ElementList = number | 'first' | 'off'; + const rule: RuleModule< + 'wrongIndentation', + [ + 'tab' | number, + { + SwitchCase?: number; + VariableDeclarator?: + | ElementList + | { + var?: ElementList; + let?: ElementList; + const?: ElementList; + }; + outerIIFEBody?: number; + MemberExpression?: number | 'off'; + FunctionDeclaration?: { + parameters?: ElementList; + body?: number; + }; + FunctionExpression?: { + parameters?: ElementList; + body?: number; + }; + CallExpression?: { + arguments?: ElementList; + }; + ArrayExpression?: ElementList; + ObjectExpression?: ElementList; + ImportDeclaration?: ElementList; + flatTernaryExpressions?: boolean; + ignoredNodes?: string[]; + ignoreComments?: boolean; + } + ], + { + '*:exit'(node: TSESTree.Node): void; + 'ArrayExpression, ArrayPattern'( + node: TSESTree.ArrayExpression | TSESTree.ArrayPattern + ): void; + 'ObjectExpression, ObjectPattern'( + node: TSESTree.ObjectExpression | TSESTree.ObjectPattern + ): void; + ArrowFunctionExpression(node: TSESTree.ArrowFunctionExpression): void; + AssignmentExpression(node: TSESTree.AssignmentExpression): void; + 'BinaryExpression, LogicalExpression'( + node: TSESTree.BinaryExpression | TSESTree.LogicalExpression + ): void; + 'BlockStatement, ClassBody'( + node: TSESTree.BlockStatement | TSESTree.ClassBody + ): void; + CallExpression(node: TSESTree.CallExpression): void; + 'ClassDeclaration[superClass], ClassExpression[superClass]'( + node: TSESTree.ClassDeclaration | TSESTree.ClassExpression + ): void; + ConditionalExpression(node: TSESTree.ConditionalExpression): void; + 'DoWhileStatement, WhileStatement, ForInStatement, ForOfStatement'( + node: + | TSESTree.DoWhileStatement + | TSESTree.WhileStatement + | TSESTree.ForInStatement + | TSESTree.ForOfStatement + ): void; + ExportNamedDeclaration(node: TSESTree.ExportNamedDeclaration): void; + ForStatement(node: TSESTree.ForStatement): void; + 'FunctionDeclaration, FunctionExpression'( + node: TSESTree.FunctionDeclaration | TSESTree.FunctionExpression + ): void; + IfStatement(node: TSESTree.IfStatement): void; + ImportDeclaration(node: TSESTree.ImportDeclaration): void; + 'MemberExpression, JSXMemberExpression, MetaProperty'( + node: + | TSESTree.MemberExpression + | TSESTree.JSXMemberExpression + | TSESTree.MetaProperty + ): void; + NewExpression(node: TSESTree.NewExpression): void; + Property(node: TSESTree.Property): void; + SwitchStatement(node: TSESTree.SwitchStatement): void; + SwitchCase(node: TSESTree.SwitchCase): void; + TemplateLiteral(node: TSESTree.TemplateLiteral): void; + VariableDeclaration(node: TSESTree.VariableDeclaration): void; + VariableDeclarator(node: TSESTree.VariableDeclarator): void; + 'JSXAttribute[value]'(node: TSESTree.JSXAttribute): void; + JSXElement(node: TSESTree.JSXElement): void; + JSXOpeningElement(node: TSESTree.JSXOpeningElement): void; + JSXClosingElement(node: TSESTree.JSXClosingElement): void; + JSXExpressionContainer(node: TSESTree.JSXExpressionContainer): void; + } + >; + export = rule; +} + +declare module 'eslint/lib/rules/no-dupe-args' { + import { TSESTree } from '@typescript-eslint/typescript-estree'; + import RuleModule from 'ts-eslint'; + + const rule: RuleModule< + 'unexpected', + [], + { + FunctionDeclaration(node: TSESTree.FunctionDeclaration): void; + FunctionExpression(node: TSESTree.FunctionExpression): void; + } + >; + export = rule; +} + +declare module 'eslint/lib/rules/no-implicit-globals' { + import { TSESTree } from '@typescript-eslint/typescript-estree'; + import RuleModule from 'ts-eslint'; + + const rule: RuleModule< + never, + [], + { + Program(node: TSESTree.Program): void; + } + >; + export = rule; +} + +declare module 'eslint/lib/rules/no-redeclare' { + import { TSESTree } from '@typescript-eslint/typescript-estree'; + import RuleModule from 'ts-eslint'; + + const rule: RuleModule< + never, + [ + { + builtinGlobals?: boolean; + }? + ], + { + ArrowFunctionExpression(node: TSESTree.ArrowFunctionExpression): void; + } + >; + export = rule; +} + +declare module 'eslint/lib/rules/no-restricted-globals' { + import { TSESTree } from '@typescript-eslint/typescript-estree'; + import RuleModule from 'ts-eslint'; + + const rule: RuleModule< + never, + ( + | string + | { + name: string; + message?: string; + })[], + { + ArrowFunctionExpression(node: TSESTree.ArrowFunctionExpression): void; + } + >; + export = rule; +} + +declare module 'eslint/lib/rules/no-shadow' { + import { TSESTree } from '@typescript-eslint/typescript-estree'; + import RuleModule from 'ts-eslint'; + + const rule: RuleModule< + never, + [ + { + builtinGlobals?: boolean; + hoist: 'all' | 'functions' | 'never'; + allow: string[]; + } + ], + { + ArrowFunctionExpression(node: TSESTree.ArrowFunctionExpression): void; + } + >; + export = rule; +} + +declare module 'eslint/lib/rules/no-undef' { + import { TSESTree } from '@typescript-eslint/typescript-estree'; + import RuleModule from 'ts-eslint'; + + const rule: RuleModule< + 'undef', + [ + { + typeof?: boolean; + } + ], + { + ArrowFunctionExpression(node: TSESTree.ArrowFunctionExpression): void; + } + >; + export = rule; +} + +declare module 'eslint/lib/rules/no-unused-vars' { + import { TSESTree } from '@typescript-eslint/typescript-estree'; + import RuleModule from 'ts-eslint'; + + const rule: RuleModule< + never, + ( + | 'all' + | 'local' + | { + vars?: 'all' | 'local'; + varsIgnorePattern?: string; + args?: 'all' | 'after-used' | 'none'; + ignoreRestSiblings?: boolean; + argsIgnorePattern?: string; + caughtErrors?: 'all' | 'none'; + caughtErrorsIgnorePattern?: string; + })[], + { + ArrowFunctionExpression(node: TSESTree.ArrowFunctionExpression): void; + } + >; + export = rule; +} + +declare module 'eslint/lib/rules/no-use-before-define' { + import { TSESTree } from '@typescript-eslint/typescript-estree'; + import RuleModule from 'ts-eslint'; + + const rule: RuleModule< + never, + ( + | 'nofunc' + | { + functions?: boolean; + classes?: boolean; + variables?: boolean; + })[], + { + ArrowFunctionExpression(node: TSESTree.ArrowFunctionExpression): void; + } + >; + export = rule; +} + +declare module 'eslint/lib/rules/strict' { + import { TSESTree } from '@typescript-eslint/typescript-estree'; + import RuleModule from 'ts-eslint'; + + const rule: RuleModule< + | 'function' + | 'global' + | 'multiple' + | 'never' + | 'unnecessary' + | 'module' + | 'implied' + | 'unnecessaryInClasses' + | 'nonSimpleParameterList' + | 'wrap', + ['never' | 'global' | 'function' | 'safe'], + { + ArrowFunctionExpression(node: TSESTree.ArrowFunctionExpression): void; + } + >; + export = rule; +} + +declare module 'eslint/lib/rules/no-useless-constructor' { + import { TSESTree } from '@typescript-eslint/typescript-estree'; + import RuleModule from 'ts-eslint'; + + const rule: RuleModule< + never, + [], + { + MethodDefinition(node: TSESTree.MethodDefinition): void; + } + >; + export = rule; +} diff --git a/packages/eslint-plugin/typings/requireindex.d.ts b/packages/eslint-plugin/typings/requireindex.d.ts new file mode 100644 index 00000000000..7ddf009d87f --- /dev/null +++ b/packages/eslint-plugin/typings/requireindex.d.ts @@ -0,0 +1,9 @@ +declare module 'requireindex' { + type RequireIndex = ( + path: string, + basenames?: string[] + ) => Record; + + const fn: RequireIndex; + export = fn; +} diff --git a/packages/eslint-plugin/typings/ts-eslint.d.ts b/packages/eslint-plugin/typings/ts-eslint.d.ts new file mode 100644 index 00000000000..9ab6ff35e21 --- /dev/null +++ b/packages/eslint-plugin/typings/ts-eslint.d.ts @@ -0,0 +1,536 @@ +/* +Redefine these types for these reasons: +1) We can better control what properties are option and what are not. +2) We have to replace definitions with our definitions which use our typescript-estree types. +3) We can better document the fields so it's easier for new contributers to understand. + +The def is wrapped up in a fake module so that it can be used in eslint-rules.d.ts +*/ + +declare module 'ts-eslint' { + import { TSESTree } from '@typescript-eslint/typescript-estree'; + import { ParserServices } from '@typescript-eslint/parser'; + import { AST, Linter } from 'eslint'; + import { JSONSchema4 } from 'json-schema'; + + //#region SourceCode + + namespace SourceCode { + export interface Config { + text: string; + ast: AST.Program; + parserServices?: ParserServices; + scopeManager?: Scope.ScopeManager; + visitorKeys?: VisitorKeys; + } + + export interface VisitorKeys { + [nodeType: string]: string[]; + } + + export type FilterPredicate = ( + tokenOrComment: TSESTree.Token | TSESTree.Comment + ) => boolean; + + export type CursorWithSkipOptions = + | number + | FilterPredicate + | { + includeComments?: boolean; + filter?: FilterPredicate; + skip?: number; + }; + + export type CursorWithCountOptions = + | number + | FilterPredicate + | { + includeComments?: boolean; + filter?: FilterPredicate; + count?: number; + }; + } + + class SourceCode { + text: string; + ast: AST.Program; + lines: string[]; + hasBOM: boolean; + parserServices: ParserServices; + scopeManager: Scope.ScopeManager; + visitorKeys: SourceCode.VisitorKeys; + + constructor(text: string, ast: AST.Program); + // eslint-disable-next-line no-dupe-class-members + constructor(config: SourceCode.Config); + + static splitLines(text: string): string[]; + + getText( + node?: TSESTree.Node, + beforeCount?: number, + afterCount?: number + ): string; + + getLines(): string[]; + + getAllComments(): TSESTree.Comment[]; + + getComments( + node: TSESTree.Node + ): { leading: TSESTree.Comment[]; trailing: TSESTree.Comment[] }; + + getJSDocComment(node: TSESTree.Node): TSESTree.Node | TSESTree.Token | null; + + getNodeByRangeIndex(index: number): TSESTree.Node | null; + + isSpaceBetweenTokens( + first: TSESTree.Token, + second: TSESTree.Token + ): boolean; + + getLocFromIndex(index: number): TSESTree.LineAndColumnData; + + getIndexFromLoc(location: TSESTree.LineAndColumnData): number; + + // Inherited methods from TokenStore + // --------------------------------- + + getTokenByRangeStart( + offset: number, + options?: { includeComments?: boolean } + ): TSESTree.Token | null; + + getFirstToken( + node: TSESTree.Node, + options?: SourceCode.CursorWithSkipOptions + ): TSESTree.Token | null; + + getFirstTokens( + node: TSESTree.Node, + options?: SourceCode.CursorWithCountOptions + ): TSESTree.Token[]; + + getLastToken( + node: TSESTree.Node, + options?: SourceCode.CursorWithSkipOptions + ): TSESTree.Token | null; + + getLastTokens( + node: TSESTree.Node, + options?: SourceCode.CursorWithCountOptions + ): TSESTree.Token[]; + + getTokenBefore( + node: TSESTree.Node | TSESTree.Token | TSESTree.Comment, + options?: SourceCode.CursorWithSkipOptions + ): TSESTree.Token | null; + + getTokensBefore( + node: TSESTree.Node | TSESTree.Token | TSESTree.Comment, + options?: SourceCode.CursorWithCountOptions + ): TSESTree.Token[]; + + getTokenAfter( + node: TSESTree.Node | TSESTree.Token | TSESTree.Comment, + options?: SourceCode.CursorWithSkipOptions + ): TSESTree.Token | null; + + getTokensAfter( + node: TSESTree.Node | TSESTree.Token | TSESTree.Comment, + options?: SourceCode.CursorWithCountOptions + ): TSESTree.Token[]; + + getFirstTokenBetween( + left: TSESTree.Node | TSESTree.Token | TSESTree.Comment, + right: TSESTree.Node | TSESTree.Token | TSESTree.Comment, + options?: SourceCode.CursorWithSkipOptions + ): TSESTree.Token | null; + + getFirstTokensBetween( + left: TSESTree.Node | TSESTree.Token | TSESTree.Comment, + right: TSESTree.Node | TSESTree.Token | TSESTree.Comment, + options?: SourceCode.CursorWithCountOptions + ): TSESTree.Token[]; + + getLastTokenBetween( + left: TSESTree.Node | TSESTree.Token | TSESTree.Comment, + right: TSESTree.Node | TSESTree.Token | TSESTree.Comment, + options?: SourceCode.CursorWithSkipOptions + ): TSESTree.Token | null; + + getLastTokensBetween( + left: TSESTree.Node | TSESTree.Token | TSESTree.Comment, + right: TSESTree.Node | TSESTree.Token | TSESTree.Comment, + options?: SourceCode.CursorWithCountOptions + ): TSESTree.Token[]; + + getTokensBetween( + left: TSESTree.Node | TSESTree.Token | TSESTree.Comment, + right: TSESTree.Node | TSESTree.Token | TSESTree.Comment, + padding?: + | number + | SourceCode.FilterPredicate + | SourceCode.CursorWithCountOptions + ): TSESTree.Token[]; + + getTokens( + node: TSESTree.Node, + beforeCount?: number, + afterCount?: number + ): TSESTree.Token[]; + // eslint-disable-next-line no-dupe-class-members + getTokens( + node: TSESTree.Node, + options: SourceCode.FilterPredicate | SourceCode.CursorWithCountOptions + ): TSESTree.Token[]; + + commentsExistBetween( + left: TSESTree.Node | TSESTree.Token, + right: TSESTree.Node | TSESTree.Token + ): boolean; + + getCommentsBefore( + nodeOrToken: TSESTree.Node | TSESTree.Token + ): TSESTree.Comment[]; + + getCommentsAfter( + nodeOrToken: TSESTree.Node | TSESTree.Token + ): TSESTree.Comment[]; + + getCommentsInside(node: TSESTree.Node): TSESTree.Comment[]; + } + + //#endregion SourceCode + + //#region Rule + + interface RuleMetaDataDocs { + /** + * The general category the rule falls within + */ + category: 'Best Practices' | 'Stylistic Issues' | 'Variables'; + /** + * Concise description of the rule + */ + description: string; + /** + * Extra information linking the rule to a tslint rule + */ + extraDescription?: string[]; + /** + * The recommendation level for the rule. + * Used by the build tools to generate the recommended config. + * Set to false to not include it as a recommendation + */ + recommended: 'error' | 'warn' | false; + /** + * The URL of the rule's docs + */ + url: string; + } + interface RuleMetaData { + /** + * True if the rule is deprecated, false otherwise + */ + deprecated?: boolean; + /** + * Documentation for the rule + */ + docs: RuleMetaDataDocs; + /** + * The fixer category. Omit if there is no fixer + */ + fixable?: 'code' | 'whitespace'; + /** + * A map of messages which the rule can report. + * The key is the messageId, and the string is the parameterised error string. + * See: https://eslint.org/docs/developer-guide/working-with-rules#messageids + */ + messages: Record; + /** + * The type of rule. + * - `"problem"` means the rule is identifying code that either will cause an error or may cause a confusing behavior. Developers should consider this a high priority to resolve. + * - `"suggestion"` means the rule is identifying something that could be done in a better way but no errors will occur if the code isn’t changed. + * - `"layout"` means the rule cares primarily about whitespace, semicolons, commas, and parentheses, all the parts of the program that determine how the code looks rather than how it executes. These rules work on parts of the code that aren’t specified in the AST. + */ + type: 'suggestion' | 'problem' | 'layout'; + /** + * The name of the rule this rule was replaced by, if it was deprecated. + */ + replacedBy?: string; + /** + * The options schema. Supply an empty array if there are no options. + */ + schema: JSONSchema4 | JSONSchema4[]; + } + + interface RuleFix { + range: AST.Range; + text: string; + } + + interface RuleFixer { + insertTextAfter( + nodeOrToken: TSESTree.Node | TSESTree.Token, + text: string + ): RuleFix; + + insertTextAfterRange(range: AST.Range, text: string): RuleFix; + + insertTextBefore( + nodeOrToken: TSESTree.Node | TSESTree.Token, + text: string + ): RuleFix; + + insertTextBeforeRange(range: AST.Range, text: string): RuleFix; + + remove(nodeOrToken: TSESTree.Node | TSESTree.Token): RuleFix; + + removeRange(range: AST.Range): RuleFix; + + replaceText( + nodeOrToken: TSESTree.Node | TSESTree.Token, + text: string + ): RuleFix; + + replaceTextRange(range: AST.Range, text: string): RuleFix; + } + + type ReportFixFunction = (fixer: RuleFixer) => null | RuleFix | RuleFix[]; + + interface ReportDescriptor { + /** + * The parameters for the message string associated with `messageId`. + */ + data?: Record; + /** + * The fixer function. + */ + fix?: ReportFixFunction | null; + /** + * The messageId which is being reported. + */ + messageId: TMessageIds; + /** + * The Node or AST Token which the report is being attached to + */ + node: TSESTree.Node | TSESTree.Comment | TSESTree.Token; + /** + * An override of the location of the report + */ + loc?: TSESTree.SourceLocation; + } + + interface RuleContext< + TMessageIds extends string, + TOptions extends Readonly + > { + /** + * The rule ID. + */ + id: string; + /** + * An array of the configured options for this rule. + * This array does not include the rule severity. + */ + options: TOptions; + /** + * The shared settings from configuration. + * We do not have any shared settings in this plugin. + */ + settings: {}; + /** + * The name of the parser from configuration. + */ + parserPath: string; + /** + * The parser options configured for this run + */ + parserOptions: Linter.ParserOptions; + /** + * An object containing parser-provided services for rules + */ + parserServices?: ParserServices; + + /** + * Returns an array of the ancestors of the currently-traversed node, starting at + * the root of the AST and continuing through the direct parent of the current node. + * This array does not include the currently-traversed node itself. + */ + getAncestors(): TSESTree.Node[]; + + /** + * Returns a list of variables declared by the given node. + * This information can be used to track references to variables. + */ + getDeclaredVariables(node: TSESTree.Node): Scope.Variable[]; + + /** + * Returns the filename associated with the source. + */ + getFilename(): string; + + /** + * Returns the scope of the currently-traversed node. + * This information can be used track references to variables. + */ + getScope(): Scope.Scope; + + /** + * Returns a SourceCode object that you can use to work with the source that + * was passed to ESLint. + */ + getSourceCode(): SourceCode; + + /** + * Marks a variable with the given name in the current scope as used. + * This affects the no-unused-vars rule. + */ + markVariableAsUsed(name: string): boolean; + + /** + * Reports a problem in the code. + */ + report(descriptor: ReportDescriptor): void; + } + + // This isn't the correct signature, but it makes it easier to do custom unions within reusable listneers + // never will break someone's code unless they specifically type the function argument + type RuleFunction = (node: never) => void; + type RuleListener = Record; + + interface RuleModule< + TMessageIds extends string, + TOptions extends Readonly, + // for extending base rules + TRuleListener extends RuleListener = RuleListener + > { + /** + * Metadata about the rule + */ + meta: RuleMetaData; + + /** + * Function which returns an object with methods that ESLint calls to “visit” + * nodes while traversing the abstract syntax tree. + */ + create(context: RuleContext): TRuleListener; + } + + //#endregion Rule + + namespace Scope { + interface ScopeManager { + scopes: Scope[]; + globalScope: Scope | null; + + acquire(node: TSESTree.Node, inner?: boolean): Scope | null; + + getDeclaredVariables(node: TSESTree.Node): Variable[]; + } + + interface Reference { + identifier: TSESTree.Identifier; + from: Scope; + resolved: Variable | null; + writeExpr: TSESTree.Node | null; + init: boolean; + + isWrite(): boolean; + + isRead(): boolean; + + isWriteOnly(): boolean; + + isReadOnly(): boolean; + + isReadWrite(): boolean; + } + + interface Variable { + name: string; + identifiers: TSESTree.Identifier[]; + references: Reference[]; + defs: Definition[]; + scope: Scope; + eslintUsed?: boolean; + } + + interface Scope { + type: + | 'block' + | 'catch' + | 'class' + | 'for' + | 'function' + | 'function-expression-name' + | 'global' + | 'module' + | 'switch' + | 'with' + | 'TDZ'; + isStrict: boolean; + upper: Scope | null; + childScopes: Scope[]; + variableScope: Scope; + block: TSESTree.Node; + variables: Variable[]; + set: Map; + references: Reference[]; + through: Reference[]; + functionExpressionScope: boolean; + } + + type DefinitionType = + | { type: 'CatchClause'; node: TSESTree.CatchClause; parent: null } + | { + type: 'ClassName'; + node: TSESTree.ClassDeclaration | TSESTree.ClassExpression; + parent: null; + } + | { + type: 'FunctionName'; + node: TSESTree.FunctionDeclaration | TSESTree.FunctionExpression; + parent: null; + } + | { type: 'ImplicitGlobalVariable'; node: TSESTree.Program; parent: null } + | { + type: 'ImportBinding'; + node: + | TSESTree.ImportSpecifier + | TSESTree.ImportDefaultSpecifier + | TSESTree.ImportNamespaceSpecifier; + parent: TSESTree.ImportDeclaration; + } + | { + type: 'Parameter'; + node: + | TSESTree.FunctionDeclaration + | TSESTree.FunctionExpression + | TSESTree.ArrowFunctionExpression; + parent: null; + } + | { type: 'TDZ'; node: any; parent: null } + | { + type: 'Variable'; + node: TSESTree.VariableDeclarator; + parent: TSESTree.VariableDeclaration; + }; + + type Definition = DefinitionType & { name: TSESTree.Identifier }; + } + + export { + ReportDescriptor, + ReportFixFunction, + RuleContext, + RuleFix, + RuleFunction, + RuleListener, + RuleMetaData, + RuleMetaDataDocs, + Scope + }; + export default RuleModule; +} diff --git a/packages/parser/package.json b/packages/parser/package.json index 5b89a50f1ef..1b3787315b4 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -27,9 +27,10 @@ ], "scripts": { "prebuild": "npm run clean", - "build": "tsc", + "build": "tsc -p tsconfig.build.json", "clean": "rimraf dist/", - "test": "jest --coverage" + "test": "jest --coverage", + "typecheck": "tsc --noEmit" }, "peerDependencies": { "eslint": "^5.0.0", diff --git a/packages/parser/src/analyze-scope.ts b/packages/parser/src/analyze-scope.ts index f3a6b5f78ac..b6446af8de1 100644 --- a/packages/parser/src/analyze-scope.ts +++ b/packages/parser/src/analyze-scope.ts @@ -361,7 +361,9 @@ class Referencer extends OriginalReferencer { * Create reference objects for the references in parameters and return type. * @param node The TSEmptyBodyFunctionExpression node to visit. */ - TSEmptyBodyFunctionExpression(node: TSESTree.FunctionExpression): void { + TSEmptyBodyFunctionExpression( + node: TSESTree.TSEmptyBodyFunctionExpression + ): void { const upperTypeMode = this.typeMode; const { typeParameters, params, returnType } = node; diff --git a/packages/parser/src/parser.ts b/packages/parser/src/parser.ts index 78d71197e9f..df73ab0be63 100644 --- a/packages/parser/src/parser.ts +++ b/packages/parser/src/parser.ts @@ -9,6 +9,7 @@ import { analyzeScope } from './analyze-scope'; import { ParserOptions } from './parser-options'; import { visitorKeys } from './visitor-keys'; +// note - cannot migrate this to an import statement because it will make TSC copy the package.json to the dist folder const packageJSON = require('../package.json'); interface ParseForESLintResult { @@ -101,3 +102,5 @@ export function parseForESLint( const scopeManager = analyzeScope(ast, options); return { ast, services, scopeManager, visitorKeys }; } + +export { ParserServices, ParserOptions }; diff --git a/packages/parser/tests/lib/parser.ts b/packages/parser/tests/lib/parser.ts index 6a844baae22..46aa13dcbbf 100644 --- a/packages/parser/tests/lib/parser.ts +++ b/packages/parser/tests/lib/parser.ts @@ -33,12 +33,12 @@ describe('parser', () => { it('parseAndGenerateServices() should be called with options', () => { const code = 'const valid = true;'; const spy = jest.spyOn(typescriptESTree, 'parseAndGenerateServices'); - parseForESLint(code, { + const config = { loc: false, comment: false, range: false, tokens: false, - sourceType: 'module', + sourceType: 'module' as 'module', ecmaVersion: 10, ecmaFeatures: { globalReturn: false, @@ -50,29 +50,13 @@ describe('parser', () => { useJSXTextNode: false, errorOnUnknownASTType: false, errorOnTypeScriptSyntacticAndSemanticIssues: false, - tsconfigRootDir: '../../', + tsconfigRootDir: './', extraFileExtensions: ['foo'] - }); + }; + parseForESLint(code, config); expect(spy).toHaveBeenCalledWith(code, { jsx: false, - loc: false, - comment: false, - range: false, - tokens: false, - sourceType: 'module', - ecmaVersion: 10, - ecmaFeatures: { - globalReturn: false, - jsx: false - }, - // ts-estree specific - filePath: 'test/foo', - project: 'tsconfig.json', - useJSXTextNode: false, - errorOnUnknownASTType: false, - errorOnTypeScriptSyntacticAndSemanticIssues: false, - tsconfigRootDir: '../../', - extraFileExtensions: ['foo'] + ...config }); }); diff --git a/packages/parser/tsconfig.build.json b/packages/parser/tsconfig.build.json new file mode 100644 index 00000000000..578d8230840 --- /dev/null +++ b/packages/parser/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "declaration": true, + "outDir": "./dist" + }, + "include": ["src", "typings"] +} diff --git a/packages/parser/tsconfig.json b/packages/parser/tsconfig.json index d74357950e9..58599d3d3ed 100644 --- a/packages/parser/tsconfig.json +++ b/packages/parser/tsconfig.json @@ -1,8 +1,5 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "declaration": true, - "outDir": "./dist" - }, - "include": ["src"] + "extends": "./tsconfig.build.json", + "include": ["src", "typings", "tests", "tools"], + "exclude": ["tests/fixtures"] } diff --git a/packages/parser/src/eslint-scope.d.ts b/packages/parser/typings/eslint-scope.d.ts similarity index 98% rename from packages/parser/src/eslint-scope.d.ts rename to packages/parser/typings/eslint-scope.d.ts index 28de7f8c759..4822d9a59cc 100644 --- a/packages/parser/src/eslint-scope.d.ts +++ b/packages/parser/typings/eslint-scope.d.ts @@ -1,6 +1,12 @@ // Type definitions for eslint-scope 4.0.0 // Project: http://github.com/eslint/eslint-scope // Definitions by: Armano + +//----------------------------------------------------------------------- +// TODO - figure out how to make ScopeManager exportable so that +// the module's type declaration files don't break +//----------------------------------------------------------------------- + declare module 'eslint-scope/lib/options' { import { TSESTree } from '@typescript-eslint/typescript-estree'; export type PatternVisitorCallback = (pattern: any, info: any) => void; diff --git a/packages/typescript-estree/package.json b/packages/typescript-estree/package.json index 18b452e4be5..339d15f1a38 100644 --- a/packages/typescript-estree/package.json +++ b/packages/typescript-estree/package.json @@ -28,11 +28,12 @@ ], "scripts": { "prebuild": "npm run clean", - "build": "tsc", + "build": "tsc -p tsconfig.build.json", "clean": "rimraf dist/", "test": "jest --coverage", "unit-tests": "jest \"./tests/lib/.*\"", - "ast-alignment-tests": "jest spec.ts" + "ast-alignment-tests": "jest spec.ts", + "typecheck": "tsc --noEmit" }, "dependencies": { "lodash.unescape": "4.0.1", @@ -42,6 +43,7 @@ "typescript": "*" }, "devDependencies": { + "@babel/types": "^7.3.2", "@typescript-eslint/shared-fixtures": "1.3.0" } } diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 9f14bbcc248..38abe7f3272 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -6,7 +6,6 @@ * MIT License */ import ts from 'typescript'; -import { TSESTree } from './ts-estree'; import { canContainDirective, createError, @@ -27,7 +26,7 @@ import { isOptional, unescapeStringLiteralText } from './node-utils'; -import { AST_NODE_TYPES } from './ast-node-types'; +import { AST_NODE_TYPES, TSESTree } from './ts-estree'; import { TSNode } from './ts-nodes'; const SyntaxKind = ts.SyntaxKind; diff --git a/packages/typescript-estree/src/node-utils.ts b/packages/typescript-estree/src/node-utils.ts index 4ab71cf5e9a..8d0db4d74b3 100644 --- a/packages/typescript-estree/src/node-utils.ts +++ b/packages/typescript-estree/src/node-utils.ts @@ -6,8 +6,7 @@ */ import ts from 'typescript'; import unescape from 'lodash.unescape'; -import { TSESTree } from './ts-estree'; -import { AST_NODE_TYPES } from './ast-node-types'; +import { AST_NODE_TYPES, AST_TOKEN_TYPES, TSESTree } from './ts-estree'; const SyntaxKind = ts.SyntaxKind; @@ -501,21 +500,21 @@ export function fixExports( * @param token the ts.Token * @returns the token type */ -export function getTokenType(token: any): TSESTree.TokenType { +export function getTokenType(token: any): AST_TOKEN_TYPES { // Need two checks for keywords since some are also identifiers if (token.originalKeywordKind) { switch (token.originalKeywordKind) { case SyntaxKind.NullKeyword: - return 'Null'; + return AST_TOKEN_TYPES.Null; case SyntaxKind.GetKeyword: case SyntaxKind.SetKeyword: case SyntaxKind.TypeKeyword: case SyntaxKind.ModuleKeyword: - return 'Identifier'; + return AST_TOKEN_TYPES.Identifier; default: - return 'Keyword'; + return AST_TOKEN_TYPES.Keyword; } } @@ -527,32 +526,32 @@ export function getTokenType(token: any): TSESTree.TokenType { token.kind === SyntaxKind.FalseKeyword || token.kind === SyntaxKind.TrueKeyword ) { - return 'Boolean'; + return AST_TOKEN_TYPES.Boolean; } - return 'Keyword'; + return AST_TOKEN_TYPES.Keyword; } if ( token.kind >= SyntaxKind.FirstPunctuation && token.kind <= SyntaxKind.LastBinaryOperator ) { - return 'Punctuator'; + return AST_TOKEN_TYPES.Punctuator; } if ( token.kind >= SyntaxKind.NoSubstitutionTemplateLiteral && token.kind <= SyntaxKind.TemplateTail ) { - return 'Template'; + return AST_TOKEN_TYPES.Template; } switch (token.kind) { case SyntaxKind.NumericLiteral: - return 'Numeric'; + return AST_TOKEN_TYPES.Numeric; case SyntaxKind.JsxText: - return 'JSXText'; + return AST_TOKEN_TYPES.JSXText; case SyntaxKind.StringLiteral: // A TypeScript-StringLiteral token with a TypeScript-JsxAttribute or TypeScript-JsxElement parent, @@ -562,13 +561,13 @@ export function getTokenType(token: any): TSESTree.TokenType { (token.parent.kind === SyntaxKind.JsxAttribute || token.parent.kind === SyntaxKind.JsxElement) ) { - return 'JSXText'; + return AST_TOKEN_TYPES.JSXText; } - return 'String'; + return AST_TOKEN_TYPES.String; case SyntaxKind.RegularExpressionLiteral: - return 'RegularExpression'; + return AST_TOKEN_TYPES.RegularExpression; case SyntaxKind.Identifier: case SyntaxKind.ConstructorKeyword: @@ -586,21 +585,21 @@ export function getTokenType(token: any): TSESTree.TokenType { token.parent.kind === SyntaxKind.PropertyAccessExpression && hasJSXAncestor(token) ) { - return 'JSXIdentifier'; + return AST_TOKEN_TYPES.JSXIdentifier; } if (isJSXToken(token.parent)) { if (token.kind === SyntaxKind.PropertyAccessExpression) { - return 'JSXMemberExpression'; + return AST_TOKEN_TYPES.JSXMemberExpression; } if (token.kind === SyntaxKind.Identifier) { - return 'JSXIdentifier'; + return AST_TOKEN_TYPES.JSXIdentifier; } } } - return 'Identifier'; + return AST_TOKEN_TYPES.Identifier; } /** diff --git a/packages/typescript-estree/src/parser-options.ts b/packages/typescript-estree/src/parser-options.ts index 6ec07b902cd..8a30b27b89f 100644 --- a/packages/typescript-estree/src/parser-options.ts +++ b/packages/typescript-estree/src/parser-options.ts @@ -1,5 +1,5 @@ import { Program } from 'typescript'; -import { Token, Comment, Node } from './typedefs'; +import { Token, Comment, Node } from './ts-estree/ts-estree'; import { TSNode } from './ts-nodes'; export interface Extra { diff --git a/packages/typescript-estree/src/parser.ts b/packages/typescript-estree/src/parser.ts index bb77a186171..9edec09a35b 100644 --- a/packages/typescript-estree/src/parser.ts +++ b/packages/typescript-estree/src/parser.ts @@ -413,7 +413,5 @@ export function parseAndGenerateServices< }; } -export { AST_NODE_TYPES } from './ast-node-types'; -export { ParserOptions }; -export { ParserServices }; -export { TSESTree }; +export { AST_NODE_TYPES, AST_TOKEN_TYPES, TSESTree } from './ts-estree'; +export { ParserOptions, ParserServices }; diff --git a/packages/typescript-estree/src/ts-estree.ts b/packages/typescript-estree/src/ts-estree.ts deleted file mode 100644 index 846392c5443..00000000000 --- a/packages/typescript-estree/src/ts-estree.ts +++ /dev/null @@ -1,2 +0,0 @@ -import * as TSESTree from './typedefs'; -export { TSESTree }; diff --git a/packages/typescript-estree/src/ast-node-types.ts b/packages/typescript-estree/src/ts-estree/ast-node-types.ts similarity index 93% rename from packages/typescript-estree/src/ast-node-types.ts rename to packages/typescript-estree/src/ts-estree/ast-node-types.ts index a5e69a66d07..2bb83192035 100644 --- a/packages/typescript-estree/src/ast-node-types.ts +++ b/packages/typescript-estree/src/ts-estree/ast-node-types.ts @@ -110,6 +110,7 @@ export enum AST_NODE_TYPES { TSConstructSignatureDeclaration = 'TSConstructSignatureDeclaration', TSDeclareKeyword = 'TSDeclareKeyword', TSDeclareFunction = 'TSDeclareFunction', + TSEmptyBodyFunctionExpression = 'TSEmptyBodyFunctionExpression', TSEnumDeclaration = 'TSEnumDeclaration', TSEnumMember = 'TSEnumMember', TSExportAssignment = 'TSExportAssignment', @@ -168,3 +169,18 @@ export enum AST_NODE_TYPES { TSUnknownKeyword = 'TSUnknownKeyword', TSVoidKeyword = 'TSVoidKeyword' } + +export enum AST_TOKEN_TYPES { + Boolean = 'Boolean', + Identifier = 'Identifier', + JSXIdentifier = 'JSXIdentifier', + JSXMemberExpression = 'JSXMemberExpression', + JSXText = 'JSXText', + Keyword = 'Keyword', + Null = 'Null', + Numeric = 'Numeric', + Punctuator = 'Punctuator', + RegularExpression = 'RegularExpression', + String = 'String', + Template = 'Template' +} diff --git a/packages/typescript-estree/src/ts-estree/index.ts b/packages/typescript-estree/src/ts-estree/index.ts new file mode 100644 index 00000000000..4a6b74aef1f --- /dev/null +++ b/packages/typescript-estree/src/ts-estree/index.ts @@ -0,0 +1,5 @@ +import * as TSESTree from './ts-estree'; + +export * from './ast-node-types'; +export * from '../ts-nodes'; +export { TSESTree }; diff --git a/packages/typescript-estree/src/typedefs.ts b/packages/typescript-estree/src/ts-estree/ts-estree.ts similarity index 98% rename from packages/typescript-estree/src/typedefs.ts rename to packages/typescript-estree/src/ts-estree/ts-estree.ts index af8d17e0157..57cc7c9016a 100644 --- a/packages/typescript-estree/src/typedefs.ts +++ b/packages/typescript-estree/src/ts-estree/ts-estree.ts @@ -1,4 +1,4 @@ -import { AST_NODE_TYPES } from './ast-node-types'; +import { AST_NODE_TYPES, AST_TOKEN_TYPES } from './ast-node-types'; export interface LineAndColumnData { /** @@ -52,22 +52,8 @@ export interface BaseNode { * are not ever included as part of the standard AST tree. */ -export type TokenType = - | 'Boolean' - | 'Identifier' - | 'JSXIdentifier' - | 'JSXMemberExpression' - | 'JSXText' - | 'Keyword' - | 'Null' - | 'Numeric' - | 'Punctuator' - | 'RegularExpression' - | 'String' - | 'Template'; - export interface Token extends BaseNode { - type: TokenType; + type: AST_TOKEN_TYPES; value: string; regex?: { pattern: string; @@ -182,6 +168,7 @@ export type Node = | TSConstructSignatureDeclaration | TSDeclareFunction | TSDeclareKeyword + | TSEmptyBodyFunctionExpression | TSEnumDeclaration | TSEnumMember | TSExportAssignment @@ -261,6 +248,7 @@ export type ClassElement = | MethodDefinition | TSAbstractClassProperty | TSAbstractMethodDefinition + | TSEmptyBodyFunctionExpression | TSIndexSignature; export type DeclarationStatement = | ClassDeclaration @@ -512,7 +500,7 @@ interface LiteralBase extends BaseNode { interface MethodDefinitionBase extends BaseNode { key: PropertyName; - value: FunctionExpression; + value: FunctionExpression | TSEmptyBodyFunctionExpression; computed: boolean; static: boolean; kind: 'method' | 'get' | 'set' | 'constructor'; @@ -1054,6 +1042,11 @@ export interface TSDeclareKeyword extends BaseNode { type: AST_NODE_TYPES.TSDeclareKeyword; } +export interface TSEmptyBodyFunctionExpression extends FunctionDeclarationBase { + type: AST_NODE_TYPES.TSEmptyBodyFunctionExpression; + body: null; +} + export interface TSEnumDeclaration extends BaseNode { type: AST_NODE_TYPES.TSEnumDeclaration; id: Identifier; diff --git a/packages/typescript-estree/tests/ast-alignment/utils.ts b/packages/typescript-estree/tests/ast-alignment/utils.ts index 3b9cdfd087c..8fcc90e1ffb 100644 --- a/packages/typescript-estree/tests/ast-alignment/utils.ts +++ b/packages/typescript-estree/tests/ast-alignment/utils.ts @@ -1,5 +1,5 @@ import isPlainObject from 'lodash.isplainobject'; -import { AST_NODE_TYPES } from '../../src/ast-node-types'; +import { AST_NODE_TYPES } from '../../src/ts-estree'; /** * By default, pretty-format (within Jest matchers) retains the names/types of nodes from the babylon AST, diff --git a/packages/typescript-estree/tests/lib/semanticInfo.ts b/packages/typescript-estree/tests/lib/semanticInfo.ts index 126ad35879b..7fb09b92dcb 100644 --- a/packages/typescript-estree/tests/lib/semanticInfo.ts +++ b/packages/typescript-estree/tests/lib/semanticInfo.ts @@ -22,7 +22,7 @@ import { VariableDeclaration, ClassDeclaration, ClassProperty -} from '../../src/typedefs'; +} from '../../src/ts-estree/ts-estree'; //------------------------------------------------------------------------------ // Setup diff --git a/packages/typescript-estree/tsconfig.build.json b/packages/typescript-estree/tsconfig.build.json new file mode 100644 index 00000000000..b0fced27d72 --- /dev/null +++ b/packages/typescript-estree/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist" + }, + "include": ["src"] +} diff --git a/packages/typescript-estree/tsconfig.json b/packages/typescript-estree/tsconfig.json index d74357950e9..1fdde9ad21c 100644 --- a/packages/typescript-estree/tsconfig.json +++ b/packages/typescript-estree/tsconfig.json @@ -1,8 +1,4 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "declaration": true, - "outDir": "./dist" - }, - "include": ["src"] + "extends": "./tsconfig.build.json", + "include": ["src", "tests", "tools"] } diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 00000000000..fe87d8e95ab --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "allowUnreachableCode": false, + "allowUnusedLabels": false, + "declaration": true, + "declarationMap": true, + "esModuleInterop": true, + "module": "commonjs", + "moduleResolution": "node", + "noImplicitReturns": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "pretty": true, + "sourceMap": true, + "strict": true, + "target": "es2017" + } +} diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 568b3c81592..00000000000 --- a/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "compilerOptions": { - "target": "es2017", - "module": "commonjs", - "moduleResolution": "node", - "esModuleInterop": true, - "strict": true - } -} diff --git a/yarn.lock b/yarn.lock index 95f575e0097..233ce66262a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23,6 +23,15 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.3.2.tgz#95cdeddfc3992a6ca2a1315191c1679ca32c55cd" integrity sha512-QzNUC2RO1gadg+fs21fi0Uu0OuGNzRKEmgCxoLNzbCdoprLwjfmZwzUrpUNfJPaVRwBpDY47A17yYEGWyRelnQ== +"@babel/types@^7.3.2": + version "7.3.2" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.3.2.tgz#424f5be4be633fff33fb83ab8d67e4a8290f5a2f" + integrity sha512-3Y6H8xlUlpbGR+XvawiH0UXehqydTmNmEpozWcXymqwcrwYAl5KMvKtQ+TF6f6E08V6Jur7v/ykdDSF+WDEIXQ== + dependencies: + esutils "^2.0.2" + lodash "^4.17.10" + to-fast-properties "^2.0.0" + "@commitlint/cli@^7.1.2", "@commitlint/cli@^7.3.2": version "7.3.2" resolved "https://registry.yarnpkg.com/@commitlint/cli/-/cli-7.3.2.tgz#61abf30b48861e4fbd521690e6d4a3e1258980bb" @@ -1031,6 +1040,11 @@ are-we-there-yet@~1.1.2: delegates "^1.0.0" readable-stream "^2.0.6" +arg@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.0.tgz#583c518199419e0037abb74062c37f8519e575f0" + integrity sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -2216,7 +2230,7 @@ dezalgo@^1.0.0: asap "^2.0.0" wrappy "1" -diff@^3.2.0, diff@^3.5.0: +diff@^3.1.0, diff@^3.2.0, diff@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== @@ -2444,49 +2458,6 @@ eslint@^5.12.1: table "^5.0.2" text-table "^0.2.0" -eslint@^5.9.0: - version "5.12.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.12.0.tgz#fab3b908f60c52671fb14e996a450b96c743c859" - integrity sha512-LntwyPxtOHrsJdcSwyQKVtHofPHdv+4+mFwEe91r2V13vqpM8yLr7b1sW+Oo/yheOPkWYsYlYJCkzlFAt8KV7g== - dependencies: - "@babel/code-frame" "^7.0.0" - ajv "^6.5.3" - chalk "^2.1.0" - cross-spawn "^6.0.5" - debug "^4.0.1" - doctrine "^2.1.0" - eslint-scope "^4.0.0" - eslint-utils "^1.3.1" - eslint-visitor-keys "^1.0.0" - espree "^5.0.0" - esquery "^1.0.1" - esutils "^2.0.2" - file-entry-cache "^2.0.0" - functional-red-black-tree "^1.0.1" - glob "^7.1.2" - globals "^11.7.0" - ignore "^4.0.6" - import-fresh "^3.0.0" - imurmurhash "^0.1.4" - inquirer "^6.1.0" - js-yaml "^3.12.0" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.3.0" - lodash "^4.17.5" - minimatch "^3.0.4" - mkdirp "^0.5.1" - natural-compare "^1.4.0" - optionator "^0.8.2" - path-is-inside "^1.0.2" - pluralize "^7.0.0" - progress "^2.0.0" - regexpp "^2.0.1" - semver "^5.5.1" - strip-ansi "^4.0.0" - strip-json-comments "^2.0.1" - table "^5.0.2" - text-table "^0.2.0" - espree@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.0.tgz#fc7f984b62b36a0f543b13fb9cd7b9f4a7f5b65c" @@ -4755,7 +4726,7 @@ make-dir@^1.0.0: dependencies: pify "^3.0.0" -make-error@1.x: +make-error@1.x, make-error@^1.1.1: version "1.3.5" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" integrity sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g== @@ -6903,6 +6874,11 @@ to-fast-properties@^1.0.3: resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc= +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + to-object-path@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" @@ -6995,6 +6971,18 @@ ts-jest@^23.10.4: semver "^5.5" yargs-parser "10.x" +ts-node@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.0.1.tgz#cad98e8d50f0474f0d763ca8dc4c1bf8b7554ae2" + integrity sha512-zER3Js6Iotp31ghen6nKjgX75UOipwTWX9T5fAVmHaaYAizWhOes4uAsLmDC8H51UG5tHL8gNjoa/wLFjo7wtA== + dependencies: + arg "^4.1.0" + arrify "^1.0.0" + diff "^3.1.0" + make-error "^1.1.1" + source-map-support "^0.5.6" + yn "^2.0.0" + tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" @@ -7424,3 +7412,8 @@ yargs@^12.0.1: which-module "^2.0.0" y18n "^3.2.1 || ^4.0.0" yargs-parser "^11.1.1" + +yn@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a" + integrity sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=