@typescript-eslint/eslint-plugin
Advanced tools
Comparing version 4.15.1 to 5.62.0
@@ -14,2 +14,4 @@ "use strict"; | ||
'@typescript-eslint/ban-types': 'error', | ||
'block-spacing': 'off', | ||
'@typescript-eslint/block-spacing': 'error', | ||
'brace-style': 'off', | ||
@@ -22,5 +24,7 @@ '@typescript-eslint/brace-style': 'error', | ||
'@typescript-eslint/comma-spacing': 'error', | ||
'@typescript-eslint/consistent-generic-constructors': 'error', | ||
'@typescript-eslint/consistent-indexed-object-style': 'error', | ||
'@typescript-eslint/consistent-type-assertions': 'error', | ||
'@typescript-eslint/consistent-type-definitions': 'error', | ||
'@typescript-eslint/consistent-type-exports': 'error', | ||
'@typescript-eslint/consistent-type-imports': 'error', | ||
@@ -40,4 +44,8 @@ 'default-param-last': 'off', | ||
'@typescript-eslint/init-declarations': 'error', | ||
'key-spacing': 'off', | ||
'@typescript-eslint/key-spacing': 'error', | ||
'keyword-spacing': 'off', | ||
'@typescript-eslint/keyword-spacing': 'error', | ||
'lines-around-comment': 'off', | ||
'@typescript-eslint/lines-around-comment': 'error', | ||
'lines-between-class-members': 'off', | ||
@@ -56,4 +64,4 @@ '@typescript-eslint/lines-between-class-members': 'error', | ||
'@typescript-eslint/no-dupe-class-members': 'error', | ||
'no-duplicate-imports': 'off', | ||
'@typescript-eslint/no-duplicate-imports': 'error', | ||
'@typescript-eslint/no-duplicate-enum-values': 'error', | ||
'@typescript-eslint/no-duplicate-type-constituents': 'error', | ||
'@typescript-eslint/no-dynamic-delete': 'error', | ||
@@ -72,5 +80,5 @@ 'no-empty-function': 'off', | ||
'@typescript-eslint/no-for-in-array': 'error', | ||
'@typescript-eslint/no-implicit-any-catch': 'error', | ||
'no-implied-eval': 'off', | ||
'@typescript-eslint/no-implied-eval': 'error', | ||
'@typescript-eslint/no-import-type-side-effects': 'error', | ||
'@typescript-eslint/no-inferrable-types': 'error', | ||
@@ -86,11 +94,16 @@ 'no-invalid-this': 'off', | ||
'@typescript-eslint/no-magic-numbers': 'error', | ||
'@typescript-eslint/no-meaningless-void-operator': 'error', | ||
'@typescript-eslint/no-misused-new': 'error', | ||
'@typescript-eslint/no-misused-promises': 'error', | ||
'@typescript-eslint/no-mixed-enums': 'error', | ||
'@typescript-eslint/no-namespace': 'error', | ||
'@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'error', | ||
'@typescript-eslint/no-non-null-asserted-optional-chain': 'error', | ||
'@typescript-eslint/no-non-null-assertion': 'error', | ||
'@typescript-eslint/no-parameter-properties': 'error', | ||
'no-redeclare': 'off', | ||
'@typescript-eslint/no-redeclare': 'error', | ||
'@typescript-eslint/no-redundant-type-constituents': 'error', | ||
'@typescript-eslint/no-require-imports': 'error', | ||
'no-restricted-imports': 'off', | ||
'@typescript-eslint/no-restricted-imports': 'error', | ||
'no-shadow': 'off', | ||
@@ -108,4 +121,7 @@ '@typescript-eslint/no-shadow': 'error', | ||
'@typescript-eslint/no-unnecessary-type-constraint': 'error', | ||
'@typescript-eslint/no-unsafe-argument': 'error', | ||
'@typescript-eslint/no-unsafe-assignment': 'error', | ||
'@typescript-eslint/no-unsafe-call': 'error', | ||
'@typescript-eslint/no-unsafe-declaration-merging': 'error', | ||
'@typescript-eslint/no-unsafe-enum-comparison': 'error', | ||
'@typescript-eslint/no-unsafe-member-access': 'error', | ||
@@ -121,2 +137,3 @@ '@typescript-eslint/no-unsafe-return': 'error', | ||
'@typescript-eslint/no-useless-constructor': 'error', | ||
'@typescript-eslint/no-useless-empty-export': 'error', | ||
'@typescript-eslint/no-var-requires': 'error', | ||
@@ -126,2 +143,5 @@ '@typescript-eslint/non-nullable-type-assertion-style': 'error', | ||
'@typescript-eslint/object-curly-spacing': 'error', | ||
'padding-line-between-statements': 'off', | ||
'@typescript-eslint/padding-line-between-statements': 'error', | ||
'@typescript-eslint/parameter-properties': 'error', | ||
'@typescript-eslint/prefer-as-const': 'error', | ||
@@ -140,2 +160,3 @@ '@typescript-eslint/prefer-enum-initializers': 'error', | ||
'@typescript-eslint/prefer-regexp-exec': 'error', | ||
'@typescript-eslint/prefer-return-this-type': 'error', | ||
'@typescript-eslint/prefer-string-starts-ends-with': 'error', | ||
@@ -155,3 +176,5 @@ '@typescript-eslint/prefer-ts-expect-error': 'error', | ||
'@typescript-eslint/semi': 'error', | ||
'@typescript-eslint/sort-type-union-intersection-members': 'error', | ||
'@typescript-eslint/sort-type-constituents': 'error', | ||
'space-before-blocks': 'off', | ||
'@typescript-eslint/space-before-blocks': 'error', | ||
'space-before-function-paren': 'off', | ||
@@ -158,0 +181,0 @@ '@typescript-eslint/space-before-function-paren': 'error', |
@@ -5,3 +5,3 @@ "use strict"; | ||
{ | ||
files: ['*.ts', '*.tsx'], | ||
files: ['*.ts', '*.tsx', '*.mts', '*.cts'], | ||
rules: { | ||
@@ -28,3 +28,3 @@ 'constructor-super': 'off', | ||
'prefer-spread': 'error', | ||
'valid-typeof': 'off', | ||
'valid-typeof': 'off', // ts(2367) | ||
}, | ||
@@ -31,0 +31,0 @@ }, |
@@ -15,2 +15,3 @@ "use strict"; | ||
'@typescript-eslint/no-unnecessary-type-assertion': 'error', | ||
'@typescript-eslint/no-unsafe-argument': 'error', | ||
'@typescript-eslint/no-unsafe-assignment': 'error', | ||
@@ -20,3 +21,2 @@ '@typescript-eslint/no-unsafe-call': 'error', | ||
'@typescript-eslint/no-unsafe-return': 'error', | ||
'@typescript-eslint/prefer-regexp-exec': 'error', | ||
'require-await': 'off', | ||
@@ -23,0 +23,0 @@ '@typescript-eslint/require-await': 'error', |
@@ -11,3 +11,2 @@ "use strict"; | ||
'@typescript-eslint/ban-types': 'error', | ||
'@typescript-eslint/explicit-module-boundary-types': 'warn', | ||
'no-array-constructor': 'off', | ||
@@ -23,2 +22,4 @@ '@typescript-eslint/no-array-constructor': 'error', | ||
'@typescript-eslint/no-inferrable-types': 'error', | ||
'no-loss-of-precision': 'off', | ||
'@typescript-eslint/no-loss-of-precision': 'error', | ||
'@typescript-eslint/no-misused-new': 'error', | ||
@@ -29,2 +30,3 @@ '@typescript-eslint/no-namespace': 'error', | ||
'@typescript-eslint/no-this-alias': 'error', | ||
'@typescript-eslint/no-unnecessary-type-constraint': 'error', | ||
'no-unused-vars': 'off', | ||
@@ -31,0 +33,0 @@ '@typescript-eslint/no-unused-vars': 'warn', |
@@ -5,8 +5,9 @@ "use strict"; | ||
}; | ||
const rules_1 = __importDefault(require("./rules")); | ||
const all_1 = __importDefault(require("./configs/all")); | ||
const base_1 = __importDefault(require("./configs/base")); | ||
const eslint_recommended_1 = __importDefault(require("./configs/eslint-recommended")); | ||
const recommended_1 = __importDefault(require("./configs/recommended")); | ||
const recommended_requiring_type_checking_1 = __importDefault(require("./configs/recommended-requiring-type-checking")); | ||
const eslint_recommended_1 = __importDefault(require("./configs/eslint-recommended")); | ||
const strict_1 = __importDefault(require("./configs/strict")); | ||
const rules_1 = __importDefault(require("./rules")); | ||
module.exports = { | ||
@@ -20,4 +21,5 @@ rules: rules_1.default, | ||
'recommended-requiring-type-checking': recommended_requiring_type_checking_1.default, | ||
strict: strict_1.default, | ||
}, | ||
}; | ||
//# sourceMappingURL=index.js.map |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -30,4 +34,3 @@ exports.default = util.createRule({ | ||
docs: { | ||
description: 'Require that member overloads be consecutive', | ||
category: 'Best Practices', | ||
description: 'Require that function overload signatures be consecutive', | ||
recommended: 'error', | ||
@@ -37,3 +40,3 @@ }, | ||
messages: { | ||
adjacentSignature: "All '{{name}}' signatures should be adjacent.", | ||
adjacentSignature: 'All {{name}} signatures should be adjacent.', | ||
}, | ||
@@ -56,4 +59,4 @@ }, | ||
switch (member.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.ExportDefaultDeclaration: | ||
case experimental_utils_1.AST_NODE_TYPES.ExportNamedDeclaration: { | ||
case utils_1.AST_NODE_TYPES.ExportDefaultDeclaration: | ||
case utils_1.AST_NODE_TYPES.ExportNamedDeclaration: { | ||
// export statements (e.g. export { a };) | ||
@@ -66,6 +69,6 @@ // have no declarations, so ignore them | ||
} | ||
case experimental_utils_1.AST_NODE_TYPES.TSDeclareFunction: | ||
case experimental_utils_1.AST_NODE_TYPES.FunctionDeclaration: { | ||
case utils_1.AST_NODE_TYPES.TSDeclareFunction: | ||
case utils_1.AST_NODE_TYPES.FunctionDeclaration: { | ||
const name = (_b = (_a = member.id) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : null; | ||
if (name === null) { | ||
if (name == null) { | ||
return null; | ||
@@ -77,17 +80,15 @@ } | ||
callSignature: false, | ||
type: util.MemberNameType.Normal, | ||
}; | ||
} | ||
case experimental_utils_1.AST_NODE_TYPES.TSMethodSignature: | ||
case utils_1.AST_NODE_TYPES.TSMethodSignature: | ||
return Object.assign(Object.assign({}, util.getNameFromMember(member, sourceCode)), { static: isStatic, callSignature: false }); | ||
case utils_1.AST_NODE_TYPES.TSCallSignatureDeclaration: | ||
return { | ||
name: util.getNameFromMember(member, sourceCode), | ||
static: isStatic, | ||
callSignature: false, | ||
}; | ||
case experimental_utils_1.AST_NODE_TYPES.TSCallSignatureDeclaration: | ||
return { | ||
name: 'call', | ||
static: isStatic, | ||
callSignature: true, | ||
type: util.MemberNameType.Normal, | ||
}; | ||
case experimental_utils_1.AST_NODE_TYPES.TSConstructSignatureDeclaration: | ||
case utils_1.AST_NODE_TYPES.TSConstructSignatureDeclaration: | ||
return { | ||
@@ -97,9 +98,6 @@ name: 'new', | ||
callSignature: false, | ||
type: util.MemberNameType.Normal, | ||
}; | ||
case experimental_utils_1.AST_NODE_TYPES.MethodDefinition: | ||
return { | ||
name: util.getNameFromMember(member, sourceCode), | ||
static: isStatic, | ||
callSignature: false, | ||
}; | ||
case utils_1.AST_NODE_TYPES.MethodDefinition: | ||
return Object.assign(Object.assign({}, util.getNameFromMember(member, sourceCode)), { static: isStatic, callSignature: false }); | ||
} | ||
@@ -112,12 +110,14 @@ return null; | ||
method1.static === method2.static && | ||
method1.callSignature === method2.callSignature); | ||
method1.callSignature === method2.callSignature && | ||
method1.type === method2.type); | ||
} | ||
function getMembers(node) { | ||
switch (node.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.ClassBody: | ||
case experimental_utils_1.AST_NODE_TYPES.Program: | ||
case experimental_utils_1.AST_NODE_TYPES.TSModuleBlock: | ||
case experimental_utils_1.AST_NODE_TYPES.TSInterfaceBody: | ||
case utils_1.AST_NODE_TYPES.ClassBody: | ||
case utils_1.AST_NODE_TYPES.Program: | ||
case utils_1.AST_NODE_TYPES.TSModuleBlock: | ||
case utils_1.AST_NODE_TYPES.TSInterfaceBody: | ||
case utils_1.AST_NODE_TYPES.BlockStatement: | ||
return node.body; | ||
case experimental_utils_1.AST_NODE_TYPES.TSTypeLiteral: | ||
case utils_1.AST_NODE_TYPES.TSTypeLiteral: | ||
return node.members; | ||
@@ -137,3 +137,3 @@ } | ||
const method = getMemberMethod(member); | ||
if (method === null) { | ||
if (method == null) { | ||
lastMethod = null; | ||
@@ -148,3 +148,3 @@ return; | ||
data: { | ||
name: (method.static ? 'static ' : '') + method.name, | ||
name: `${method.static ? 'static ' : ''}${method.name}`, | ||
}, | ||
@@ -166,2 +166,3 @@ }); | ||
TSInterfaceBody: checkBodyForOverloadMethods, | ||
BlockStatement: checkBodyForOverloadMethods, | ||
}; | ||
@@ -168,0 +169,0 @@ }, |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -31,21 +35,22 @@ /** | ||
switch (node.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.Identifier: | ||
case experimental_utils_1.AST_NODE_TYPES.TSAnyKeyword: | ||
case experimental_utils_1.AST_NODE_TYPES.TSBooleanKeyword: | ||
case experimental_utils_1.AST_NODE_TYPES.TSNeverKeyword: | ||
case experimental_utils_1.AST_NODE_TYPES.TSNumberKeyword: | ||
case experimental_utils_1.AST_NODE_TYPES.TSObjectKeyword: | ||
case experimental_utils_1.AST_NODE_TYPES.TSStringKeyword: | ||
case experimental_utils_1.AST_NODE_TYPES.TSSymbolKeyword: | ||
case experimental_utils_1.AST_NODE_TYPES.TSUnknownKeyword: | ||
case experimental_utils_1.AST_NODE_TYPES.TSVoidKeyword: | ||
case experimental_utils_1.AST_NODE_TYPES.TSNullKeyword: | ||
case experimental_utils_1.AST_NODE_TYPES.TSArrayType: | ||
case experimental_utils_1.AST_NODE_TYPES.TSUndefinedKeyword: | ||
case experimental_utils_1.AST_NODE_TYPES.TSThisType: | ||
case experimental_utils_1.AST_NODE_TYPES.TSQualifiedName: | ||
case utils_1.AST_NODE_TYPES.Identifier: | ||
case utils_1.AST_NODE_TYPES.TSAnyKeyword: | ||
case utils_1.AST_NODE_TYPES.TSBooleanKeyword: | ||
case utils_1.AST_NODE_TYPES.TSNeverKeyword: | ||
case utils_1.AST_NODE_TYPES.TSNumberKeyword: | ||
case utils_1.AST_NODE_TYPES.TSBigIntKeyword: | ||
case utils_1.AST_NODE_TYPES.TSObjectKeyword: | ||
case utils_1.AST_NODE_TYPES.TSStringKeyword: | ||
case utils_1.AST_NODE_TYPES.TSSymbolKeyword: | ||
case utils_1.AST_NODE_TYPES.TSUnknownKeyword: | ||
case utils_1.AST_NODE_TYPES.TSVoidKeyword: | ||
case utils_1.AST_NODE_TYPES.TSNullKeyword: | ||
case utils_1.AST_NODE_TYPES.TSArrayType: | ||
case utils_1.AST_NODE_TYPES.TSUndefinedKeyword: | ||
case utils_1.AST_NODE_TYPES.TSThisType: | ||
case utils_1.AST_NODE_TYPES.TSQualifiedName: | ||
return true; | ||
case experimental_utils_1.AST_NODE_TYPES.TSTypeReference: | ||
case utils_1.AST_NODE_TYPES.TSTypeReference: | ||
if (node.typeName && | ||
node.typeName.type === experimental_utils_1.AST_NODE_TYPES.Identifier && | ||
node.typeName.type === utils_1.AST_NODE_TYPES.Identifier && | ||
node.typeName.name === 'Array') { | ||
@@ -76,11 +81,12 @@ if (!node.typeParameters) { | ||
switch (node.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.TSTypeReference: | ||
case utils_1.AST_NODE_TYPES.TSTypeReference: | ||
return typeNeedsParentheses(node.typeName); | ||
case experimental_utils_1.AST_NODE_TYPES.TSUnionType: | ||
case experimental_utils_1.AST_NODE_TYPES.TSFunctionType: | ||
case experimental_utils_1.AST_NODE_TYPES.TSIntersectionType: | ||
case experimental_utils_1.AST_NODE_TYPES.TSTypeOperator: | ||
case experimental_utils_1.AST_NODE_TYPES.TSInferType: | ||
case utils_1.AST_NODE_TYPES.TSUnionType: | ||
case utils_1.AST_NODE_TYPES.TSFunctionType: | ||
case utils_1.AST_NODE_TYPES.TSIntersectionType: | ||
case utils_1.AST_NODE_TYPES.TSTypeOperator: | ||
case utils_1.AST_NODE_TYPES.TSInferType: | ||
case utils_1.AST_NODE_TYPES.TSConstructorType: | ||
return true; | ||
case experimental_utils_1.AST_NODE_TYPES.Identifier: | ||
case utils_1.AST_NODE_TYPES.Identifier: | ||
return node.name === 'ReadonlyArray'; | ||
@@ -91,3 +97,2 @@ default: | ||
} | ||
const arrayOption = { enum: ['array', 'generic', 'array-simple'] }; | ||
exports.default = util.createRule({ | ||
@@ -98,23 +103,35 @@ name: 'array-type', | ||
docs: { | ||
description: 'Requires using either `T[]` or `Array<T>` for arrays', | ||
category: 'Stylistic Issues', | ||
// too opinionated to be recommended | ||
recommended: false, | ||
description: 'Require consistently using either `T[]` or `Array<T>` for arrays', | ||
recommended: 'strict', | ||
}, | ||
fixable: 'code', | ||
messages: { | ||
errorStringGeneric: "Array type using '{{type}}[]' is forbidden. Use 'Array<{{type}}>' instead.", | ||
errorStringGenericSimple: "Array type using '{{type}}[]' is forbidden for non-simple types. Use 'Array<{{type}}>' instead.", | ||
errorStringArray: "Array type using 'Array<{{type}}>' is forbidden. Use '{{type}}[]' instead.", | ||
errorStringArraySimple: "Array type using 'Array<{{type}}>' is forbidden for simple types. Use '{{type}}[]' instead.", | ||
errorStringGeneric: "Array type using '{{readonlyPrefix}}{{type}}[]' is forbidden. Use '{{className}}<{{type}}>' instead.", | ||
errorStringArray: "Array type using '{{className}}<{{type}}>' is forbidden. Use '{{readonlyPrefix}}{{type}}[]' instead.", | ||
errorStringArraySimple: "Array type using '{{className}}<{{type}}>' is forbidden for simple types. Use '{{readonlyPrefix}}{{type}}[]' instead.", | ||
errorStringGenericSimple: "Array type using '{{readonlyPrefix}}{{type}}[]' is forbidden for non-simple types. Use '{{className}}<{{type}}>' instead.", | ||
}, | ||
schema: [ | ||
{ | ||
type: 'object', | ||
properties: { | ||
default: arrayOption, | ||
readonly: arrayOption, | ||
schema: { | ||
$defs: { | ||
arrayOption: { | ||
enum: ['array', 'generic', 'array-simple'], | ||
}, | ||
}, | ||
], | ||
prefixItems: [ | ||
{ | ||
properties: { | ||
default: { | ||
$ref: '#/$defs/arrayOption', | ||
description: 'The array type expected for mutable cases...', | ||
}, | ||
readonly: { | ||
$ref: '#/$defs/arrayOption', | ||
description: 'The array type expected for readonly cases. If omitted, the value for `default` will be used.', | ||
}, | ||
}, | ||
type: 'object', | ||
}, | ||
], | ||
type: 'array', | ||
}, | ||
}, | ||
@@ -135,9 +152,4 @@ defaultOptions: [ | ||
function getMessageType(node) { | ||
if (node) { | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.TSParenthesizedType) { | ||
return getMessageType(node.typeAnnotation); | ||
} | ||
if (isSimpleType(node)) { | ||
return sourceCode.getText(node); | ||
} | ||
if (node && isSimpleType(node)) { | ||
return sourceCode.getText(node); | ||
} | ||
@@ -149,3 +161,3 @@ return 'T'; | ||
const isReadonly = node.parent && | ||
node.parent.type === experimental_utils_1.AST_NODE_TYPES.TSTypeOperator && | ||
node.parent.type === utils_1.AST_NODE_TYPES.TSTypeOperator && | ||
node.parent.operator === 'readonly'; | ||
@@ -165,8 +177,8 @@ const currentOption = isReadonly ? readonlyOption : defaultOption; | ||
data: { | ||
className: isReadonly ? 'ReadonlyArray' : 'Array', | ||
readonlyPrefix: isReadonly ? 'readonly ' : '', | ||
type: getMessageType(node.elementType), | ||
}, | ||
fix(fixer) { | ||
const typeNode = node.elementType.type === experimental_utils_1.AST_NODE_TYPES.TSParenthesizedType | ||
? node.elementType.typeAnnotation | ||
: node.elementType; | ||
const typeNode = node.elementType; | ||
const arrayType = isReadonly ? 'ReadonlyArray' : 'Array'; | ||
@@ -182,3 +194,3 @@ return [ | ||
var _a, _b; | ||
if (node.typeName.type !== experimental_utils_1.AST_NODE_TYPES.Identifier || | ||
if (node.typeName.type !== utils_1.AST_NODE_TYPES.Identifier || | ||
!(node.typeName.name === 'Array' || | ||
@@ -206,2 +218,4 @@ node.typeName.name === 'ReadonlyArray')) { | ||
data: { | ||
className: isReadonlyArrayType ? 'ReadonlyArray' : 'Array', | ||
readonlyPrefix, | ||
type: 'any', | ||
@@ -221,3 +235,5 @@ }, | ||
const typeParens = typeNeedsParentheses(type); | ||
const parentParens = readonlyPrefix && ((_b = node.parent) === null || _b === void 0 ? void 0 : _b.type) === experimental_utils_1.AST_NODE_TYPES.TSArrayType; | ||
const parentParens = readonlyPrefix && | ||
((_b = node.parent) === null || _b === void 0 ? void 0 : _b.type) === utils_1.AST_NODE_TYPES.TSArrayType && | ||
!util.isParenthesized(node.parent.elementType, sourceCode); | ||
const start = `${parentParens ? '(' : ''}${readonlyPrefix}${typeParens ? '(' : ''}`; | ||
@@ -229,2 +245,4 @@ const end = `${typeParens ? ')' : ''}[]${parentParens ? ')' : ''}`; | ||
data: { | ||
className: isReadonlyArrayType ? 'ReadonlyArray' : 'Array', | ||
readonlyPrefix, | ||
type: getMessageType(type), | ||
@@ -231,0 +249,0 @@ }, |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -28,4 +32,3 @@ if (k2 === undefined) k2 = k; | ||
docs: { | ||
description: 'Disallows awaiting a value that is not a Thenable', | ||
category: 'Best Practices', | ||
description: 'Disallow awaiting a value that is not a Thenable', | ||
recommended: 'error', | ||
@@ -32,0 +35,0 @@ requiresTypeChecking: true, |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -23,3 +27,3 @@ if (k2 === undefined) k2 = k; | ||
exports.defaultMinimumDescriptionLength = void 0; | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -32,4 +36,3 @@ exports.defaultMinimumDescriptionLength = 3; | ||
docs: { | ||
description: 'Bans `@ts-<directive>` comments from being used or requires descriptions after directive', | ||
category: 'Best Practices', | ||
description: 'Disallow `@ts-<directive>` comments or require descriptions after directives', | ||
recommended: 'error', | ||
@@ -40,59 +43,43 @@ }, | ||
tsDirectiveCommentRequiresDescription: 'Include a description after the "@ts-{{directive}}" directive to explain why the @ts-{{directive}} is necessary. The description must be {{minimumDescriptionLength}} characters or longer.', | ||
tsDirectiveCommentDescriptionNotMatchPattern: 'The description for the "@ts-{{directive}}" directive must match the {{format}} format.', | ||
}, | ||
schema: [ | ||
{ | ||
type: 'object', | ||
properties: { | ||
'ts-expect-error': { | ||
oneOf: [ | ||
{ | ||
type: 'boolean', | ||
default: true, | ||
schema: { | ||
$defs: { | ||
directiveConfigSchema: { | ||
oneOf: [ | ||
{ | ||
type: 'boolean', | ||
default: true, | ||
}, | ||
{ | ||
enum: ['allow-with-description'], | ||
}, | ||
{ | ||
type: 'object', | ||
properties: { | ||
descriptionFormat: { type: 'string' }, | ||
}, | ||
{ | ||
enum: ['allow-with-description'], | ||
}, | ||
], | ||
}, | ||
], | ||
}, | ||
}, | ||
prefixItems: [ | ||
{ | ||
properties: { | ||
'ts-expect-error': { | ||
$ref: '#/$defs/directiveConfigSchema', | ||
}, | ||
'ts-ignore': { $ref: '#/$defs/directiveConfigSchema' }, | ||
'ts-nocheck': { $ref: '#/$defs/directiveConfigSchema' }, | ||
'ts-check': { $ref: '#/$defs/directiveConfigSchema' }, | ||
minimumDescriptionLength: { | ||
type: 'number', | ||
default: exports.defaultMinimumDescriptionLength, | ||
}, | ||
}, | ||
'ts-ignore': { | ||
oneOf: [ | ||
{ | ||
type: 'boolean', | ||
default: true, | ||
}, | ||
{ | ||
enum: ['allow-with-description'], | ||
}, | ||
], | ||
}, | ||
'ts-nocheck': { | ||
oneOf: [ | ||
{ | ||
type: 'boolean', | ||
default: true, | ||
}, | ||
{ | ||
enum: ['allow-with-description'], | ||
}, | ||
], | ||
}, | ||
'ts-check': { | ||
oneOf: [ | ||
{ | ||
type: 'boolean', | ||
default: true, | ||
}, | ||
{ | ||
enum: ['allow-with-description'], | ||
}, | ||
], | ||
}, | ||
minimumDescriptionLength: { | ||
type: 'number', | ||
default: exports.defaultMinimumDescriptionLength, | ||
}, | ||
additionalProperties: false, | ||
}, | ||
additionalProperties: false, | ||
}, | ||
], | ||
], | ||
type: 'array', | ||
}, | ||
}, | ||
@@ -111,7 +98,19 @@ defaultOptions: [ | ||
The regex used are taken from the ones used in the official TypeScript repo - | ||
https://github.com/microsoft/TypeScript/blob/master/src/compiler/scanner.ts#L281-L289 | ||
https://github.com/microsoft/TypeScript/blob/408c760fae66080104bc85c449282c2d207dfe8e/src/compiler/scanner.ts#L288-L296 | ||
*/ | ||
const commentDirectiveRegExSingleLine = /^\/*\s*@ts-(expect-error|ignore|check|nocheck)(.*)/; | ||
const commentDirectiveRegExMultiLine = /^\s*(?:\/|\*)*\s*@ts-(expect-error|ignore|check|nocheck)(.*)/; | ||
const commentDirectiveRegExSingleLine = /^\/*\s*@ts-(?<directive>expect-error|ignore|check|nocheck)(?<description>.*)/; | ||
const commentDirectiveRegExMultiLine = /^\s*(?:\/|\*)*\s*@ts-(?<directive>expect-error|ignore|check|nocheck)(?<description>.*)/; | ||
const sourceCode = context.getSourceCode(); | ||
const descriptionFormats = new Map(); | ||
for (const directive of [ | ||
'ts-expect-error', | ||
'ts-ignore', | ||
'ts-nocheck', | ||
'ts-check', | ||
]) { | ||
const option = options[directive]; | ||
if (typeof option === 'object' && option.descriptionFormat) { | ||
descriptionFormats.set(directive, new RegExp(option.descriptionFormat)); | ||
} | ||
} | ||
return { | ||
@@ -121,8 +120,10 @@ Program() { | ||
comments.forEach(comment => { | ||
var _a; | ||
let regExp = commentDirectiveRegExSingleLine; | ||
if (comment.type !== experimental_utils_1.AST_TOKEN_TYPES.Line) { | ||
regExp = commentDirectiveRegExMultiLine; | ||
const regExp = comment.type === utils_1.AST_TOKEN_TYPES.Line | ||
? commentDirectiveRegExSingleLine | ||
: commentDirectiveRegExMultiLine; | ||
const match = regExp.exec(comment.value); | ||
if (!match) { | ||
return; | ||
} | ||
const [, directive, description] = (_a = regExp.exec(comment.value)) !== null && _a !== void 0 ? _a : []; | ||
const { directive, description } = match.groups; | ||
const fullDirective = `ts-${directive}`; | ||
@@ -137,5 +138,8 @@ const option = options[fullDirective]; | ||
} | ||
if (option === 'allow-with-description') { | ||
if (option === 'allow-with-description' || | ||
(typeof option === 'object' && option.descriptionFormat)) { | ||
const { minimumDescriptionLength = exports.defaultMinimumDescriptionLength, } = options; | ||
if (description.trim().length < minimumDescriptionLength) { | ||
const format = descriptionFormats.get(fullDirective); | ||
if (util.getStringLength(description.trim()) < | ||
minimumDescriptionLength) { | ||
context.report({ | ||
@@ -147,2 +151,9 @@ data: { directive, minimumDescriptionLength }, | ||
} | ||
else if (format && !format.test(description)) { | ||
context.report({ | ||
data: { directive, format: format.source }, | ||
node: comment, | ||
messageId: 'tsDirectiveCommentDescriptionNotMatchPattern', | ||
}); | ||
} | ||
} | ||
@@ -149,0 +160,0 @@ }); |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -28,3 +32,3 @@ // tslint regex | ||
const ENABLE_DISABLE_REGEX = /^\s*tslint:(enable|disable)(?:-(line|next-line))?(:|\s|$)/; | ||
const toText = (text, type) => type === experimental_utils_1.AST_TOKEN_TYPES.Line | ||
const toText = (text, type) => type === utils_1.AST_TOKEN_TYPES.Line | ||
? ['//', text.trim()].join(' ') | ||
@@ -37,5 +41,4 @@ : ['/*', text.trim(), '*/'].join(' '); | ||
docs: { | ||
description: 'Bans `// tslint:<rule-flag>` comments from being used', | ||
category: 'Stylistic Issues', | ||
recommended: false, | ||
description: 'Disallow `// tslint:<rule-flag>` comments', | ||
recommended: 'strict', | ||
}, | ||
@@ -42,0 +45,0 @@ messages: { |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -23,6 +27,6 @@ if (k2 === undefined) k2 = k; | ||
exports.TYPE_KEYWORDS = void 0; | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
function removeSpaces(str) { | ||
return str.replace(/ /g, ''); | ||
return str.replace(/\s/g, ''); | ||
} | ||
@@ -33,3 +37,3 @@ function stringifyNode(node, sourceCode) { | ||
function getCustomMessage(bannedType) { | ||
if (bannedType === null) { | ||
if (bannedType == null) { | ||
return ''; | ||
@@ -62,2 +66,6 @@ } | ||
}, | ||
BigInt: { | ||
message: 'Use bigint instead', | ||
fixWith: 'bigint', | ||
}, | ||
Function: { | ||
@@ -75,5 +83,7 @@ message: [ | ||
'The `Object` type actually means "any non-nullish value", so it is marginally better than `unknown`.', | ||
'- If you want a type meaning "any object", you probably want `Record<string, unknown>` instead.', | ||
'- If you want a type meaning "any object", you probably want `object` instead.', | ||
'- If you want a type meaning "any value", you probably want `unknown` instead.', | ||
'- If you really want a type meaning "any non-nullish value", you probably want `NonNullable<unknown>` instead.', | ||
].join('\n'), | ||
suggest: ['object', 'unknown', 'NonNullable<unknown>'], | ||
}, | ||
@@ -83,26 +93,27 @@ '{}': { | ||
'`{}` actually means "any non-nullish value".', | ||
'- If you want a type meaning "any object", you probably want `Record<string, unknown>` instead.', | ||
'- If you want a type meaning "any object", you probably want `object` instead.', | ||
'- If you want a type meaning "any value", you probably want `unknown` instead.', | ||
'- If you want a type meaning "empty object", you probably want `Record<string, never>` instead.', | ||
'- If you really want a type meaning "any non-nullish value", you probably want `NonNullable<unknown>` instead.', | ||
].join('\n'), | ||
suggest: [ | ||
'object', | ||
'unknown', | ||
'Record<string, never>', | ||
'NonNullable<unknown>', | ||
], | ||
}, | ||
object: { | ||
message: [ | ||
'The `object` type is currently hard to use ([see this issue](https://github.com/microsoft/TypeScript/issues/21732)).', | ||
'Consider using `Record<string, unknown>` instead, as it allows you to more easily inspect and use the keys.', | ||
].join('\n'), | ||
}, | ||
}; | ||
exports.TYPE_KEYWORDS = { | ||
bigint: experimental_utils_1.AST_NODE_TYPES.TSBigIntKeyword, | ||
boolean: experimental_utils_1.AST_NODE_TYPES.TSBooleanKeyword, | ||
never: experimental_utils_1.AST_NODE_TYPES.TSNeverKeyword, | ||
null: experimental_utils_1.AST_NODE_TYPES.TSNullKeyword, | ||
number: experimental_utils_1.AST_NODE_TYPES.TSNumberKeyword, | ||
object: experimental_utils_1.AST_NODE_TYPES.TSObjectKeyword, | ||
string: experimental_utils_1.AST_NODE_TYPES.TSStringKeyword, | ||
symbol: experimental_utils_1.AST_NODE_TYPES.TSSymbolKeyword, | ||
undefined: experimental_utils_1.AST_NODE_TYPES.TSUndefinedKeyword, | ||
unknown: experimental_utils_1.AST_NODE_TYPES.TSUnknownKeyword, | ||
void: experimental_utils_1.AST_NODE_TYPES.TSVoidKeyword, | ||
bigint: utils_1.AST_NODE_TYPES.TSBigIntKeyword, | ||
boolean: utils_1.AST_NODE_TYPES.TSBooleanKeyword, | ||
never: utils_1.AST_NODE_TYPES.TSNeverKeyword, | ||
null: utils_1.AST_NODE_TYPES.TSNullKeyword, | ||
number: utils_1.AST_NODE_TYPES.TSNumberKeyword, | ||
object: utils_1.AST_NODE_TYPES.TSObjectKeyword, | ||
string: utils_1.AST_NODE_TYPES.TSStringKeyword, | ||
symbol: utils_1.AST_NODE_TYPES.TSSymbolKeyword, | ||
undefined: utils_1.AST_NODE_TYPES.TSUndefinedKeyword, | ||
unknown: utils_1.AST_NODE_TYPES.TSUnknownKeyword, | ||
void: utils_1.AST_NODE_TYPES.TSVoidKeyword, | ||
}; | ||
@@ -114,9 +125,10 @@ exports.default = util.createRule({ | ||
docs: { | ||
description: 'Bans specific types from being used', | ||
category: 'Best Practices', | ||
description: 'Disallow certain types', | ||
recommended: 'error', | ||
}, | ||
fixable: 'code', | ||
hasSuggestions: true, | ||
messages: { | ||
bannedTypeMessage: "Don't use `{{name}}` as a type.{{customMessage}}", | ||
bannedTypeReplacement: 'Replace `{{name}}` with `{{replacement}}`.', | ||
}, | ||
@@ -139,2 +151,6 @@ schema: [ | ||
fixWith: { type: 'string' }, | ||
suggest: { | ||
type: 'array', | ||
items: { type: 'string' }, | ||
}, | ||
}, | ||
@@ -168,2 +184,5 @@ additionalProperties: false, | ||
const fixWith = bannedType && typeof bannedType === 'object' && bannedType.fixWith; | ||
const suggest = bannedType && typeof bannedType === 'object' | ||
? bannedType.suggest | ||
: undefined; | ||
context.report({ | ||
@@ -179,2 +198,10 @@ node: typeNode, | ||
: null, | ||
suggest: suggest === null || suggest === void 0 ? void 0 : suggest.map(replacement => ({ | ||
messageId: 'bannedTypeReplacement', | ||
data: { | ||
name, | ||
replacement, | ||
}, | ||
fix: (fixer) => fixer.replaceText(typeNode, replacement), | ||
})), | ||
}); | ||
@@ -204,2 +231,8 @@ } | ||
} | ||
}, | ||
TSInterfaceHeritage(node) { | ||
checkBannedTypes(node); | ||
}, | ||
TSClassImplements(node) { | ||
checkBannedTypes(node); | ||
} }); | ||
@@ -206,0 +239,0 @@ }, |
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const brace_style_1 = __importDefault(require("eslint/lib/rules/brace-style")); | ||
const util_1 = require("../util"); | ||
exports.default = util_1.createRule({ | ||
const getESLintCoreRule_1 = require("../util/getESLintCoreRule"); | ||
const baseRule = (0, getESLintCoreRule_1.getESLintCoreRule)('brace-style'); | ||
exports.default = (0, util_1.createRule)({ | ||
name: 'brace-style', | ||
@@ -14,16 +12,18 @@ meta: { | ||
description: 'Enforce consistent brace style for blocks', | ||
category: 'Stylistic Issues', | ||
recommended: false, | ||
extendsBaseRule: true, | ||
}, | ||
messages: brace_style_1.default.meta.messages, | ||
fixable: brace_style_1.default.meta.fixable, | ||
schema: brace_style_1.default.meta.schema, | ||
messages: baseRule.meta.messages, | ||
fixable: baseRule.meta.fixable, | ||
hasSuggestions: baseRule.meta.hasSuggestions, | ||
schema: baseRule.meta.schema, | ||
}, | ||
defaultOptions: ['1tbs'], | ||
create(context) { | ||
const [style, { allowSingleLine } = { allowSingleLine: false },] = context.options; | ||
const [style, { allowSingleLine } = { allowSingleLine: false }] = | ||
// eslint-disable-next-line no-restricted-syntax -- Use raw options for extended rules. | ||
context.options; | ||
const isAllmanStyle = style === 'allman'; | ||
const sourceCode = context.getSourceCode(); | ||
const rules = brace_style_1.default.create(context); | ||
const rules = baseRule.create(context); | ||
/** | ||
@@ -34,3 +34,3 @@ * Checks a pair of curly brackets based on the user's config | ||
if (allowSingleLine && | ||
util_1.isTokenOnSameLine(openingCurlyToken, closingCurlyToken)) { | ||
(0, util_1.isTokenOnSameLine)(openingCurlyToken, closingCurlyToken)) { | ||
return; | ||
@@ -42,3 +42,3 @@ } | ||
if (!isAllmanStyle && | ||
!util_1.isTokenOnSameLine(tokenBeforeOpeningCurly, openingCurlyToken)) { | ||
!(0, util_1.isTokenOnSameLine)(tokenBeforeOpeningCurly, openingCurlyToken)) { | ||
context.report({ | ||
@@ -61,3 +61,3 @@ node: openingCurlyToken, | ||
if (isAllmanStyle && | ||
util_1.isTokenOnSameLine(tokenBeforeOpeningCurly, openingCurlyToken)) { | ||
(0, util_1.isTokenOnSameLine)(tokenBeforeOpeningCurly, openingCurlyToken)) { | ||
context.report({ | ||
@@ -69,3 +69,3 @@ node: openingCurlyToken, | ||
} | ||
if (util_1.isTokenOnSameLine(openingCurlyToken, tokenAfterOpeningCurly) && | ||
if ((0, util_1.isTokenOnSameLine)(openingCurlyToken, tokenAfterOpeningCurly) && | ||
tokenAfterOpeningCurly !== closingCurlyToken) { | ||
@@ -78,3 +78,3 @@ context.report({ | ||
} | ||
if (util_1.isTokenOnSameLine(tokenBeforeClosingCurly, closingCurlyToken) && | ||
if ((0, util_1.isTokenOnSameLine)(tokenBeforeClosingCurly, closingCurlyToken) && | ||
tokenBeforeClosingCurly !== openingCurlyToken) { | ||
@@ -81,0 +81,0 @@ context.report({ |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,14 +26,14 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
const printNodeModifiers = (node, final) => { | ||
var _a; | ||
return `${(_a = node.accessibility) !== null && _a !== void 0 ? _a : ''}${node.static ? ' static' : ''} ${final} `.trimLeft(); | ||
return `${(_a = node.accessibility) !== null && _a !== void 0 ? _a : ''}${node.static ? ' static' : ''} ${final} `.trimStart(); | ||
}; | ||
const isSupportedLiteral = (node) => { | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.Literal) { | ||
if (node.type === utils_1.AST_NODE_TYPES.Literal) { | ||
return true; | ||
} | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.TaggedTemplateExpression || | ||
node.type === experimental_utils_1.AST_NODE_TYPES.TemplateLiteral) { | ||
if (node.type === utils_1.AST_NODE_TYPES.TaggedTemplateExpression || | ||
node.type === utils_1.AST_NODE_TYPES.TemplateLiteral) { | ||
return ('quasi' in node ? node.quasi.quasis : node.quasis).length === 1; | ||
@@ -44,10 +48,11 @@ } | ||
docs: { | ||
description: 'Ensures that literals on classes are exposed in a consistent style', | ||
category: 'Best Practices', | ||
recommended: false, | ||
description: 'Enforce that literals on classes are exposed in a consistent style', | ||
recommended: 'strict', | ||
}, | ||
fixable: 'code', | ||
hasSuggestions: true, | ||
messages: { | ||
preferFieldStyle: 'Literals should be exposed using readonly fields.', | ||
preferFieldStyleSuggestion: 'Replace the literals with readonly fields.', | ||
preferGetterStyle: 'Literals should be exposed using getters.', | ||
preferGetterStyleSuggestion: 'Replace the literals with getters.', | ||
}, | ||
@@ -58,36 +63,38 @@ schema: [{ enum: ['fields', 'getters'] }], | ||
create(context, [style]) { | ||
if (style === 'fields') { | ||
return { | ||
MethodDefinition(node) { | ||
if (node.kind !== 'get' || | ||
!node.value.body || | ||
!node.value.body.body.length) { | ||
return; | ||
} | ||
const [statement] = node.value.body.body; | ||
if (statement.type !== experimental_utils_1.AST_NODE_TYPES.ReturnStatement) { | ||
return; | ||
} | ||
const { argument } = statement; | ||
if (!argument || !isSupportedLiteral(argument)) { | ||
return; | ||
} | ||
context.report({ | ||
node: node.key, | ||
messageId: 'preferFieldStyle', | ||
fix(fixer) { | ||
const sourceCode = context.getSourceCode(); | ||
const name = sourceCode.getText(node.key); | ||
let text = ''; | ||
text += printNodeModifiers(node, 'readonly'); | ||
text += node.computed ? `[${name}]` : name; | ||
text += ` = ${sourceCode.getText(argument)};`; | ||
return fixer.replaceText(node, text); | ||
return Object.assign(Object.assign({}, (style === 'fields' && { | ||
MethodDefinition(node) { | ||
if (node.kind !== 'get' || | ||
!node.value.body || | ||
!node.value.body.body.length) { | ||
return; | ||
} | ||
const [statement] = node.value.body.body; | ||
if (statement.type !== utils_1.AST_NODE_TYPES.ReturnStatement) { | ||
return; | ||
} | ||
const { argument } = statement; | ||
if (!argument || !isSupportedLiteral(argument)) { | ||
return; | ||
} | ||
context.report({ | ||
node: node.key, | ||
messageId: 'preferFieldStyle', | ||
suggest: [ | ||
{ | ||
messageId: 'preferFieldStyleSuggestion', | ||
fix(fixer) { | ||
const sourceCode = context.getSourceCode(); | ||
const name = sourceCode.getText(node.key); | ||
let text = ''; | ||
text += printNodeModifiers(node, 'readonly'); | ||
text += node.computed ? `[${name}]` : name; | ||
text += ` = ${sourceCode.getText(argument)};`; | ||
return fixer.replaceText(node, text); | ||
}, | ||
}, | ||
}); | ||
}, | ||
}; | ||
} | ||
return { | ||
ClassProperty(node) { | ||
], | ||
}); | ||
}, | ||
})), (style === 'getters' && { | ||
PropertyDefinition(node) { | ||
if (!node.readonly || node.declare) { | ||
@@ -103,16 +110,21 @@ return; | ||
messageId: 'preferGetterStyle', | ||
fix(fixer) { | ||
const sourceCode = context.getSourceCode(); | ||
const name = sourceCode.getText(node.key); | ||
let text = ''; | ||
text += printNodeModifiers(node, 'get'); | ||
text += node.computed ? `[${name}]` : name; | ||
text += `() { return ${sourceCode.getText(value)}; }`; | ||
return fixer.replaceText(node, text); | ||
}, | ||
suggest: [ | ||
{ | ||
messageId: 'preferGetterStyleSuggestion', | ||
fix(fixer) { | ||
const sourceCode = context.getSourceCode(); | ||
const name = sourceCode.getText(node.key); | ||
let text = ''; | ||
text += printNodeModifiers(node, 'get'); | ||
text += node.computed ? `[${name}]` : name; | ||
text += `() { return ${sourceCode.getText(value)}; }`; | ||
return fixer.replaceText(node, text); | ||
}, | ||
}, | ||
], | ||
}); | ||
}, | ||
}; | ||
})); | ||
}, | ||
}); | ||
//# sourceMappingURL=class-literal-property-style.js.map |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -21,9 +25,7 @@ if (k2 === undefined) k2 = k; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
const comma_dangle_1 = __importDefault(require("eslint/lib/rules/comma-dangle")); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const getESLintCoreRule_1 = require("../util/getESLintCoreRule"); | ||
const baseRule = (0, getESLintCoreRule_1.getESLintCoreRule)('comma-dangle'); | ||
const OPTION_VALUE_SCHEME = [ | ||
@@ -56,4 +58,3 @@ 'always-multiline', | ||
docs: { | ||
description: 'Require or disallow trailing comma', | ||
category: 'Stylistic Issues', | ||
description: 'Require or disallow trailing commas', | ||
recommended: false, | ||
@@ -63,3 +64,3 @@ extendsBaseRule: true, | ||
schema: { | ||
definitions: { | ||
$defs: { | ||
value: { | ||
@@ -77,3 +78,3 @@ enum: OPTION_VALUE_SCHEME, | ||
{ | ||
$ref: '#/definitions/value', | ||
$ref: '#/$defs/value', | ||
}, | ||
@@ -83,10 +84,10 @@ { | ||
properties: { | ||
arrays: { $ref: '#/definitions/valueWithIgnore' }, | ||
objects: { $ref: '#/definitions/valueWithIgnore' }, | ||
imports: { $ref: '#/definitions/valueWithIgnore' }, | ||
exports: { $ref: '#/definitions/valueWithIgnore' }, | ||
functions: { $ref: '#/definitions/valueWithIgnore' }, | ||
enums: { $ref: '#/definitions/valueWithIgnore' }, | ||
generics: { $ref: '#/definitions/valueWithIgnore' }, | ||
tuples: { $ref: '#/definitions/valueWithIgnore' }, | ||
arrays: { $ref: '#/$defs/valueWithIgnore' }, | ||
objects: { $ref: '#/$defs/valueWithIgnore' }, | ||
imports: { $ref: '#/$defs/valueWithIgnore' }, | ||
exports: { $ref: '#/$defs/valueWithIgnore' }, | ||
functions: { $ref: '#/$defs/valueWithIgnore' }, | ||
enums: { $ref: '#/$defs/valueWithIgnore' }, | ||
generics: { $ref: '#/$defs/valueWithIgnore' }, | ||
tuples: { $ref: '#/$defs/valueWithIgnore' }, | ||
}, | ||
@@ -98,9 +99,11 @@ additionalProperties: false, | ||
], | ||
additionalProperties: false, | ||
}, | ||
fixable: 'code', | ||
messages: comma_dangle_1.default.meta.messages, | ||
hasSuggestions: baseRule.meta.hasSuggestions, | ||
messages: baseRule.meta.messages, | ||
}, | ||
defaultOptions: ['never'], | ||
create(context, [options]) { | ||
const rules = comma_dangle_1.default.create(context); | ||
const rules = baseRule.create(context); | ||
const sourceCode = context.getSourceCode(); | ||
@@ -121,7 +124,7 @@ const normalizedOptions = normalizeOptions(options); | ||
switch (node.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.TSEnumDeclaration: | ||
case utils_1.AST_NODE_TYPES.TSEnumDeclaration: | ||
return last(node.members); | ||
case experimental_utils_1.AST_NODE_TYPES.TSTypeParameterDeclaration: | ||
case utils_1.AST_NODE_TYPES.TSTypeParameterDeclaration: | ||
return last(node.params); | ||
case experimental_utils_1.AST_NODE_TYPES.TSTupleType: | ||
case utils_1.AST_NODE_TYPES.TSTupleType: | ||
return last(node.elementTypes); | ||
@@ -128,0 +131,0 @@ default: |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util_1 = require("../util"); | ||
exports.default = util_1.createRule({ | ||
exports.default = (0, util_1.createRule)({ | ||
name: 'comma-spacing', | ||
meta: { | ||
type: 'suggestion', | ||
type: 'layout', | ||
docs: { | ||
description: 'Enforces consistent spacing before and after commas', | ||
category: 'Stylistic Issues', | ||
description: 'Enforce consistent spacing before and after commas', | ||
recommended: false, | ||
@@ -55,5 +54,5 @@ extendsBaseRule: true, | ||
let token; | ||
if (element === null) { | ||
if (element == null) { | ||
token = sourceCode.getTokenAfter(previousToken); | ||
if (token && util_1.isCommaToken(token)) { | ||
if (token && (0, util_1.isCommaToken)(token)) { | ||
ignoredTokens.add(token); | ||
@@ -77,3 +76,3 @@ } | ||
const afterToken = sourceCode.getTokenAfter(param); | ||
if (afterToken && util_1.isCommaToken(afterToken)) { | ||
if (afterToken && (0, util_1.isCommaToken)(afterToken)) { | ||
ignoredTokens.add(afterToken); | ||
@@ -91,3 +90,4 @@ } | ||
if (prevToken && | ||
util_1.isTokenOnSameLine(prevToken, commaToken) && | ||
(0, util_1.isTokenOnSameLine)(prevToken, commaToken) && | ||
// eslint-disable-next-line deprecation/deprecation -- TODO - switch once our min ESLint version is 6.7.0 | ||
spaceBefore !== sourceCode.isSpaceBetweenTokens(prevToken, commaToken)) { | ||
@@ -105,10 +105,16 @@ context.report({ | ||
} | ||
if (nextToken && util_1.isClosingParenToken(nextToken)) { | ||
if (nextToken && (0, util_1.isClosingParenToken)(nextToken)) { | ||
return; | ||
} | ||
if (!spaceAfter && nextToken && nextToken.type === experimental_utils_1.AST_TOKEN_TYPES.Line) { | ||
if (spaceAfter && | ||
nextToken && | ||
((0, util_1.isClosingBraceToken)(nextToken) || (0, util_1.isClosingBracketToken)(nextToken))) { | ||
return; | ||
} | ||
if (!spaceAfter && nextToken && nextToken.type === utils_1.AST_TOKEN_TYPES.Line) { | ||
return; | ||
} | ||
if (nextToken && | ||
util_1.isTokenOnSameLine(commaToken, nextToken) && | ||
(0, util_1.isTokenOnSameLine)(commaToken, nextToken) && | ||
// eslint-disable-next-line deprecation/deprecation -- TODO - switch once our min ESLint version is 6.7.0 | ||
spaceAfter !== sourceCode.isSpaceBetweenTokens(commaToken, nextToken)) { | ||
@@ -133,3 +139,3 @@ context.report({ | ||
tokensAndComments.forEach((token, i) => { | ||
if (!util_1.isCommaToken(token)) { | ||
if (!(0, util_1.isCommaToken)(token)) { | ||
return; | ||
@@ -139,5 +145,5 @@ } | ||
const nextToken = tokensAndComments[i + 1]; | ||
validateCommaSpacing(token, util_1.isCommaToken(prevToken) || ignoredTokens.has(token) | ||
validateCommaSpacing(token, (0, util_1.isCommaToken)(prevToken) || ignoredTokens.has(token) | ||
? null | ||
: prevToken, util_1.isCommaToken(nextToken) || ignoredTokens.has(token) | ||
: prevToken, (nextToken && (0, util_1.isCommaToken)(nextToken)) || ignoredTokens.has(token) | ||
? null | ||
@@ -144,0 +150,0 @@ : nextToken); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util_1 = require("../util"); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
exports.default = util_1.createRule({ | ||
exports.default = (0, util_1.createRule)({ | ||
name: 'consistent-indexed-object-style', | ||
@@ -10,9 +10,7 @@ meta: { | ||
docs: { | ||
description: 'Enforce or disallow the use of the record type', | ||
category: 'Stylistic Issues', | ||
// too opinionated to be recommended | ||
recommended: false, | ||
description: 'Require or disallow the `Record` type', | ||
recommended: 'strict', | ||
}, | ||
messages: { | ||
preferRecord: 'A record is preferred over an index signature', | ||
preferRecord: 'A record is preferred over an index signature.', | ||
preferIndexSignature: 'An index signature is preferred over a record.', | ||
@@ -28,32 +26,5 @@ }, | ||
defaultOptions: ['record'], | ||
create(context) { | ||
create(context, [mode]) { | ||
const sourceCode = context.getSourceCode(); | ||
if (context.options[0] === 'index-signature') { | ||
return { | ||
TSTypeReference(node) { | ||
var _a; | ||
const typeName = node.typeName; | ||
if (typeName.type !== experimental_utils_1.AST_NODE_TYPES.Identifier) { | ||
return; | ||
} | ||
if (typeName.name !== 'Record') { | ||
return; | ||
} | ||
const params = (_a = node.typeParameters) === null || _a === void 0 ? void 0 : _a.params; | ||
if ((params === null || params === void 0 ? void 0 : params.length) !== 2) { | ||
return; | ||
} | ||
context.report({ | ||
node, | ||
messageId: 'preferIndexSignature', | ||
fix(fixer) { | ||
const key = sourceCode.getText(params[0]); | ||
const type = sourceCode.getText(params[1]); | ||
return fixer.replaceText(node, `{ [key: ${key}]: ${type} }`); | ||
}, | ||
}); | ||
}, | ||
}; | ||
} | ||
function checkMembers(members, node, prefix, postfix) { | ||
function checkMembers(members, node, parentId, prefix, postfix, safeFix = true) { | ||
if (members.length !== 1) { | ||
@@ -63,3 +34,3 @@ return; | ||
const [member] = members; | ||
if (member.type !== experimental_utils_1.AST_NODE_TYPES.TSIndexSignature) { | ||
if (member.type !== utils_1.AST_NODE_TYPES.TSIndexSignature) { | ||
return; | ||
@@ -71,3 +42,3 @@ } | ||
} | ||
if (parameter.type !== experimental_utils_1.AST_NODE_TYPES.Identifier) { | ||
if (parameter.type !== utils_1.AST_NODE_TYPES.Identifier) { | ||
return; | ||
@@ -83,30 +54,78 @@ } | ||
} | ||
if (parentId) { | ||
const scope = context.getScope(); | ||
const superVar = utils_1.ASTUtils.findVariable(scope, parentId.name); | ||
if (superVar) { | ||
const isCircular = superVar.references.some(item => item.isTypeReference && | ||
node.range[0] <= item.identifier.range[0] && | ||
node.range[1] >= item.identifier.range[1]); | ||
if (isCircular) { | ||
return; | ||
} | ||
} | ||
} | ||
context.report({ | ||
node, | ||
messageId: 'preferRecord', | ||
fix(fixer) { | ||
const key = sourceCode.getText(keyType.typeAnnotation); | ||
const value = sourceCode.getText(valueType.typeAnnotation); | ||
const record = member.readonly | ||
? `Readonly<Record<${key}, ${value}>>` | ||
: `Record<${key}, ${value}>`; | ||
return fixer.replaceText(node, `${prefix}${record}${postfix}`); | ||
}, | ||
fix: safeFix | ||
? (fixer) => { | ||
const key = sourceCode.getText(keyType.typeAnnotation); | ||
const value = sourceCode.getText(valueType.typeAnnotation); | ||
const record = member.readonly | ||
? `Readonly<Record<${key}, ${value}>>` | ||
: `Record<${key}, ${value}>`; | ||
return fixer.replaceText(node, `${prefix}${record}${postfix}`); | ||
} | ||
: null, | ||
}); | ||
} | ||
return { | ||
return Object.assign(Object.assign({}, (mode === 'index-signature' && { | ||
TSTypeReference(node) { | ||
var _a; | ||
const typeName = node.typeName; | ||
if (typeName.type !== utils_1.AST_NODE_TYPES.Identifier) { | ||
return; | ||
} | ||
if (typeName.name !== 'Record') { | ||
return; | ||
} | ||
const params = (_a = node.typeParameters) === null || _a === void 0 ? void 0 : _a.params; | ||
if ((params === null || params === void 0 ? void 0 : params.length) !== 2) { | ||
return; | ||
} | ||
context.report({ | ||
node, | ||
messageId: 'preferIndexSignature', | ||
fix(fixer) { | ||
const key = sourceCode.getText(params[0]); | ||
const type = sourceCode.getText(params[1]); | ||
return fixer.replaceText(node, `{ [key: ${key}]: ${type} }`); | ||
}, | ||
}); | ||
}, | ||
})), (mode === 'record' && { | ||
TSTypeLiteral(node) { | ||
checkMembers(node.members, node, '', ''); | ||
const parent = findParentDeclaration(node); | ||
checkMembers(node.members, node, parent === null || parent === void 0 ? void 0 : parent.id, '', ''); | ||
}, | ||
TSInterfaceDeclaration(node) { | ||
var _a, _b, _c; | ||
var _a, _b, _c, _d; | ||
let genericTypes = ''; | ||
if (((_b = (_a = node.typeParameters) === null || _a === void 0 ? void 0 : _a.params) !== null && _b !== void 0 ? _b : []).length > 0) { | ||
genericTypes = `<${(_c = node.typeParameters) === null || _c === void 0 ? void 0 : _c.params.map(p => p.name.name).join(', ')}>`; | ||
genericTypes = `<${(_c = node.typeParameters) === null || _c === void 0 ? void 0 : _c.params.map(p => sourceCode.getText(p)).join(', ')}>`; | ||
} | ||
checkMembers(node.body.body, node, `type ${node.id.name}${genericTypes} = `, ';'); | ||
checkMembers(node.body.body, node, node.id, `type ${node.id.name}${genericTypes} = `, ';', !((_d = node.extends) === null || _d === void 0 ? void 0 : _d.length)); | ||
}, | ||
}; | ||
})); | ||
}, | ||
}); | ||
function findParentDeclaration(node) { | ||
if (node.parent && node.parent.type !== utils_1.AST_NODE_TYPES.TSTypeAnnotation) { | ||
if (node.parent.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration) { | ||
return node.parent; | ||
} | ||
return findParentDeclaration(node.parent); | ||
} | ||
return undefined; | ||
} | ||
//# sourceMappingURL=consistent-indexed-object-style.js.map |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,4 +26,4 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
exports.default = util.createRule({ | ||
@@ -29,6 +33,7 @@ name: 'consistent-type-assertions', | ||
type: 'suggestion', | ||
fixable: 'code', | ||
hasSuggestions: true, | ||
docs: { | ||
category: 'Best Practices', | ||
description: 'Enforces consistent usage of type assertions', | ||
recommended: false, | ||
description: 'Enforce consistent usage of type assertions', | ||
recommended: 'strict', | ||
}, | ||
@@ -40,2 +45,4 @@ messages: { | ||
unexpectedObjectTypeAssertion: 'Always prefer const x: T = { ... }.', | ||
replaceObjectTypeAssertionWithAnnotation: 'Use const x: {{cast}} = { ... } instead.', | ||
replaceObjectTypeAssertionWithSatisfies: 'Use const x = { ... } satisfies {{cast}} instead.', | ||
}, | ||
@@ -81,14 +88,26 @@ schema: [ | ||
function isConst(node) { | ||
if (node.type !== experimental_utils_1.AST_NODE_TYPES.TSTypeReference) { | ||
if (node.type !== utils_1.AST_NODE_TYPES.TSTypeReference) { | ||
return false; | ||
} | ||
return (node.typeName.type === experimental_utils_1.AST_NODE_TYPES.Identifier && | ||
return (node.typeName.type === utils_1.AST_NODE_TYPES.Identifier && | ||
node.typeName.name === 'const'); | ||
} | ||
function getTextWithParentheses(node) { | ||
// Capture parentheses before and after the node | ||
let beforeCount = 0; | ||
let afterCount = 0; | ||
if (util.isParenthesized(node, sourceCode)) { | ||
const bodyOpeningParen = sourceCode.getTokenBefore(node, util.isOpeningParenToken); | ||
const bodyClosingParen = sourceCode.getTokenAfter(node, util.isClosingParenToken); | ||
beforeCount = node.range[0] - bodyOpeningParen.range[0]; | ||
afterCount = bodyClosingParen.range[1] - node.range[1]; | ||
} | ||
return sourceCode.getText(node, beforeCount, afterCount); | ||
} | ||
function reportIncorrectAssertionType(node) { | ||
const messageId = options.assertionStyle; | ||
// If this node is `as const`, then don't report an error. | ||
if (isConst(node.typeAnnotation)) { | ||
if (isConst(node.typeAnnotation) && messageId === 'never') { | ||
return; | ||
} | ||
const messageId = options.assertionStyle; | ||
context.report({ | ||
@@ -100,2 +119,8 @@ node, | ||
: {}, | ||
fix: messageId === 'as' | ||
? (fixer) => [ | ||
fixer.replaceText(node, getTextWithParentheses(node.expression)), | ||
fixer.insertTextAfter(node, ` as ${getTextWithParentheses(node.typeAnnotation)}`), | ||
] | ||
: undefined, | ||
}); | ||
@@ -105,6 +130,6 @@ } | ||
switch (node.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.TSAnyKeyword: | ||
case experimental_utils_1.AST_NODE_TYPES.TSUnknownKeyword: | ||
case utils_1.AST_NODE_TYPES.TSAnyKeyword: | ||
case utils_1.AST_NODE_TYPES.TSUnknownKeyword: | ||
return false; | ||
case experimental_utils_1.AST_NODE_TYPES.TSTypeReference: | ||
case utils_1.AST_NODE_TYPES.TSTypeReference: | ||
return ( | ||
@@ -114,3 +139,3 @@ // Ignore `as const` and `<const>` | ||
// Allow qualified names which have dots between identifiers, `Foo.Bar` | ||
node.typeName.type === experimental_utils_1.AST_NODE_TYPES.TSQualifiedName); | ||
node.typeName.type === utils_1.AST_NODE_TYPES.TSQualifiedName); | ||
default: | ||
@@ -121,5 +146,6 @@ return true; | ||
function checkExpression(node) { | ||
var _a; | ||
if (options.assertionStyle === 'never' || | ||
options.objectLiteralTypeAssertions === 'allow' || | ||
node.expression.type !== experimental_utils_1.AST_NODE_TYPES.ObjectExpression) { | ||
node.expression.type !== utils_1.AST_NODE_TYPES.ObjectExpression) { | ||
return; | ||
@@ -129,14 +155,38 @@ } | ||
node.parent && | ||
(node.parent.type === experimental_utils_1.AST_NODE_TYPES.NewExpression || | ||
node.parent.type === experimental_utils_1.AST_NODE_TYPES.CallExpression || | ||
node.parent.type === experimental_utils_1.AST_NODE_TYPES.ThrowStatement || | ||
node.parent.type === experimental_utils_1.AST_NODE_TYPES.AssignmentPattern || | ||
node.parent.type === experimental_utils_1.AST_NODE_TYPES.JSXExpressionContainer)) { | ||
(node.parent.type === utils_1.AST_NODE_TYPES.NewExpression || | ||
node.parent.type === utils_1.AST_NODE_TYPES.CallExpression || | ||
node.parent.type === utils_1.AST_NODE_TYPES.ThrowStatement || | ||
node.parent.type === utils_1.AST_NODE_TYPES.AssignmentPattern || | ||
node.parent.type === utils_1.AST_NODE_TYPES.JSXExpressionContainer)) { | ||
return; | ||
} | ||
if (checkType(node.typeAnnotation) && | ||
node.expression.type === experimental_utils_1.AST_NODE_TYPES.ObjectExpression) { | ||
node.expression.type === utils_1.AST_NODE_TYPES.ObjectExpression) { | ||
const suggest = []; | ||
if (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.VariableDeclarator && | ||
!node.parent.id.typeAnnotation) { | ||
const { parent } = node; | ||
suggest.push({ | ||
messageId: 'replaceObjectTypeAssertionWithAnnotation', | ||
data: { cast: sourceCode.getText(node.typeAnnotation) }, | ||
fix: fixer => [ | ||
fixer.insertTextAfter(parent.id, `: ${sourceCode.getText(node.typeAnnotation)}`), | ||
fixer.replaceText(node, getTextWithParentheses(node.expression)), | ||
], | ||
}); | ||
} | ||
suggest.push({ | ||
messageId: 'replaceObjectTypeAssertionWithSatisfies', | ||
data: { cast: sourceCode.getText(node.typeAnnotation) }, | ||
fix: fixer => [ | ||
fixer.replaceText(node, getTextWithParentheses(node.expression)), | ||
fixer.insertTextAfter(node, ` satisfies ${context | ||
.getSourceCode() | ||
.getText(node.typeAnnotation)}`), | ||
], | ||
}); | ||
context.report({ | ||
node, | ||
messageId: 'unexpectedObjectTypeAssertion', | ||
suggest, | ||
}); | ||
@@ -143,0 +193,0 @@ } |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -30,6 +34,4 @@ exports.default = util.createRule({ | ||
docs: { | ||
description: 'Consistent with type definition either `interface` or `type`', | ||
category: 'Stylistic Issues', | ||
// too opinionated to be recommended | ||
recommended: false, | ||
description: 'Enforce type definitions to consistently use either `interface` or `type`', | ||
recommended: 'strict', | ||
}, | ||
@@ -57,66 +59,67 @@ messages: { | ||
.getAncestors() | ||
.some(node => node.type === experimental_utils_1.AST_NODE_TYPES.TSModuleDeclaration && | ||
.some(node => node.type === utils_1.AST_NODE_TYPES.TSModuleDeclaration && | ||
node.declare && | ||
node.global); | ||
} | ||
return { | ||
return Object.assign(Object.assign({}, (option === 'interface' && { | ||
"TSTypeAliasDeclaration[typeAnnotation.type='TSTypeLiteral']"(node) { | ||
if (option === 'interface') { | ||
context.report({ | ||
node: node.id, | ||
messageId: 'interfaceOverType', | ||
fix(fixer) { | ||
var _a; | ||
const typeNode = (_a = node.typeParameters) !== null && _a !== void 0 ? _a : node.id; | ||
const fixes = []; | ||
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 === experimental_utils_1.AST_TOKEN_TYPES.Punctuator && | ||
afterToken.value === ';') { | ||
fixes.push(fixer.remove(afterToken)); | ||
} | ||
return fixes; | ||
}, | ||
}); | ||
} | ||
context.report({ | ||
node: node.id, | ||
messageId: 'interfaceOverType', | ||
fix(fixer) { | ||
var _a; | ||
const typeNode = (_a = node.typeParameters) !== null && _a !== void 0 ? _a : node.id; | ||
const fixes = []; | ||
const firstToken = sourceCode.getTokenBefore(node.id); | ||
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 === utils_1.AST_TOKEN_TYPES.Punctuator && | ||
afterToken.value === ';') { | ||
fixes.push(fixer.remove(afterToken)); | ||
} | ||
return fixes; | ||
}, | ||
}); | ||
}, | ||
})), (option === 'type' && { | ||
TSInterfaceDeclaration(node) { | ||
if (option === 'type') { | ||
context.report({ | ||
node: node.id, | ||
messageId: 'typeOverInterface', | ||
/** | ||
* remove automatically fix when the interface is within a declare global | ||
* @see {@link https://github.com/typescript-eslint/typescript-eslint/issues/2707} | ||
*/ | ||
fix: isCurrentlyTraversedNodeWithinModuleDeclaration() | ||
? null | ||
: (fixer) => { | ||
var _a; | ||
const typeNode = (_a = node.typeParameters) !== null && _a !== void 0 ? _a : node.id; | ||
const fixes = []; | ||
const firstToken = sourceCode.getFirstToken(node); | ||
if (firstToken) { | ||
fixes.push(fixer.replaceText(firstToken, 'type')); | ||
fixes.push(fixer.replaceTextRange([typeNode.range[1], node.body.range[0]], ' = ')); | ||
} | ||
if (node.extends) { | ||
node.extends.forEach(heritage => { | ||
const typeIdentifier = sourceCode.getText(heritage); | ||
fixes.push(fixer.insertTextAfter(node.body, ` & ${typeIdentifier}`)); | ||
}); | ||
} | ||
return fixes; | ||
}, | ||
}); | ||
} | ||
const fix = isCurrentlyTraversedNodeWithinModuleDeclaration() | ||
? null | ||
: (fixer) => { | ||
var _a, _b; | ||
const typeNode = (_a = node.typeParameters) !== null && _a !== void 0 ? _a : node.id; | ||
const fixes = []; | ||
const firstToken = sourceCode.getTokenBefore(node.id); | ||
if (firstToken) { | ||
fixes.push(fixer.replaceText(firstToken, 'type')); | ||
fixes.push(fixer.replaceTextRange([typeNode.range[1], node.body.range[0]], ' = ')); | ||
} | ||
if (node.extends) { | ||
node.extends.forEach(heritage => { | ||
const typeIdentifier = sourceCode.getText(heritage); | ||
fixes.push(fixer.insertTextAfter(node.body, ` & ${typeIdentifier}`)); | ||
}); | ||
} | ||
if (((_b = node.parent) === null || _b === void 0 ? void 0 : _b.type) === utils_1.AST_NODE_TYPES.ExportDefaultDeclaration) { | ||
fixes.push(fixer.removeRange([node.parent.range[0], node.range[0]]), fixer.insertTextAfter(node.body, `\nexport default ${node.id.name}`)); | ||
} | ||
return fixes; | ||
}; | ||
context.report({ | ||
node: node.id, | ||
messageId: 'typeOverInterface', | ||
/** | ||
* remove automatically fix when the interface is within a declare global | ||
* @see {@link https://github.com/typescript-eslint/typescript-eslint/issues/2707} | ||
*/ | ||
fix, | ||
}); | ||
}, | ||
}; | ||
})); | ||
}, | ||
}); | ||
//# sourceMappingURL=consistent-type-definitions.js.map |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,10 +26,4 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
function isImportToken(token) { | ||
return token.type === experimental_utils_1.AST_TOKEN_TYPES.Keyword && token.value === 'import'; | ||
} | ||
function isTypeToken(token) { | ||
return token.type === experimental_utils_1.AST_TOKEN_TYPES.Identifier && token.value === 'type'; | ||
} | ||
exports.default = util.createRule({ | ||
@@ -36,12 +34,11 @@ name: 'consistent-type-imports', | ||
docs: { | ||
description: 'Enforces consistent usage of type imports', | ||
category: 'Stylistic Issues', | ||
description: 'Enforce consistent usage of type imports', | ||
recommended: false, | ||
}, | ||
messages: { | ||
typeOverValue: 'All imports in the declaration are only used as types. Use `import type`', | ||
someImportsAreOnlyTypes: 'Imports {{typeImports}} are only used as types', | ||
aImportIsOnlyTypes: 'Import {{typeImports}} is only used as types', | ||
someImportsInDecoMeta: 'Type imports {{typeImports}} are used by decorator metadata', | ||
aImportInDecoMeta: 'Type import {{typeImports}} is used by decorator metadata', | ||
typeOverValue: 'All imports in the declaration are only used as types. Use `import type`.', | ||
someImportsAreOnlyTypes: 'Imports {{typeImports}} are only used as types.', | ||
aImportIsOnlyTypes: 'Import {{typeImports}} is only used as types.', | ||
someImportsInDecoMeta: 'Type imports {{typeImports}} are used by decorator metadata.', | ||
aImportInDecoMeta: 'Type import {{typeImports}} is used by decorator metadata.', | ||
valueOverType: 'Use an `import` instead of an `import type`.', | ||
@@ -60,2 +57,5 @@ noImportTypeAnnotations: '`import()` type annotations are forbidden.', | ||
}, | ||
fixStyle: { | ||
enum: ['separate-type-imports', 'inline-type-imports'], | ||
}, | ||
}, | ||
@@ -71,8 +71,10 @@ additionalProperties: false, | ||
disallowTypeAnnotations: true, | ||
fixStyle: 'separate-type-imports', | ||
}, | ||
], | ||
create(context, [option]) { | ||
var _a; | ||
var _a, _b; | ||
const prefer = (_a = option.prefer) !== null && _a !== void 0 ? _a : 'type-imports'; | ||
const disallowTypeAnnotations = option.disallowTypeAnnotations !== false; | ||
const fixStyle = (_b = option.fixStyle) !== null && _b !== void 0 ? _b : 'separate-type-imports'; | ||
const sourceCode = context.getSourceCode(); | ||
@@ -86,2 +88,3 @@ const sourceImportsMap = {}; | ||
const source = node.source.value; | ||
// sourceImports is the object containing all the specifics for a particular import source, type or value | ||
const sourceImports = (_a = sourceImportsMap[source]) !== null && _a !== void 0 ? _a : (sourceImportsMap[source] = { | ||
@@ -92,6 +95,8 @@ source, | ||
valueOnlyNamedImport: null, | ||
valueImport: null, // if only value imports | ||
}); | ||
if (node.importKind === 'type') { | ||
if (!sourceImports.typeOnlyNamedImport && | ||
node.specifiers.every(specifier => specifier.type === experimental_utils_1.AST_NODE_TYPES.ImportSpecifier)) { | ||
node.specifiers.every(specifier => specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier)) { | ||
// definitely import type { TypeX } | ||
sourceImports.typeOnlyNamedImport = node; | ||
@@ -102,10 +107,21 @@ } | ||
if (!sourceImports.valueOnlyNamedImport && | ||
node.specifiers.every(specifier => specifier.type === experimental_utils_1.AST_NODE_TYPES.ImportSpecifier)) { | ||
node.specifiers.every(specifier => specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier)) { | ||
sourceImports.valueOnlyNamedImport = node; | ||
sourceImports.valueImport = node; | ||
} | ||
else if (!sourceImports.valueImport && | ||
node.specifiers.some(specifier => specifier.type === utils_1.AST_NODE_TYPES.ImportDefaultSpecifier)) { | ||
sourceImports.valueImport = node; | ||
} | ||
} | ||
const typeSpecifiers = []; | ||
const inlineTypeSpecifiers = []; | ||
const valueSpecifiers = []; | ||
const unusedSpecifiers = []; | ||
for (const specifier of node.specifiers) { | ||
if (specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier && | ||
specifier.importKind === 'type') { | ||
inlineTypeSpecifiers.push(specifier); | ||
continue; | ||
} | ||
const [variable] = context.getDeclaredVariables(specifier); | ||
@@ -124,5 +140,5 @@ if (variable.references.length === 0) { | ||
if (((_a = ref.identifier.parent) === null || _a === void 0 ? void 0 : _a.type) === | ||
experimental_utils_1.AST_NODE_TYPES.ExportSpecifier || | ||
utils_1.AST_NODE_TYPES.ExportSpecifier || | ||
((_b = ref.identifier.parent) === null || _b === void 0 ? void 0 : _b.type) === | ||
experimental_utils_1.AST_NODE_TYPES.ExportDefaultDeclaration) { | ||
utils_1.AST_NODE_TYPES.ExportDefaultDeclaration) { | ||
if (ref.isValueReference && ref.isTypeReference) { | ||
@@ -140,5 +156,5 @@ return node.importKind === 'type'; | ||
// however this value reference is safe to use with type-only imports | ||
case experimental_utils_1.AST_NODE_TYPES.TSTypeQuery: | ||
case utils_1.AST_NODE_TYPES.TSTypeQuery: | ||
return true; | ||
case experimental_utils_1.AST_NODE_TYPES.TSQualifiedName: | ||
case utils_1.AST_NODE_TYPES.TSQualifiedName: | ||
// TSTypeQuery must have a TSESTree.EntityName as its child, so we can filter here and break early | ||
@@ -157,5 +173,5 @@ if (parent.left !== child) { | ||
// Also this is represented as a non-type AST - hence it uses MemberExpression | ||
case experimental_utils_1.AST_NODE_TYPES.TSPropertySignature: | ||
case utils_1.AST_NODE_TYPES.TSPropertySignature: | ||
return parent.key === child; | ||
case experimental_utils_1.AST_NODE_TYPES.MemberExpression: | ||
case utils_1.AST_NODE_TYPES.MemberExpression: | ||
if (parent.object !== child) { | ||
@@ -190,2 +206,3 @@ return false; | ||
unusedSpecifiers, | ||
inlineTypeSpecifiers, | ||
}); | ||
@@ -197,2 +214,3 @@ } | ||
if (sourceImports.reportValueImports.length === 0) { | ||
// nothing to fix. value specifiers and type specifiers are correctly written | ||
continue; | ||
@@ -202,4 +220,4 @@ } | ||
if (report.valueSpecifiers.length === 0 && | ||
report.unusedSpecifiers.length === 0) { | ||
// import is all type-only, convert the entire import to `import type` | ||
report.unusedSpecifiers.length === 0 && | ||
report.node.importKind !== 'type') { | ||
context.report({ | ||
@@ -209,3 +227,3 @@ node: report.node, | ||
*fix(fixer) { | ||
yield* fixToTypeImport(fixer, report, sourceImports); | ||
yield* fixToTypeImportDeclaration(fixer, report, sourceImports); | ||
}, | ||
@@ -216,9 +234,10 @@ }); | ||
const isTypeImport = report.node.importKind === 'type'; | ||
// we have a mixed type/value import, so we need to split them out into multiple exports | ||
// we have a mixed type/value import or just value imports, so we need to split them out into multiple imports if separate-type-imports is configured | ||
const importNames = (isTypeImport | ||
? report.valueSpecifiers | ||
: report.typeSpecifiers).map(specifier => `"${specifier.local.name}"`); | ||
? report.valueSpecifiers // import type { A } from 'roo'; // WHERE A is used in value position | ||
: report.typeSpecifiers) // import { A, B } from 'roo'; // WHERE A is used in type position and B is in value position | ||
.map(specifier => `"${specifier.local.name}"`); | ||
const message = (() => { | ||
const typeImports = util.formatWordList(importNames); | ||
if (importNames.length === 1) { | ||
const typeImports = importNames[0]; | ||
if (isTypeImport) { | ||
@@ -238,10 +257,6 @@ return { | ||
else { | ||
const typeImports = [ | ||
importNames.slice(0, -1).join(', '), | ||
importNames.slice(-1)[0], | ||
].join(' and '); | ||
if (isTypeImport) { | ||
return { | ||
messageId: 'someImportsInDecoMeta', | ||
data: { typeImports }, | ||
data: { typeImports }, // typeImports are all the value specifiers that are in the type position | ||
}; | ||
@@ -252,3 +267,3 @@ } | ||
messageId: 'someImportsAreOnlyTypes', | ||
data: { typeImports }, | ||
data: { typeImports }, // typeImports are all the type specifiers in the value position | ||
}; | ||
@@ -260,6 +275,8 @@ } | ||
if (isTypeImport) { | ||
yield* fixToValueImportInDecoMeta(fixer, report, sourceImports); | ||
// take all the valueSpecifiers and put them on a new line | ||
yield* fixToValueImportDeclaration(fixer, report, sourceImports); | ||
} | ||
else { | ||
yield* fixToTypeImport(fixer, report, sourceImports); | ||
// take all the typeSpecifiers and put them on a new line | ||
yield* fixToTypeImportDeclaration(fixer, report, sourceImports); | ||
} | ||
@@ -279,6 +296,15 @@ } })); | ||
fix(fixer) { | ||
return fixToValueImport(fixer, node); | ||
return fixRemoveTypeSpecifierFromImportDeclaration(fixer, node); | ||
}, | ||
}); | ||
}, | ||
'ImportSpecifier[importKind = "type"]'(node) { | ||
context.report({ | ||
node, | ||
messageId: 'valueOverType', | ||
fix(fixer) { | ||
return fixRemoveTypeSpecifierFromImportSpecifier(fixer, node); | ||
}, | ||
}); | ||
}, | ||
})), (disallowTypeAnnotations | ||
@@ -297,7 +323,7 @@ ? { | ||
var _a; | ||
const defaultSpecifier = node.specifiers[0].type === experimental_utils_1.AST_NODE_TYPES.ImportDefaultSpecifier | ||
const defaultSpecifier = node.specifiers[0].type === utils_1.AST_NODE_TYPES.ImportDefaultSpecifier | ||
? node.specifiers[0] | ||
: null; | ||
const namespaceSpecifier = (_a = node.specifiers.find((specifier) => specifier.type === experimental_utils_1.AST_NODE_TYPES.ImportNamespaceSpecifier)) !== null && _a !== void 0 ? _a : null; | ||
const namedSpecifiers = node.specifiers.filter((specifier) => specifier.type === experimental_utils_1.AST_NODE_TYPES.ImportSpecifier); | ||
const namespaceSpecifier = (_a = node.specifiers.find((specifier) => specifier.type === utils_1.AST_NODE_TYPES.ImportNamespaceSpecifier)) !== null && _a !== void 0 ? _a : null; | ||
const namedSpecifiers = node.specifiers.filter((specifier) => specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier); | ||
return { | ||
@@ -310,5 +336,5 @@ defaultSpecifier, | ||
/** | ||
* Returns information for fixing named specifiers. | ||
* Returns information for fixing named specifiers, type or value | ||
*/ | ||
function getFixesNamedSpecifiers(fixer, node, typeNamedSpecifiers, allNamedSpecifiers) { | ||
function getFixesNamedSpecifiers(fixer, node, subsetNamedSpecifiers, allNamedSpecifiers) { | ||
if (allNamedSpecifiers.length === 0) { | ||
@@ -322,7 +348,6 @@ return { | ||
const removeTypeNamedSpecifiers = []; | ||
if (typeNamedSpecifiers.length === allNamedSpecifiers.length) { | ||
// e.g. | ||
if (subsetNamedSpecifiers.length === allNamedSpecifiers.length) { | ||
// import Foo, {Type1, Type2} from 'foo' | ||
// import DefType, {Type1, Type2} from 'foo' | ||
const openingBraceToken = util.nullThrows(sourceCode.getTokenBefore(typeNamedSpecifiers[0], util.isOpeningBraceToken), util.NullThrowsReasons.MissingToken('{', node.type)); | ||
const openingBraceToken = util.nullThrows(sourceCode.getTokenBefore(subsetNamedSpecifiers[0], util.isOpeningBraceToken), util.NullThrowsReasons.MissingToken('{', node.type)); | ||
const commaToken = util.nullThrows(sourceCode.getTokenBefore(openingBraceToken, util.isCommaToken), util.NullThrowsReasons.MissingToken(',', node.type)); | ||
@@ -336,10 +361,10 @@ const closingBraceToken = util.nullThrows(sourceCode.getFirstTokenBetween(openingBraceToken, node.source, util.isClosingBraceToken), util.NullThrowsReasons.MissingToken('}', node.type)); | ||
else { | ||
const typeNamedSpecifierGroups = []; | ||
const namedSpecifierGroups = []; | ||
let group = []; | ||
for (const namedSpecifier of allNamedSpecifiers) { | ||
if (typeNamedSpecifiers.includes(namedSpecifier)) { | ||
if (subsetNamedSpecifiers.includes(namedSpecifier)) { | ||
group.push(namedSpecifier); | ||
} | ||
else if (group.length) { | ||
typeNamedSpecifierGroups.push(group); | ||
namedSpecifierGroups.push(group); | ||
group = []; | ||
@@ -349,5 +374,5 @@ } | ||
if (group.length) { | ||
typeNamedSpecifierGroups.push(group); | ||
namedSpecifierGroups.push(group); | ||
} | ||
for (const namedSpecifiers of typeNamedSpecifierGroups) { | ||
for (const namedSpecifiers of namedSpecifierGroups) { | ||
const { removeRange, textRange } = getNamedSpecifierRanges(namedSpecifiers, allNamedSpecifiers); | ||
@@ -399,17 +424,44 @@ removeTypeNamedSpecifiers.push(fixer.removeRange(removeRange)); | ||
*/ | ||
function insertToNamedImport(fixer, target, insertText) { | ||
function fixInsertNamedSpecifiersInNamedSpecifierList(fixer, target, insertText) { | ||
const closingBraceToken = util.nullThrows(sourceCode.getFirstTokenBetween(sourceCode.getFirstToken(target), target.source, util.isClosingBraceToken), util.NullThrowsReasons.MissingToken('}', target.type)); | ||
const before = sourceCode.getTokenBefore(closingBraceToken); | ||
if (!util.isCommaToken(before) && !util.isOpeningBraceToken(before)) { | ||
insertText = ',' + insertText; | ||
insertText = `,${insertText}`; | ||
} | ||
return fixer.insertTextBefore(closingBraceToken, insertText); | ||
} | ||
function* fixToTypeImport(fixer, report, sourceImports) { | ||
/** | ||
* insert type keyword to named import node. | ||
* e.g. | ||
* import ADefault, { Already, type Type1, type Type2 } from 'foo' | ||
* ^^^^ insert | ||
*/ | ||
function* fixInsertTypeKeywordInNamedSpecifierList(fixer, typeSpecifiers) { | ||
for (const spec of typeSpecifiers) { | ||
const insertText = sourceCode.text.slice(...spec.range); | ||
yield fixer.replaceTextRange(spec.range, `type ${insertText}`); | ||
} | ||
} | ||
function* fixInlineTypeImportDeclaration(fixer, report, sourceImports) { | ||
const { node } = report; | ||
const { defaultSpecifier, namespaceSpecifier, namedSpecifiers, } = classifySpecifier(node); | ||
// For a value import, will only add an inline type to named specifiers | ||
const { namedSpecifiers } = classifySpecifier(node); | ||
const typeNamedSpecifiers = namedSpecifiers.filter(specifier => report.typeSpecifiers.includes(specifier)); | ||
if (sourceImports.valueImport) { | ||
// add import named type specifiers to its value import | ||
// import ValueA, { type A } | ||
// ^^^^ insert | ||
const { namedSpecifiers: valueImportNamedSpecifiers } = classifySpecifier(sourceImports.valueImport); | ||
if (sourceImports.valueOnlyNamedImport || | ||
valueImportNamedSpecifiers.length) { | ||
yield* fixInsertTypeKeywordInNamedSpecifierList(fixer, typeNamedSpecifiers); | ||
} | ||
} | ||
} | ||
function* fixToTypeImportDeclaration(fixer, report, sourceImports) { | ||
const { node } = report; | ||
const { defaultSpecifier, namespaceSpecifier, namedSpecifiers } = classifySpecifier(node); | ||
if (namespaceSpecifier && !defaultSpecifier) { | ||
// e.g. | ||
// import * as types from 'foo' | ||
yield* fixToTypeImportByInsertType(fixer, node, false); | ||
yield* fixInsertTypeSpecifierForImportDeclaration(fixer, node, false); | ||
return; | ||
@@ -421,14 +473,26 @@ } | ||
!namespaceSpecifier) { | ||
// e.g. | ||
// import Type from 'foo' | ||
yield* fixToTypeImportByInsertType(fixer, node, true); | ||
yield* fixInsertTypeSpecifierForImportDeclaration(fixer, node, true); | ||
return; | ||
} | ||
else if (fixStyle === 'inline-type-imports' && | ||
!report.typeSpecifiers.includes(defaultSpecifier) && | ||
namedSpecifiers.length > 0 && | ||
!namespaceSpecifier) { | ||
// if there is a default specifier but it isn't a type specifier, then just add the inline type modifier to the named specifiers | ||
// import AValue, {BValue, Type1, Type2} from 'foo' | ||
yield* fixInlineTypeImportDeclaration(fixer, report, sourceImports); | ||
return; | ||
} | ||
} | ||
else { | ||
if (namedSpecifiers.every(specifier => report.typeSpecifiers.includes(specifier)) && | ||
!namespaceSpecifier) { | ||
// e.g. | ||
else if (!namespaceSpecifier) { | ||
if (fixStyle === 'inline-type-imports' && | ||
namedSpecifiers.some(specifier => report.typeSpecifiers.includes(specifier))) { | ||
// import {AValue, Type1, Type2} from 'foo' | ||
yield* fixInlineTypeImportDeclaration(fixer, report, sourceImports); | ||
return; | ||
} | ||
else if (namedSpecifiers.every(specifier => report.typeSpecifiers.includes(specifier))) { | ||
// import {Type1, Type2} from 'foo' | ||
yield* fixToTypeImportByInsertType(fixer, node, false); | ||
yield* fixInsertTypeSpecifierForImportDeclaration(fixer, node, false); | ||
return; | ||
@@ -442,3 +506,3 @@ } | ||
if (sourceImports.typeOnlyNamedImport) { | ||
const insertTypeNamedSpecifiers = insertToNamedImport(fixer, sourceImports.typeOnlyNamedImport, fixesNamedSpecifiers.typeNamedSpecifiersText); | ||
const insertTypeNamedSpecifiers = fixInsertNamedSpecifiersInNamedSpecifierList(fixer, sourceImports.typeOnlyNamedImport, fixesNamedSpecifiers.typeNamedSpecifiersText); | ||
if (sourceImports.typeOnlyNamedImport.range[1] <= node.range[0]) { | ||
@@ -452,3 +516,14 @@ yield insertTypeNamedSpecifiers; | ||
else { | ||
yield fixer.insertTextBefore(node, `import type {${fixesNamedSpecifiers.typeNamedSpecifiersText}} from ${sourceCode.getText(node.source)};\n`); | ||
// The import is both default and named. Insert named on new line because can't mix default type import and named type imports | ||
if (fixStyle === 'inline-type-imports') { | ||
yield fixer.insertTextBefore(node, `import {${typeNamedSpecifiers | ||
.map(spec => { | ||
const insertText = sourceCode.text.slice(...spec.range); | ||
return `type ${insertText}`; | ||
}) | ||
.join(', ')}} from ${sourceCode.getText(node.source)};\n`); | ||
} | ||
else { | ||
yield fixer.insertTextBefore(node, `import type {${fixesNamedSpecifiers.typeNamedSpecifiersText}} from ${sourceCode.getText(node.source)};\n`); | ||
} | ||
} | ||
@@ -459,3 +534,2 @@ } | ||
report.typeSpecifiers.includes(namespaceSpecifier)) { | ||
// e.g. | ||
// import Foo, * as Type from 'foo' | ||
@@ -475,3 +549,3 @@ // import DefType, * as Type from 'foo' | ||
if (report.typeSpecifiers.length === node.specifiers.length) { | ||
const importToken = util.nullThrows(sourceCode.getFirstToken(node, isImportToken), util.NullThrowsReasons.MissingToken('import', node.type)); | ||
const importToken = util.nullThrows(sourceCode.getFirstToken(node, util.isImportKeyword), util.NullThrowsReasons.MissingToken('import', node.type)); | ||
// import type Type from 'foo' | ||
@@ -502,6 +576,6 @@ // ^^^^ insert | ||
} | ||
function* fixToTypeImportByInsertType(fixer, node, isDefaultImport) { | ||
function* fixInsertTypeSpecifierForImportDeclaration(fixer, node, isDefaultImport) { | ||
// import type Foo from 'foo' | ||
// ^^^^^ insert | ||
const importToken = util.nullThrows(sourceCode.getFirstToken(node, isImportToken), util.NullThrowsReasons.MissingToken('import', node.type)); | ||
const importToken = util.nullThrows(sourceCode.getFirstToken(node, util.isImportKeyword), util.NullThrowsReasons.MissingToken('import', node.type)); | ||
yield fixer.insertTextAfter(importToken, ' type'); | ||
@@ -523,4 +597,2 @@ if (isDefaultImport) { | ||
if (node.specifiers.length > 1) { | ||
// import type Foo from 'foo' | ||
// import type {...} from 'foo' // <- insert | ||
yield fixer.insertTextAfter(node, `\nimport type${specifiersText} from ${sourceCode.getText(node.source)};`); | ||
@@ -530,10 +602,16 @@ } | ||
} | ||
// make sure we don't do anything like `import type {type T} from 'foo';` | ||
for (const specifier of node.specifiers) { | ||
if (specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier && | ||
specifier.importKind === 'type') { | ||
yield* fixRemoveTypeSpecifierFromImportSpecifier(fixer, specifier); | ||
} | ||
} | ||
} | ||
function* fixToValueImportInDecoMeta(fixer, report, sourceImports) { | ||
function* fixToValueImportDeclaration(fixer, report, sourceImports) { | ||
const { node } = report; | ||
const { defaultSpecifier, namespaceSpecifier, namedSpecifiers, } = classifySpecifier(node); | ||
const { defaultSpecifier, namespaceSpecifier, namedSpecifiers } = classifySpecifier(node); | ||
if (namespaceSpecifier) { | ||
// e.g. | ||
// import type * as types from 'foo' | ||
yield* fixToValueImport(fixer, node); | ||
yield* fixRemoveTypeSpecifierFromImportDeclaration(fixer, node); | ||
return; | ||
@@ -544,5 +622,4 @@ } | ||
namedSpecifiers.length === 0) { | ||
// e.g. | ||
// import type Type from 'foo' | ||
yield* fixToValueImport(fixer, node); | ||
yield* fixRemoveTypeSpecifierFromImportDeclaration(fixer, node); | ||
return; | ||
@@ -553,8 +630,10 @@ } | ||
if (namedSpecifiers.every(specifier => report.valueSpecifiers.includes(specifier))) { | ||
// e.g. | ||
// import type {Type1, Type2} from 'foo' | ||
yield* fixToValueImport(fixer, node); | ||
yield* fixRemoveTypeSpecifierFromImportDeclaration(fixer, node); | ||
return; | ||
} | ||
} | ||
// we have some valueSpecifiers intermixed in types that need to be put on their own line | ||
// import type { Type1, A } from 'foo' | ||
// import type { A } from 'foo' | ||
const valueNamedSpecifiers = namedSpecifiers.filter(specifier => report.valueSpecifiers.includes(specifier)); | ||
@@ -565,3 +644,3 @@ const fixesNamedSpecifiers = getFixesNamedSpecifiers(fixer, node, valueNamedSpecifiers, namedSpecifiers); | ||
if (sourceImports.valueOnlyNamedImport) { | ||
const insertTypeNamedSpecifiers = insertToNamedImport(fixer, sourceImports.valueOnlyNamedImport, fixesNamedSpecifiers.typeNamedSpecifiersText); | ||
const insertTypeNamedSpecifiers = fixInsertNamedSpecifiersInNamedSpecifierList(fixer, sourceImports.valueOnlyNamedImport, fixesNamedSpecifiers.typeNamedSpecifiersText); | ||
if (sourceImports.valueOnlyNamedImport.range[1] <= node.range[0]) { | ||
@@ -575,2 +654,4 @@ yield insertTypeNamedSpecifiers; | ||
else { | ||
// some are types. | ||
// Add new value import and later remove those value specifiers from import type | ||
yield fixer.insertTextBefore(node, `import {${fixesNamedSpecifiers.typeNamedSpecifiersText}} from ${sourceCode.getText(node.source)};\n`); | ||
@@ -582,13 +663,20 @@ } | ||
} | ||
function* fixToValueImport(fixer, node) { | ||
function* fixRemoveTypeSpecifierFromImportDeclaration(fixer, node) { | ||
var _a, _b; | ||
// import type Foo from 'foo' | ||
// ^^^^ remove | ||
const importToken = util.nullThrows(sourceCode.getFirstToken(node, isImportToken), util.NullThrowsReasons.MissingToken('import', node.type)); | ||
const typeToken = util.nullThrows(sourceCode.getFirstTokenBetween(importToken, (_b = (_a = node.specifiers[0]) === null || _a === void 0 ? void 0 : _a.local) !== null && _b !== void 0 ? _b : node.source, isTypeToken), util.NullThrowsReasons.MissingToken('type', node.type)); | ||
const importToken = util.nullThrows(sourceCode.getFirstToken(node, util.isImportKeyword), util.NullThrowsReasons.MissingToken('import', node.type)); | ||
const typeToken = util.nullThrows(sourceCode.getFirstTokenBetween(importToken, (_b = (_a = node.specifiers[0]) === null || _a === void 0 ? void 0 : _a.local) !== null && _b !== void 0 ? _b : node.source, util.isTypeKeyword), util.NullThrowsReasons.MissingToken('type', node.type)); | ||
const afterToken = util.nullThrows(sourceCode.getTokenAfter(typeToken, { includeComments: true }), util.NullThrowsReasons.MissingToken('any token', node.type)); | ||
yield fixer.removeRange([typeToken.range[0], afterToken.range[0]]); | ||
} | ||
function* fixRemoveTypeSpecifierFromImportSpecifier(fixer, node) { | ||
// import { type Foo } from 'foo' | ||
// ^^^^ remove | ||
const typeToken = util.nullThrows(sourceCode.getFirstToken(node, util.isTypeKeyword), util.NullThrowsReasons.MissingToken('type', node.type)); | ||
const afterToken = util.nullThrows(sourceCode.getTokenAfter(typeToken, { includeComments: true }), util.NullThrowsReasons.MissingToken('any token', node.type)); | ||
yield fixer.removeRange([typeToken.range[0], afterToken.range[0]]); | ||
} | ||
}, | ||
}); | ||
//# sourceMappingURL=consistent-type-imports.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util_1 = require("../util"); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
exports.default = util_1.createRule({ | ||
exports.default = (0, util_1.createRule)({ | ||
name: 'default-param-last', | ||
@@ -11,3 +11,2 @@ meta: { | ||
description: 'Enforce default parameters to be last', | ||
category: 'Best Practices', | ||
recommended: false, | ||
@@ -37,4 +36,4 @@ extendsBaseRule: true, | ||
function isPlainParam(node) { | ||
return !(node.type === experimental_utils_1.AST_NODE_TYPES.AssignmentPattern || | ||
node.type === experimental_utils_1.AST_NODE_TYPES.RestElement || | ||
return !(node.type === utils_1.AST_NODE_TYPES.AssignmentPattern || | ||
node.type === utils_1.AST_NODE_TYPES.RestElement || | ||
isOptionalParam(node)); | ||
@@ -46,3 +45,3 @@ } | ||
const current = node.params[i]; | ||
const param = current.type === experimental_utils_1.AST_NODE_TYPES.TSParameterProperty | ||
const param = current.type === utils_1.AST_NODE_TYPES.TSParameterProperty | ||
? current.parameter | ||
@@ -56,3 +55,3 @@ : current; | ||
(isOptionalParam(param) || | ||
param.type === experimental_utils_1.AST_NODE_TYPES.AssignmentPattern)) { | ||
param.type === utils_1.AST_NODE_TYPES.AssignmentPattern)) { | ||
context.report({ node: current, messageId: 'shouldBeLast' }); | ||
@@ -59,0 +58,0 @@ } |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -21,10 +25,9 @@ if (k2 === undefined) k2 = k; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const tsutils = __importStar(require("tsutils")); | ||
const ts = __importStar(require("typescript")); | ||
const dot_notation_1 = __importDefault(require("eslint/lib/rules/dot-notation")); | ||
const util_1 = require("../util"); | ||
exports.default = util_1.createRule({ | ||
const getESLintCoreRule_1 = require("../util/getESLintCoreRule"); | ||
const baseRule = (0, getESLintCoreRule_1.getESLintCoreRule)('dot-notation'); | ||
exports.default = (0, util_1.createRule)({ | ||
name: 'dot-notation', | ||
@@ -34,5 +37,4 @@ meta: { | ||
docs: { | ||
description: 'enforce dot notation whenever possible', | ||
category: 'Best Practices', | ||
recommended: false, | ||
description: 'Enforce dot notation whenever possible', | ||
recommended: 'strict', | ||
extendsBaseRule: true, | ||
@@ -61,2 +63,6 @@ requiresTypeChecking: true, | ||
}, | ||
allowIndexSignaturePropertyAccess: { | ||
type: 'boolean', | ||
default: false, | ||
}, | ||
}, | ||
@@ -66,4 +72,5 @@ additionalProperties: false, | ||
], | ||
fixable: dot_notation_1.default.meta.fixable, | ||
messages: dot_notation_1.default.meta.messages, | ||
fixable: baseRule.meta.fixable, | ||
hasSuggestions: baseRule.meta.hasSuggestions, | ||
messages: baseRule.meta.messages, | ||
}, | ||
@@ -74,2 +81,3 @@ defaultOptions: [ | ||
allowProtectedClassPropertyAccess: false, | ||
allowIndexSignaturePropertyAccess: false, | ||
allowKeywords: true, | ||
@@ -80,22 +88,38 @@ allowPattern: '', | ||
create(context, [options]) { | ||
const rules = dot_notation_1.default.create(context); | ||
var _a; | ||
const rules = baseRule.create(context); | ||
const { program, esTreeNodeToTSNodeMap } = (0, util_1.getParserServices)(context); | ||
const typeChecker = program.getTypeChecker(); | ||
const allowPrivateClassPropertyAccess = options.allowPrivateClassPropertyAccess; | ||
const allowProtectedClassPropertyAccess = options.allowProtectedClassPropertyAccess; | ||
const parserServices = util_1.getParserServices(context); | ||
const typeChecker = parserServices.program.getTypeChecker(); | ||
const allowIndexSignaturePropertyAccess = ((_a = options.allowIndexSignaturePropertyAccess) !== null && _a !== void 0 ? _a : false) || | ||
tsutils.isCompilerOptionEnabled(program.getCompilerOptions(), | ||
// @ts-expect-error - TS is refining the type to never for some reason | ||
'noPropertyAccessFromIndexSignature'); | ||
return { | ||
MemberExpression(node) { | ||
var _a, _b, _c; | ||
var _a, _b; | ||
if ((allowPrivateClassPropertyAccess || | ||
allowProtectedClassPropertyAccess) && | ||
allowProtectedClassPropertyAccess || | ||
allowIndexSignaturePropertyAccess) && | ||
node.computed) { | ||
// for perf reasons - only fetch the symbol if we have to | ||
const objectSymbol = typeChecker.getSymbolAtLocation(parserServices.esTreeNodeToTSNodeMap.get(node.property)); | ||
const modifierKind = (_c = (_b = (_a = objectSymbol === null || objectSymbol === void 0 ? void 0 : objectSymbol.getDeclarations()) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.modifiers) === null || _c === void 0 ? void 0 : _c[0].kind; | ||
// for perf reasons - only fetch symbols if we have to | ||
const propertySymbol = typeChecker.getSymbolAtLocation(esTreeNodeToTSNodeMap.get(node.property)); | ||
const modifierKind = (_b = (0, util_1.getModifiers)((_a = propertySymbol === null || propertySymbol === void 0 ? void 0 : propertySymbol.getDeclarations()) === null || _a === void 0 ? void 0 : _a[0])) === null || _b === void 0 ? void 0 : _b[0].kind; | ||
if ((allowPrivateClassPropertyAccess && | ||
modifierKind == ts.SyntaxKind.PrivateKeyword) || | ||
modifierKind === ts.SyntaxKind.PrivateKeyword) || | ||
(allowProtectedClassPropertyAccess && | ||
modifierKind == ts.SyntaxKind.ProtectedKeyword)) { | ||
modifierKind === ts.SyntaxKind.ProtectedKeyword)) { | ||
return; | ||
} | ||
if (propertySymbol === undefined && | ||
allowIndexSignaturePropertyAccess) { | ||
const objectType = typeChecker.getTypeAtLocation(esTreeNodeToTSNodeMap.get(node.object)); | ||
const indexType = objectType | ||
.getNonNullableType() | ||
.getStringIndexType(); | ||
if (indexType !== undefined) { | ||
return; | ||
} | ||
} | ||
} | ||
@@ -102,0 +126,0 @@ rules.MemberExpression(node); |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -32,3 +36,2 @@ const explicitReturnTypeUtils_1 = require("../util/explicitReturnTypeUtils"); | ||
description: 'Require explicit return types on functions and class methods', | ||
category: 'Stylistic Issues', | ||
recommended: false, | ||
@@ -43,17 +46,37 @@ }, | ||
properties: { | ||
allowExpressions: { | ||
allowConciseArrowFunctionExpressionsStartingWithVoid: { | ||
description: 'Whether to allow arrow functions that start with the `void` keyword.', | ||
type: 'boolean', | ||
}, | ||
allowTypedFunctionExpressions: { | ||
allowExpressions: { | ||
description: 'Whether to ignore function expressions (functions which are not part of a declaration).', | ||
type: 'boolean', | ||
}, | ||
allowHigherOrderFunctions: { | ||
description: 'Whether to ignore functions immediately returning another function expression.', | ||
type: 'boolean', | ||
}, | ||
allowTypedFunctionExpressions: { | ||
description: 'Whether to ignore type annotations on the variable of function expressions.', | ||
type: 'boolean', | ||
}, | ||
allowDirectConstAssertionInArrowFunctions: { | ||
description: 'Whether to ignore arrow functions immediately returning a `as const` value.', | ||
type: 'boolean', | ||
}, | ||
allowConciseArrowFunctionExpressionsStartingWithVoid: { | ||
allowFunctionsWithoutTypeParameters: { | ||
description: "Whether to ignore functions that don't have generic type parameters.", | ||
type: 'boolean', | ||
}, | ||
allowedNames: { | ||
description: 'An array of function/method names that will not have their arguments or return values checked.', | ||
items: { | ||
type: 'string', | ||
}, | ||
type: 'array', | ||
}, | ||
allowIIFEs: { | ||
description: 'Whether to ignore immediately invoked function expressions (IIFEs).', | ||
type: 'boolean', | ||
}, | ||
}, | ||
@@ -71,2 +94,5 @@ additionalProperties: false, | ||
allowConciseArrowFunctionExpressionsStartingWithVoid: false, | ||
allowFunctionsWithoutTypeParameters: false, | ||
allowedNames: [], | ||
allowIIFEs: false, | ||
}, | ||
@@ -76,12 +102,72 @@ ], | ||
const sourceCode = context.getSourceCode(); | ||
function isAllowedFunction(node) { | ||
var _a, _b; | ||
if (options.allowFunctionsWithoutTypeParameters && !node.typeParameters) { | ||
return true; | ||
} | ||
if (options.allowIIFEs && isIIFE(node)) { | ||
return true; | ||
} | ||
if (!((_a = options.allowedNames) === null || _a === void 0 ? void 0 : _a.length)) { | ||
return false; | ||
} | ||
if (node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression || | ||
node.type === utils_1.AST_NODE_TYPES.FunctionExpression) { | ||
const parent = node.parent; | ||
let funcName; | ||
if ((_b = node.id) === null || _b === void 0 ? void 0 : _b.name) { | ||
funcName = node.id.name; | ||
} | ||
else if (parent) { | ||
switch (parent.type) { | ||
case utils_1.AST_NODE_TYPES.VariableDeclarator: { | ||
if (parent.id.type === utils_1.AST_NODE_TYPES.Identifier) { | ||
funcName = parent.id.name; | ||
} | ||
break; | ||
} | ||
case utils_1.AST_NODE_TYPES.MethodDefinition: | ||
case utils_1.AST_NODE_TYPES.PropertyDefinition: | ||
case utils_1.AST_NODE_TYPES.Property: { | ||
if (parent.key.type === utils_1.AST_NODE_TYPES.Identifier && | ||
parent.computed === false) { | ||
funcName = parent.key.name; | ||
} | ||
break; | ||
} | ||
} | ||
} | ||
if (!!funcName && !!options.allowedNames.includes(funcName)) { | ||
return true; | ||
} | ||
} | ||
if (node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration && | ||
node.id && | ||
node.id.type === utils_1.AST_NODE_TYPES.Identifier && | ||
!!options.allowedNames.includes(node.id.name)) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
function isIIFE(node) { | ||
return node.parent.type === utils_1.AST_NODE_TYPES.CallExpression; | ||
} | ||
return { | ||
'ArrowFunctionExpression, FunctionExpression'(node) { | ||
if (options.allowConciseArrowFunctionExpressionsStartingWithVoid && | ||
node.type === experimental_utils_1.AST_NODE_TYPES.ArrowFunctionExpression && | ||
node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression && | ||
node.expression && | ||
node.body.type === experimental_utils_1.AST_NODE_TYPES.UnaryExpression && | ||
node.body.type === utils_1.AST_NODE_TYPES.UnaryExpression && | ||
node.body.operator === 'void') { | ||
return; | ||
} | ||
explicitReturnTypeUtils_1.checkFunctionExpressionReturnType(node, options, sourceCode, loc => context.report({ | ||
if (isAllowedFunction(node)) { | ||
return; | ||
} | ||
if (options.allowTypedFunctionExpressions && | ||
((0, explicitReturnTypeUtils_1.isValidFunctionExpressionReturnType)(node, options) || | ||
(0, explicitReturnTypeUtils_1.ancestorHasReturnType)(node))) { | ||
return; | ||
} | ||
(0, explicitReturnTypeUtils_1.checkFunctionReturnType)(node, options, sourceCode, loc => context.report({ | ||
node, | ||
@@ -93,3 +179,9 @@ loc, | ||
FunctionDeclaration(node) { | ||
explicitReturnTypeUtils_1.checkFunctionReturnType(node, options, sourceCode, loc => context.report({ | ||
if (isAllowedFunction(node)) { | ||
return; | ||
} | ||
if (options.allowTypedFunctionExpressions && node.returnType) { | ||
return; | ||
} | ||
(0, explicitReturnTypeUtils_1.checkFunctionReturnType)(node, options, sourceCode, loc => context.report({ | ||
node, | ||
@@ -96,0 +188,0 @@ loc, |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,12 +26,27 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
const accessibilityLevel = { enum: ['explicit', 'no-public', 'off'] }; | ||
const accessibilityLevel = { | ||
oneOf: [ | ||
{ | ||
const: 'explicit', | ||
description: 'Always require an accessor.', | ||
}, | ||
{ | ||
const: 'no-public', | ||
description: 'Require an accessor except when public.', | ||
}, | ||
{ | ||
const: 'off', | ||
description: 'Never check whether there is an accessor.', | ||
}, | ||
], | ||
}; | ||
exports.default = util.createRule({ | ||
name: 'explicit-member-accessibility', | ||
meta: { | ||
hasSuggestions: true, | ||
type: 'problem', | ||
docs: { | ||
description: 'Require explicit accessibility modifiers on class properties and methods', | ||
category: 'Stylistic Issues', | ||
// too opinionated to be recommended | ||
@@ -40,29 +59,38 @@ recommended: false, | ||
unwantedPublicAccessibility: 'Public accessibility modifier on {{type}} {{name}}.', | ||
addExplicitAccessibility: "Add '{{ type }}' accessibility modifier", | ||
}, | ||
schema: [ | ||
{ | ||
type: 'object', | ||
properties: { | ||
accessibility: accessibilityLevel, | ||
overrides: { | ||
type: 'object', | ||
properties: { | ||
accessors: accessibilityLevel, | ||
constructors: accessibilityLevel, | ||
methods: accessibilityLevel, | ||
properties: accessibilityLevel, | ||
parameterProperties: accessibilityLevel, | ||
schema: { | ||
$defs: { | ||
accessibilityLevel, | ||
}, | ||
prefixItems: [ | ||
{ | ||
type: 'object', | ||
properties: { | ||
accessibility: { $ref: '#/$defs/accessibilityLevel' }, | ||
overrides: { | ||
type: 'object', | ||
properties: { | ||
accessors: { $ref: '#/$defs/accessibilityLevel' }, | ||
constructors: { $ref: '#/$defs/accessibilityLevel' }, | ||
methods: { $ref: '#/$defs/accessibilityLevel' }, | ||
properties: { $ref: '#/$defs/accessibilityLevel' }, | ||
parameterProperties: { | ||
$ref: '#/$defs/accessibilityLevel', | ||
}, | ||
}, | ||
additionalProperties: false, | ||
}, | ||
additionalProperties: false, | ||
}, | ||
ignoredMethodNames: { | ||
type: 'array', | ||
items: { | ||
type: 'string', | ||
ignoredMethodNames: { | ||
type: 'array', | ||
items: { | ||
type: 'string', | ||
}, | ||
}, | ||
}, | ||
additionalProperties: false, | ||
}, | ||
additionalProperties: false, | ||
}, | ||
], | ||
], | ||
type: 'array', | ||
}, | ||
}, | ||
@@ -82,16 +110,2 @@ defaultOptions: [{ accessibility: 'explicit' }], | ||
/** | ||
* Generates the report for rule violations | ||
*/ | ||
function reportIssue(messageId, nodeType, node, nodeName, fix = null) { | ||
context.report({ | ||
node: node, | ||
messageId: messageId, | ||
data: { | ||
type: nodeType, | ||
name: nodeName, | ||
}, | ||
fix: fix, | ||
}); | ||
} | ||
/** | ||
* Checks if a method declaration has an accessibility modifier. | ||
@@ -101,2 +115,5 @@ * @param methodDefinition The node representing a MethodDefinition. | ||
function checkMethodAccessibilityModifier(methodDefinition) { | ||
if (methodDefinition.key.type === utils_1.AST_NODE_TYPES.PrivateIdentifier) { | ||
return; | ||
} | ||
let nodeType = 'method definition'; | ||
@@ -117,3 +134,3 @@ let check = baseCheck; | ||
} | ||
const methodName = util.getNameFromMember(methodDefinition, sourceCode); | ||
const { name: methodName } = util.getNameFromMember(methodDefinition, sourceCode); | ||
if (check === 'off' || ignoredMethodNames.has(methodName)) { | ||
@@ -124,6 +141,22 @@ return; | ||
methodDefinition.accessibility === 'public') { | ||
reportIssue('unwantedPublicAccessibility', nodeType, methodDefinition, methodName, getUnwantedPublicAccessibilityFixer(methodDefinition)); | ||
context.report({ | ||
node: methodDefinition, | ||
messageId: 'unwantedPublicAccessibility', | ||
data: { | ||
type: nodeType, | ||
name: methodName, | ||
}, | ||
fix: getUnwantedPublicAccessibilityFixer(methodDefinition), | ||
}); | ||
} | ||
else if (check === 'explicit' && !methodDefinition.accessibility) { | ||
reportIssue('missingAccessibility', nodeType, methodDefinition, methodName); | ||
context.report({ | ||
node: methodDefinition, | ||
messageId: 'missingAccessibility', | ||
data: { | ||
type: nodeType, | ||
name: methodName, | ||
}, | ||
suggest: getMissingAccessibilitySuggestions(methodDefinition), | ||
}); | ||
} | ||
@@ -140,3 +173,3 @@ } | ||
const token = tokens[i]; | ||
if (token.type === experimental_utils_1.AST_TOKEN_TYPES.Keyword && | ||
if (token.type === utils_1.AST_TOKEN_TYPES.Keyword && | ||
token.value === 'public') { | ||
@@ -165,14 +198,65 @@ const commensAfterPublicKeyword = sourceCode.getCommentsAfter(token); | ||
/** | ||
* Creates a fixer that adds a "public" keyword with following spaces | ||
*/ | ||
function getMissingAccessibilitySuggestions(node) { | ||
function fix(accessibility, fixer) { | ||
var _a; | ||
if ((_a = node === null || node === void 0 ? void 0 : node.decorators) === null || _a === void 0 ? void 0 : _a.length) { | ||
const lastDecorator = node.decorators[node.decorators.length - 1]; | ||
const nextToken = sourceCode.getTokenAfter(lastDecorator); | ||
return fixer.insertTextBefore(nextToken, `${accessibility} `); | ||
} | ||
return fixer.insertTextBefore(node, `${accessibility} `); | ||
} | ||
return [ | ||
{ | ||
messageId: 'addExplicitAccessibility', | ||
data: { type: 'public' }, | ||
fix: fixer => fix('public', fixer), | ||
}, | ||
{ | ||
messageId: 'addExplicitAccessibility', | ||
data: { type: 'private' }, | ||
fix: fixer => fix('private', fixer), | ||
}, | ||
{ | ||
messageId: 'addExplicitAccessibility', | ||
data: { type: 'protected' }, | ||
fix: fixer => fix('protected', fixer), | ||
}, | ||
]; | ||
} | ||
/** | ||
* Checks if property has an accessibility modifier. | ||
* @param classProperty The node representing a ClassProperty. | ||
* @param propertyDefinition The node representing a PropertyDefinition. | ||
*/ | ||
function checkPropertyAccessibilityModifier(classProperty) { | ||
function checkPropertyAccessibilityModifier(propertyDefinition) { | ||
if (propertyDefinition.key.type === utils_1.AST_NODE_TYPES.PrivateIdentifier) { | ||
return; | ||
} | ||
const nodeType = 'class property'; | ||
const propertyName = util.getNameFromMember(classProperty, sourceCode); | ||
const { name: propertyName } = util.getNameFromMember(propertyDefinition, sourceCode); | ||
if (propCheck === 'no-public' && | ||
classProperty.accessibility === 'public') { | ||
reportIssue('unwantedPublicAccessibility', nodeType, classProperty, propertyName, getUnwantedPublicAccessibilityFixer(classProperty)); | ||
propertyDefinition.accessibility === 'public') { | ||
context.report({ | ||
node: propertyDefinition, | ||
messageId: 'unwantedPublicAccessibility', | ||
data: { | ||
type: nodeType, | ||
name: propertyName, | ||
}, | ||
fix: getUnwantedPublicAccessibilityFixer(propertyDefinition), | ||
}); | ||
} | ||
else if (propCheck === 'explicit' && !classProperty.accessibility) { | ||
reportIssue('missingAccessibility', nodeType, classProperty, propertyName); | ||
else if (propCheck === 'explicit' && | ||
!propertyDefinition.accessibility) { | ||
context.report({ | ||
node: propertyDefinition, | ||
messageId: 'missingAccessibility', | ||
data: { | ||
type: nodeType, | ||
name: propertyName, | ||
}, | ||
suggest: getMissingAccessibilitySuggestions(propertyDefinition), | ||
}); | ||
} | ||
@@ -187,7 +271,7 @@ } | ||
// HAS to be an identifier or assignment or TSC will throw | ||
if (node.parameter.type !== experimental_utils_1.AST_NODE_TYPES.Identifier && | ||
node.parameter.type !== experimental_utils_1.AST_NODE_TYPES.AssignmentPattern) { | ||
if (node.parameter.type !== utils_1.AST_NODE_TYPES.Identifier && | ||
node.parameter.type !== utils_1.AST_NODE_TYPES.AssignmentPattern) { | ||
return; | ||
} | ||
const nodeName = node.parameter.type === experimental_utils_1.AST_NODE_TYPES.Identifier | ||
const nodeName = node.parameter.type === utils_1.AST_NODE_TYPES.Identifier | ||
? node.parameter.name | ||
@@ -199,3 +283,11 @@ : // has to be an Identifier or TSC will throw an error | ||
if (!node.accessibility) { | ||
reportIssue('missingAccessibility', nodeType, node, nodeName); | ||
context.report({ | ||
node, | ||
messageId: 'missingAccessibility', | ||
data: { | ||
type: nodeType, | ||
name: nodeName, | ||
}, | ||
suggest: getMissingAccessibilitySuggestions(node), | ||
}); | ||
} | ||
@@ -206,3 +298,11 @@ break; | ||
if (node.accessibility === 'public' && node.readonly) { | ||
reportIssue('unwantedPublicAccessibility', nodeType, node, nodeName, getUnwantedPublicAccessibilityFixer(node)); | ||
context.report({ | ||
node, | ||
messageId: 'unwantedPublicAccessibility', | ||
data: { | ||
type: nodeType, | ||
name: nodeName, | ||
}, | ||
fix: getUnwantedPublicAccessibilityFixer(node), | ||
}); | ||
} | ||
@@ -214,5 +314,5 @@ break; | ||
return { | ||
'MethodDefinition, TSAbstractMethodDefinition': checkMethodAccessibilityModifier, | ||
'PropertyDefinition, TSAbstractPropertyDefinition': checkPropertyAccessibilityModifier, | ||
TSParameterProperty: checkParameterPropertyAccessibilityModifier, | ||
ClassProperty: checkPropertyAccessibilityModifier, | ||
MethodDefinition: checkMethodAccessibilityModifier, | ||
}; | ||
@@ -219,0 +319,0 @@ }, |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,4 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const scope_manager_1 = require("@typescript-eslint/scope-manager"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -32,4 +37,3 @@ const explicitReturnTypeUtils_1 = require("../util/explicitReturnTypeUtils"); | ||
description: "Require explicit return and argument types on exported functions' and classes' public class methods", | ||
category: 'Stylistic Issues', | ||
recommended: 'warn', | ||
recommended: false, | ||
}, | ||
@@ -48,17 +52,28 @@ messages: { | ||
allowArgumentsExplicitlyTypedAsAny: { | ||
description: 'Whether to ignore arguments that are explicitly typed as `any`.', | ||
type: 'boolean', | ||
}, | ||
allowDirectConstAssertionInArrowFunctions: { | ||
description: [ | ||
'Whether to ignore return type annotations on body-less arrow functions that return an `as const` type assertion.', | ||
'You must still type the parameters of the function.', | ||
].join('\n'), | ||
type: 'boolean', | ||
}, | ||
allowedNames: { | ||
type: 'array', | ||
description: 'An array of function/method names that will not have their arguments or return values checked.', | ||
items: { | ||
type: 'string', | ||
}, | ||
type: 'array', | ||
}, | ||
allowHigherOrderFunctions: { | ||
description: [ | ||
'Whether to ignore return type annotations on functions immediately returning another function expression.', | ||
'You must still type the parameters of the function.', | ||
].join('\n'), | ||
type: 'boolean', | ||
}, | ||
allowTypedFunctionExpressions: { | ||
description: 'Whether to ignore type annotations on the variable of a function expresion.', | ||
type: 'boolean', | ||
@@ -134,3 +149,3 @@ }, | ||
function report(namedMessageId, unnamedMessageId) { | ||
if (param.type === experimental_utils_1.AST_NODE_TYPES.Identifier) { | ||
if (param.type === utils_1.AST_NODE_TYPES.Identifier) { | ||
context.report({ | ||
@@ -142,3 +157,3 @@ node: param, | ||
} | ||
else if (param.type === experimental_utils_1.AST_NODE_TYPES.ArrayPattern) { | ||
else if (param.type === utils_1.AST_NODE_TYPES.ArrayPattern) { | ||
context.report({ | ||
@@ -150,3 +165,3 @@ node: param, | ||
} | ||
else if (param.type === experimental_utils_1.AST_NODE_TYPES.ObjectPattern) { | ||
else if (param.type === utils_1.AST_NODE_TYPES.ObjectPattern) { | ||
context.report({ | ||
@@ -158,4 +173,4 @@ node: param, | ||
} | ||
else if (param.type === experimental_utils_1.AST_NODE_TYPES.RestElement) { | ||
if (param.argument.type === experimental_utils_1.AST_NODE_TYPES.Identifier) { | ||
else if (param.type === utils_1.AST_NODE_TYPES.RestElement) { | ||
if (param.argument.type === utils_1.AST_NODE_TYPES.Identifier) { | ||
context.report({ | ||
@@ -177,6 +192,6 @@ node: param, | ||
switch (param.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.ArrayPattern: | ||
case experimental_utils_1.AST_NODE_TYPES.Identifier: | ||
case experimental_utils_1.AST_NODE_TYPES.ObjectPattern: | ||
case experimental_utils_1.AST_NODE_TYPES.RestElement: | ||
case utils_1.AST_NODE_TYPES.ArrayPattern: | ||
case utils_1.AST_NODE_TYPES.Identifier: | ||
case utils_1.AST_NODE_TYPES.ObjectPattern: | ||
case utils_1.AST_NODE_TYPES.RestElement: | ||
if (!param.typeAnnotation) { | ||
@@ -187,9 +202,9 @@ report('missingArgType', 'missingArgTypeUnnamed'); | ||
param.typeAnnotation.typeAnnotation.type === | ||
experimental_utils_1.AST_NODE_TYPES.TSAnyKeyword) { | ||
utils_1.AST_NODE_TYPES.TSAnyKeyword) { | ||
report('anyTypedArg', 'anyTypedArgUnnamed'); | ||
} | ||
return; | ||
case experimental_utils_1.AST_NODE_TYPES.TSParameterProperty: | ||
case utils_1.AST_NODE_TYPES.TSParameterProperty: | ||
return checkParameter(param.parameter); | ||
case experimental_utils_1.AST_NODE_TYPES.AssignmentPattern: // ignored as it has a type via its assignment | ||
case utils_1.AST_NODE_TYPES.AssignmentPattern: // ignored as it has a type via its assignment | ||
return; | ||
@@ -210,19 +225,20 @@ } | ||
} | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.VariableDeclarator || | ||
node.type === experimental_utils_1.AST_NODE_TYPES.FunctionDeclaration) { | ||
return (((_a = node.id) === null || _a === void 0 ? void 0 : _a.type) === experimental_utils_1.AST_NODE_TYPES.Identifier && | ||
if (node.type === utils_1.AST_NODE_TYPES.VariableDeclarator || | ||
node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration) { | ||
return (((_a = node.id) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.Identifier && | ||
options.allowedNames.includes(node.id.name)); | ||
} | ||
else if (node.type === experimental_utils_1.AST_NODE_TYPES.MethodDefinition || | ||
node.type === experimental_utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition || | ||
(node.type === experimental_utils_1.AST_NODE_TYPES.Property && node.method)) { | ||
if (node.key.type === experimental_utils_1.AST_NODE_TYPES.Literal && | ||
else if (node.type === utils_1.AST_NODE_TYPES.MethodDefinition || | ||
node.type === utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition || | ||
(node.type === utils_1.AST_NODE_TYPES.Property && node.method) || | ||
node.type === utils_1.AST_NODE_TYPES.PropertyDefinition) { | ||
if (node.key.type === utils_1.AST_NODE_TYPES.Literal && | ||
typeof node.key.value === 'string') { | ||
return options.allowedNames.includes(node.key.value); | ||
} | ||
if (node.key.type === experimental_utils_1.AST_NODE_TYPES.TemplateLiteral && | ||
if (node.key.type === utils_1.AST_NODE_TYPES.TemplateLiteral && | ||
node.key.expressions.length === 0) { | ||
return options.allowedNames.includes(node.key.quasis[0].value.raw); | ||
} | ||
if (!node.computed && node.key.type === experimental_utils_1.AST_NODE_TYPES.Identifier) { | ||
if (!node.computed && node.key.type === utils_1.AST_NODE_TYPES.Identifier) { | ||
return options.allowedNames.includes(node.key.name); | ||
@@ -237,3 +253,3 @@ } | ||
while (current) { | ||
if (current.type === experimental_utils_1.AST_NODE_TYPES.ReturnStatement) { | ||
if (current.type === utils_1.AST_NODE_TYPES.ReturnStatement) { | ||
// the parent of a return will always be a block statement, so we can skip over it | ||
@@ -244,3 +260,3 @@ current = (_a = current.parent) === null || _a === void 0 ? void 0 : _a.parent; | ||
if (!util.isFunction(current) || | ||
!explicitReturnTypeUtils_1.doesImmediatelyReturnFunctionExpression(current)) { | ||
!(0, explicitReturnTypeUtils_1.doesImmediatelyReturnFunctionExpression)(current)) { | ||
return false; | ||
@@ -264,7 +280,8 @@ } | ||
// cases we don't care about in this rule | ||
if (definition.type === 'ImplicitGlobalVariable' || | ||
definition.type === 'ImportBinding' || | ||
// eslint-disable-next-line @typescript-eslint/internal/prefer-ast-types-enum | ||
definition.type === 'CatchClause' || | ||
definition.type === 'Parameter') { | ||
if ([ | ||
scope_manager_1.DefinitionType.ImplicitGlobalVariable, | ||
scope_manager_1.DefinitionType.ImportBinding, | ||
scope_manager_1.DefinitionType.CatchClause, | ||
scope_manager_1.DefinitionType.Parameter, | ||
].includes(definition.type)) { | ||
continue; | ||
@@ -290,6 +307,6 @@ } | ||
switch (node.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.ArrowFunctionExpression: | ||
case experimental_utils_1.AST_NODE_TYPES.FunctionExpression: | ||
case utils_1.AST_NODE_TYPES.ArrowFunctionExpression: | ||
case utils_1.AST_NODE_TYPES.FunctionExpression: | ||
return checkFunctionExpression(node); | ||
case experimental_utils_1.AST_NODE_TYPES.ArrayExpression: | ||
case utils_1.AST_NODE_TYPES.ArrayExpression: | ||
for (const element of node.elements) { | ||
@@ -299,10 +316,10 @@ checkNode(element); | ||
return; | ||
case experimental_utils_1.AST_NODE_TYPES.ClassProperty: | ||
case experimental_utils_1.AST_NODE_TYPES.TSAbstractClassProperty: | ||
if (node.accessibility === 'private') { | ||
case utils_1.AST_NODE_TYPES.PropertyDefinition: | ||
if (node.accessibility === 'private' || | ||
node.key.type === utils_1.AST_NODE_TYPES.PrivateIdentifier) { | ||
return; | ||
} | ||
return checkNode(node.value); | ||
case experimental_utils_1.AST_NODE_TYPES.ClassDeclaration: | ||
case experimental_utils_1.AST_NODE_TYPES.ClassExpression: | ||
case utils_1.AST_NODE_TYPES.ClassDeclaration: | ||
case utils_1.AST_NODE_TYPES.ClassExpression: | ||
for (const element of node.body.body) { | ||
@@ -312,13 +329,14 @@ checkNode(element); | ||
return; | ||
case experimental_utils_1.AST_NODE_TYPES.FunctionDeclaration: | ||
case utils_1.AST_NODE_TYPES.FunctionDeclaration: | ||
return checkFunction(node); | ||
case experimental_utils_1.AST_NODE_TYPES.MethodDefinition: | ||
case experimental_utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition: | ||
if (node.accessibility === 'private') { | ||
case utils_1.AST_NODE_TYPES.MethodDefinition: | ||
case utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition: | ||
if (node.accessibility === 'private' || | ||
node.key.type === utils_1.AST_NODE_TYPES.PrivateIdentifier) { | ||
return; | ||
} | ||
return checkNode(node.value); | ||
case experimental_utils_1.AST_NODE_TYPES.Identifier: | ||
case utils_1.AST_NODE_TYPES.Identifier: | ||
return followReference(node); | ||
case experimental_utils_1.AST_NODE_TYPES.ObjectExpression: | ||
case utils_1.AST_NODE_TYPES.ObjectExpression: | ||
for (const property of node.properties) { | ||
@@ -328,7 +346,7 @@ checkNode(property); | ||
return; | ||
case experimental_utils_1.AST_NODE_TYPES.Property: | ||
case utils_1.AST_NODE_TYPES.Property: | ||
return checkNode(node.value); | ||
case experimental_utils_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression: | ||
case utils_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression: | ||
return checkEmptyBodyFunctionExpression(node); | ||
case experimental_utils_1.AST_NODE_TYPES.VariableDeclaration: | ||
case utils_1.AST_NODE_TYPES.VariableDeclaration: | ||
for (const declaration of node.declarations) { | ||
@@ -338,52 +356,12 @@ checkNode(declaration); | ||
return; | ||
case experimental_utils_1.AST_NODE_TYPES.VariableDeclarator: | ||
case utils_1.AST_NODE_TYPES.VariableDeclarator: | ||
return checkNode(node.init); | ||
} | ||
} | ||
/** | ||
* Check whether any ancestor of the provided function has a valid return type. | ||
* This function assumes that the function either: | ||
* - belongs to an exported function chain validated by isExportedHigherOrderFunction | ||
* - is directly exported itself | ||
*/ | ||
function ancestorHasReturnType(node) { | ||
let ancestor = node.parent; | ||
if ((ancestor === null || ancestor === void 0 ? void 0 : ancestor.type) === experimental_utils_1.AST_NODE_TYPES.Property) { | ||
ancestor = ancestor.value; | ||
} | ||
// if the ancestor is not a return, then this function was not returned at all, so we can exit early | ||
const isReturnStatement = (ancestor === null || ancestor === void 0 ? void 0 : ancestor.type) === experimental_utils_1.AST_NODE_TYPES.ReturnStatement; | ||
const isBodylessArrow = (ancestor === null || ancestor === void 0 ? void 0 : ancestor.type) === experimental_utils_1.AST_NODE_TYPES.ArrowFunctionExpression && | ||
ancestor.body.type !== experimental_utils_1.AST_NODE_TYPES.BlockStatement; | ||
if (!isReturnStatement && !isBodylessArrow) { | ||
return false; | ||
} | ||
while (ancestor) { | ||
switch (ancestor.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.ArrowFunctionExpression: | ||
case experimental_utils_1.AST_NODE_TYPES.FunctionExpression: | ||
case experimental_utils_1.AST_NODE_TYPES.FunctionDeclaration: | ||
if (ancestor.returnType) { | ||
return true; | ||
} | ||
// assume | ||
break; | ||
// const x: Foo = () => {}; | ||
// Assume that a typed variable types the function expression | ||
case experimental_utils_1.AST_NODE_TYPES.VariableDeclarator: | ||
if (ancestor.id.typeAnnotation) { | ||
return true; | ||
} | ||
break; | ||
} | ||
ancestor = ancestor.parent; | ||
} | ||
return false; | ||
} | ||
function checkEmptyBodyFunctionExpression(node) { | ||
var _a, _b, _c; | ||
const isConstructor = ((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === experimental_utils_1.AST_NODE_TYPES.MethodDefinition && | ||
const isConstructor = ((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.MethodDefinition && | ||
node.parent.kind === 'constructor'; | ||
const isSetAccessor = (((_b = node.parent) === null || _b === void 0 ? void 0 : _b.type) === experimental_utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition || | ||
((_c = node.parent) === null || _c === void 0 ? void 0 : _c.type) === experimental_utils_1.AST_NODE_TYPES.MethodDefinition) && | ||
const isSetAccessor = (((_b = node.parent) === null || _b === void 0 ? void 0 : _b.type) === utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition || | ||
((_c = node.parent) === null || _c === void 0 ? void 0 : _c.type) === utils_1.AST_NODE_TYPES.MethodDefinition) && | ||
node.parent.kind === 'set'; | ||
@@ -404,7 +382,7 @@ if (!isConstructor && !isSetAccessor && !node.returnType) { | ||
if (isAllowedName(node.parent) || | ||
explicitReturnTypeUtils_1.isTypedFunctionExpression(node, options) || | ||
ancestorHasReturnType(node)) { | ||
(0, explicitReturnTypeUtils_1.isTypedFunctionExpression)(node, options) || | ||
(0, explicitReturnTypeUtils_1.ancestorHasReturnType)(node)) { | ||
return; | ||
} | ||
explicitReturnTypeUtils_1.checkFunctionExpressionReturnType(node, options, sourceCode, loc => { | ||
(0, explicitReturnTypeUtils_1.checkFunctionExpressionReturnType)(node, options, sourceCode, loc => { | ||
context.report({ | ||
@@ -423,6 +401,6 @@ node, | ||
checkedFunctions.add(node); | ||
if (isAllowedName(node) || ancestorHasReturnType(node)) { | ||
if (isAllowedName(node) || (0, explicitReturnTypeUtils_1.ancestorHasReturnType)(node)) { | ||
return; | ||
} | ||
explicitReturnTypeUtils_1.checkFunctionReturnType(node, options, sourceCode, loc => { | ||
(0, explicitReturnTypeUtils_1.checkFunctionReturnType)(node, options, sourceCode, loc => { | ||
context.report({ | ||
@@ -429,0 +407,0 @@ node, |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -29,3 +33,2 @@ if (k2 === undefined) k2 = k; | ||
description: 'Require or disallow spacing between function identifiers and their invocations', | ||
category: 'Stylistic Issues', | ||
recommended: false, | ||
@@ -32,0 +35,0 @@ extendsBaseRule: true, |
@@ -7,6 +7,10 @@ "use strict"; | ||
*/ | ||
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment */ | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -28,75 +32,71 @@ if (k2 === undefined) k2 = k; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
var _a; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const indent_1 = __importDefault(require("eslint/lib/rules/indent")); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
const getESLintCoreRule_1 = require("../util/getESLintCoreRule"); | ||
const baseRule = (0, getESLintCoreRule_1.getESLintCoreRule)('indent'); | ||
const KNOWN_NODES = new Set([ | ||
// Class properties aren't yet supported by eslint... | ||
experimental_utils_1.AST_NODE_TYPES.ClassProperty, | ||
utils_1.AST_NODE_TYPES.PropertyDefinition, | ||
// ts keywords | ||
experimental_utils_1.AST_NODE_TYPES.TSAbstractKeyword, | ||
experimental_utils_1.AST_NODE_TYPES.TSAnyKeyword, | ||
experimental_utils_1.AST_NODE_TYPES.TSBooleanKeyword, | ||
experimental_utils_1.AST_NODE_TYPES.TSNeverKeyword, | ||
experimental_utils_1.AST_NODE_TYPES.TSNumberKeyword, | ||
experimental_utils_1.AST_NODE_TYPES.TSStringKeyword, | ||
experimental_utils_1.AST_NODE_TYPES.TSSymbolKeyword, | ||
experimental_utils_1.AST_NODE_TYPES.TSUndefinedKeyword, | ||
experimental_utils_1.AST_NODE_TYPES.TSUnknownKeyword, | ||
experimental_utils_1.AST_NODE_TYPES.TSVoidKeyword, | ||
experimental_utils_1.AST_NODE_TYPES.TSNullKeyword, | ||
utils_1.AST_NODE_TYPES.TSAbstractKeyword, | ||
utils_1.AST_NODE_TYPES.TSAnyKeyword, | ||
utils_1.AST_NODE_TYPES.TSBooleanKeyword, | ||
utils_1.AST_NODE_TYPES.TSNeverKeyword, | ||
utils_1.AST_NODE_TYPES.TSNumberKeyword, | ||
utils_1.AST_NODE_TYPES.TSStringKeyword, | ||
utils_1.AST_NODE_TYPES.TSSymbolKeyword, | ||
utils_1.AST_NODE_TYPES.TSUndefinedKeyword, | ||
utils_1.AST_NODE_TYPES.TSUnknownKeyword, | ||
utils_1.AST_NODE_TYPES.TSVoidKeyword, | ||
utils_1.AST_NODE_TYPES.TSNullKeyword, | ||
// ts specific nodes we want to support | ||
experimental_utils_1.AST_NODE_TYPES.TSAbstractClassProperty, | ||
experimental_utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition, | ||
experimental_utils_1.AST_NODE_TYPES.TSArrayType, | ||
experimental_utils_1.AST_NODE_TYPES.TSAsExpression, | ||
experimental_utils_1.AST_NODE_TYPES.TSCallSignatureDeclaration, | ||
experimental_utils_1.AST_NODE_TYPES.TSConditionalType, | ||
experimental_utils_1.AST_NODE_TYPES.TSConstructorType, | ||
experimental_utils_1.AST_NODE_TYPES.TSConstructSignatureDeclaration, | ||
experimental_utils_1.AST_NODE_TYPES.TSDeclareFunction, | ||
experimental_utils_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression, | ||
experimental_utils_1.AST_NODE_TYPES.TSEnumDeclaration, | ||
experimental_utils_1.AST_NODE_TYPES.TSEnumMember, | ||
experimental_utils_1.AST_NODE_TYPES.TSExportAssignment, | ||
experimental_utils_1.AST_NODE_TYPES.TSExternalModuleReference, | ||
experimental_utils_1.AST_NODE_TYPES.TSFunctionType, | ||
experimental_utils_1.AST_NODE_TYPES.TSImportType, | ||
experimental_utils_1.AST_NODE_TYPES.TSIndexedAccessType, | ||
experimental_utils_1.AST_NODE_TYPES.TSIndexSignature, | ||
experimental_utils_1.AST_NODE_TYPES.TSInferType, | ||
experimental_utils_1.AST_NODE_TYPES.TSInterfaceBody, | ||
experimental_utils_1.AST_NODE_TYPES.TSInterfaceDeclaration, | ||
experimental_utils_1.AST_NODE_TYPES.TSInterfaceHeritage, | ||
experimental_utils_1.AST_NODE_TYPES.TSIntersectionType, | ||
experimental_utils_1.AST_NODE_TYPES.TSImportEqualsDeclaration, | ||
experimental_utils_1.AST_NODE_TYPES.TSLiteralType, | ||
experimental_utils_1.AST_NODE_TYPES.TSMappedType, | ||
experimental_utils_1.AST_NODE_TYPES.TSMethodSignature, | ||
utils_1.AST_NODE_TYPES.TSAbstractPropertyDefinition, | ||
utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition, | ||
utils_1.AST_NODE_TYPES.TSArrayType, | ||
utils_1.AST_NODE_TYPES.TSAsExpression, | ||
utils_1.AST_NODE_TYPES.TSCallSignatureDeclaration, | ||
utils_1.AST_NODE_TYPES.TSConditionalType, | ||
utils_1.AST_NODE_TYPES.TSConstructorType, | ||
utils_1.AST_NODE_TYPES.TSConstructSignatureDeclaration, | ||
utils_1.AST_NODE_TYPES.TSDeclareFunction, | ||
utils_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression, | ||
utils_1.AST_NODE_TYPES.TSEnumDeclaration, | ||
utils_1.AST_NODE_TYPES.TSEnumMember, | ||
utils_1.AST_NODE_TYPES.TSExportAssignment, | ||
utils_1.AST_NODE_TYPES.TSExternalModuleReference, | ||
utils_1.AST_NODE_TYPES.TSFunctionType, | ||
utils_1.AST_NODE_TYPES.TSImportType, | ||
utils_1.AST_NODE_TYPES.TSIndexedAccessType, | ||
utils_1.AST_NODE_TYPES.TSIndexSignature, | ||
utils_1.AST_NODE_TYPES.TSInferType, | ||
utils_1.AST_NODE_TYPES.TSInterfaceBody, | ||
utils_1.AST_NODE_TYPES.TSInterfaceDeclaration, | ||
utils_1.AST_NODE_TYPES.TSInterfaceHeritage, | ||
utils_1.AST_NODE_TYPES.TSIntersectionType, | ||
utils_1.AST_NODE_TYPES.TSImportEqualsDeclaration, | ||
utils_1.AST_NODE_TYPES.TSLiteralType, | ||
utils_1.AST_NODE_TYPES.TSMappedType, | ||
utils_1.AST_NODE_TYPES.TSMethodSignature, | ||
'TSMinusToken', | ||
experimental_utils_1.AST_NODE_TYPES.TSModuleBlock, | ||
experimental_utils_1.AST_NODE_TYPES.TSModuleDeclaration, | ||
experimental_utils_1.AST_NODE_TYPES.TSNonNullExpression, | ||
experimental_utils_1.AST_NODE_TYPES.TSParameterProperty, | ||
experimental_utils_1.AST_NODE_TYPES.TSParenthesizedType, | ||
utils_1.AST_NODE_TYPES.TSModuleBlock, | ||
utils_1.AST_NODE_TYPES.TSModuleDeclaration, | ||
utils_1.AST_NODE_TYPES.TSNonNullExpression, | ||
utils_1.AST_NODE_TYPES.TSParameterProperty, | ||
'TSPlusToken', | ||
experimental_utils_1.AST_NODE_TYPES.TSPropertySignature, | ||
experimental_utils_1.AST_NODE_TYPES.TSQualifiedName, | ||
utils_1.AST_NODE_TYPES.TSPropertySignature, | ||
utils_1.AST_NODE_TYPES.TSQualifiedName, | ||
'TSQuestionToken', | ||
experimental_utils_1.AST_NODE_TYPES.TSRestType, | ||
experimental_utils_1.AST_NODE_TYPES.TSThisType, | ||
experimental_utils_1.AST_NODE_TYPES.TSTupleType, | ||
experimental_utils_1.AST_NODE_TYPES.TSTypeAnnotation, | ||
experimental_utils_1.AST_NODE_TYPES.TSTypeLiteral, | ||
experimental_utils_1.AST_NODE_TYPES.TSTypeOperator, | ||
experimental_utils_1.AST_NODE_TYPES.TSTypeParameter, | ||
experimental_utils_1.AST_NODE_TYPES.TSTypeParameterDeclaration, | ||
experimental_utils_1.AST_NODE_TYPES.TSTypeParameterInstantiation, | ||
experimental_utils_1.AST_NODE_TYPES.TSTypeReference, | ||
experimental_utils_1.AST_NODE_TYPES.TSUnionType, | ||
experimental_utils_1.AST_NODE_TYPES.Decorator, | ||
utils_1.AST_NODE_TYPES.TSRestType, | ||
utils_1.AST_NODE_TYPES.TSThisType, | ||
utils_1.AST_NODE_TYPES.TSTupleType, | ||
utils_1.AST_NODE_TYPES.TSTypeAnnotation, | ||
utils_1.AST_NODE_TYPES.TSTypeLiteral, | ||
utils_1.AST_NODE_TYPES.TSTypeOperator, | ||
utils_1.AST_NODE_TYPES.TSTypeParameter, | ||
utils_1.AST_NODE_TYPES.TSTypeParameterDeclaration, | ||
utils_1.AST_NODE_TYPES.TSTypeParameterInstantiation, | ||
utils_1.AST_NODE_TYPES.TSTypeReference, | ||
utils_1.AST_NODE_TYPES.TSUnionType, | ||
utils_1.AST_NODE_TYPES.Decorator, | ||
]); | ||
@@ -109,3 +109,2 @@ exports.default = util.createRule({ | ||
description: 'Enforce consistent indentation', | ||
category: 'Stylistic Issues', | ||
// too opinionated to be recommended | ||
@@ -116,6 +115,5 @@ recommended: false, | ||
fixable: 'whitespace', | ||
schema: indent_1.default.meta.schema, | ||
messages: (_a = indent_1.default.meta.messages) !== null && _a !== void 0 ? _a : { | ||
wrongIndentation: 'Expected indentation of {{expected}} but found {{actual}}.', | ||
}, | ||
hasSuggestions: baseRule.meta.hasSuggestions, | ||
schema: baseRule.meta.schema, | ||
messages: baseRule.meta.messages, | ||
}, | ||
@@ -143,3 +141,3 @@ defaultOptions: [ | ||
}); | ||
const rules = indent_1.default.create(contextWithDefaults); | ||
const rules = baseRule.create(contextWithDefaults); | ||
/** | ||
@@ -151,3 +149,3 @@ * Converts from a TSPropertySignature to a Property | ||
*/ | ||
function TSPropertySignatureToProperty(node, type = experimental_utils_1.AST_NODE_TYPES.Property) { | ||
function TSPropertySignatureToProperty(node, type = utils_1.AST_NODE_TYPES.Property) { | ||
const base = { | ||
@@ -168,3 +166,3 @@ // indent doesn't actually use these | ||
}; | ||
if (type === experimental_utils_1.AST_NODE_TYPES.Property) { | ||
if (type === utils_1.AST_NODE_TYPES.Property) { | ||
return Object.assign({ type }, base); | ||
@@ -194,3 +192,3 @@ } | ||
return rules['BinaryExpression, LogicalExpression']({ | ||
type: experimental_utils_1.AST_NODE_TYPES.BinaryExpression, | ||
type: utils_1.AST_NODE_TYPES.BinaryExpression, | ||
operator: 'as', | ||
@@ -209,5 +207,5 @@ left: node.expression, | ||
return rules.ConditionalExpression({ | ||
type: experimental_utils_1.AST_NODE_TYPES.ConditionalExpression, | ||
type: utils_1.AST_NODE_TYPES.ConditionalExpression, | ||
test: { | ||
type: experimental_utils_1.AST_NODE_TYPES.BinaryExpression, | ||
type: utils_1.AST_NODE_TYPES.BinaryExpression, | ||
operator: 'extends', | ||
@@ -234,3 +232,3 @@ left: node.checkType, | ||
return rules['ObjectExpression, ObjectPattern']({ | ||
type: experimental_utils_1.AST_NODE_TYPES.ObjectExpression, | ||
type: utils_1.AST_NODE_TYPES.ObjectExpression, | ||
properties: node.members.map(member => TSPropertySignatureToProperty(member)), | ||
@@ -248,7 +246,7 @@ // location data | ||
return rules.VariableDeclaration({ | ||
type: experimental_utils_1.AST_NODE_TYPES.VariableDeclaration, | ||
type: utils_1.AST_NODE_TYPES.VariableDeclaration, | ||
kind: 'const', | ||
declarations: [ | ||
{ | ||
type: experimental_utils_1.AST_NODE_TYPES.VariableDeclarator, | ||
type: utils_1.AST_NODE_TYPES.VariableDeclarator, | ||
range: [id.range[0], moduleReference.range[1]], | ||
@@ -261,5 +259,5 @@ loc: { | ||
init: { | ||
type: experimental_utils_1.AST_NODE_TYPES.CallExpression, | ||
type: utils_1.AST_NODE_TYPES.CallExpression, | ||
callee: { | ||
type: experimental_utils_1.AST_NODE_TYPES.Identifier, | ||
type: utils_1.AST_NODE_TYPES.Identifier, | ||
name: 'require', | ||
@@ -296,3 +294,3 @@ range: [ | ||
return rules['MemberExpression, JSXMemberExpression, MetaProperty']({ | ||
type: experimental_utils_1.AST_NODE_TYPES.MemberExpression, | ||
type: utils_1.AST_NODE_TYPES.MemberExpression, | ||
object: node.objectType, | ||
@@ -311,4 +309,4 @@ property: node.indexType, | ||
return rules['BlockStatement, ClassBody']({ | ||
type: experimental_utils_1.AST_NODE_TYPES.ClassBody, | ||
body: node.body.map(p => TSPropertySignatureToProperty(p, experimental_utils_1.AST_NODE_TYPES.ClassProperty)), | ||
type: utils_1.AST_NODE_TYPES.ClassBody, | ||
body: node.body.map(p => TSPropertySignatureToProperty(p, utils_1.AST_NODE_TYPES.PropertyDefinition)), | ||
// location data | ||
@@ -323,3 +321,3 @@ parent: node.parent, | ||
return rules['ClassDeclaration[superClass], ClassExpression[superClass]']({ | ||
type: experimental_utils_1.AST_NODE_TYPES.ClassDeclaration, | ||
type: utils_1.AST_NODE_TYPES.ClassDeclaration, | ||
body: node.body, | ||
@@ -340,6 +338,6 @@ id: null, | ||
return rules['ObjectExpression, ObjectPattern']({ | ||
type: experimental_utils_1.AST_NODE_TYPES.ObjectExpression, | ||
type: utils_1.AST_NODE_TYPES.ObjectExpression, | ||
properties: [ | ||
{ | ||
type: experimental_utils_1.AST_NODE_TYPES.Property, | ||
type: utils_1.AST_NODE_TYPES.Property, | ||
key: node.typeParameter, | ||
@@ -375,3 +373,3 @@ value: node.typeAnnotation, | ||
return rules['BlockStatement, ClassBody']({ | ||
type: experimental_utils_1.AST_NODE_TYPES.BlockStatement, | ||
type: utils_1.AST_NODE_TYPES.BlockStatement, | ||
body: node.body, | ||
@@ -386,3 +384,3 @@ // location data | ||
return rules['MemberExpression, JSXMemberExpression, MetaProperty']({ | ||
type: experimental_utils_1.AST_NODE_TYPES.MemberExpression, | ||
type: utils_1.AST_NODE_TYPES.MemberExpression, | ||
object: node.left, | ||
@@ -401,3 +399,3 @@ property: node.right, | ||
return rules['ArrayExpression, ArrayPattern']({ | ||
type: experimental_utils_1.AST_NODE_TYPES.ArrayExpression, | ||
type: utils_1.AST_NODE_TYPES.ArrayExpression, | ||
elements: node.elementTypes, | ||
@@ -418,3 +416,3 @@ // location data | ||
return rules.JSXOpeningElement({ | ||
type: experimental_utils_1.AST_NODE_TYPES.JSXOpeningElement, | ||
type: utils_1.AST_NODE_TYPES.JSXOpeningElement, | ||
selfClosing: false, | ||
@@ -421,0 +419,0 @@ name: name, |
@@ -12,2 +12,3 @@ "use strict"; | ||
const ban_types_1 = __importDefault(require("./ban-types")); | ||
const block_spacing_1 = __importDefault(require("./block-spacing")); | ||
const brace_style_1 = __importDefault(require("./brace-style")); | ||
@@ -17,5 +18,7 @@ const class_literal_property_style_1 = __importDefault(require("./class-literal-property-style")); | ||
const comma_spacing_1 = __importDefault(require("./comma-spacing")); | ||
const consistent_generic_constructors_1 = __importDefault(require("./consistent-generic-constructors")); | ||
const consistent_indexed_object_style_1 = __importDefault(require("./consistent-indexed-object-style")); | ||
const consistent_type_assertions_1 = __importDefault(require("./consistent-type-assertions")); | ||
const consistent_type_definitions_1 = __importDefault(require("./consistent-type-definitions")); | ||
const consistent_type_exports_1 = __importDefault(require("./consistent-type-exports")); | ||
const consistent_type_imports_1 = __importDefault(require("./consistent-type-imports")); | ||
@@ -30,3 +33,5 @@ const default_param_last_1 = __importDefault(require("./default-param-last")); | ||
const init_declarations_1 = __importDefault(require("./init-declarations")); | ||
const key_spacing_1 = __importDefault(require("./key-spacing")); | ||
const keyword_spacing_1 = __importDefault(require("./keyword-spacing")); | ||
const lines_around_comment_1 = __importDefault(require("./lines-around-comment")); | ||
const lines_between_class_members_1 = __importDefault(require("./lines-between-class-members")); | ||
@@ -42,3 +47,5 @@ const member_delimiter_style_1 = __importDefault(require("./member-delimiter-style")); | ||
const no_dupe_class_members_1 = __importDefault(require("./no-dupe-class-members")); | ||
const no_duplicate_enum_values_1 = __importDefault(require("./no-duplicate-enum-values")); | ||
const no_duplicate_imports_1 = __importDefault(require("./no-duplicate-imports")); | ||
const no_duplicate_type_constituents_1 = __importDefault(require("./no-duplicate-type-constituents")); | ||
const no_dynamic_delete_1 = __importDefault(require("./no-dynamic-delete")); | ||
@@ -56,2 +63,3 @@ const no_empty_function_1 = __importDefault(require("./no-empty-function")); | ||
const no_implied_eval_1 = __importDefault(require("./no-implied-eval")); | ||
const no_import_type_side_effects_1 = __importDefault(require("./no-import-type-side-effects")); | ||
const no_inferrable_types_1 = __importDefault(require("./no-inferrable-types")); | ||
@@ -63,5 +71,8 @@ const no_invalid_this_1 = __importDefault(require("./no-invalid-this")); | ||
const no_magic_numbers_1 = __importDefault(require("./no-magic-numbers")); | ||
const no_meaningless_void_operator_1 = __importDefault(require("./no-meaningless-void-operator")); | ||
const no_misused_new_1 = __importDefault(require("./no-misused-new")); | ||
const no_misused_promises_1 = __importDefault(require("./no-misused-promises")); | ||
const no_mixed_enums_1 = __importDefault(require("./no-mixed-enums")); | ||
const no_namespace_1 = __importDefault(require("./no-namespace")); | ||
const no_non_null_asserted_nullish_coalescing_1 = __importDefault(require("./no-non-null-asserted-nullish-coalescing")); | ||
const no_non_null_asserted_optional_chain_1 = __importDefault(require("./no-non-null-asserted-optional-chain")); | ||
@@ -71,3 +82,5 @@ const no_non_null_assertion_1 = __importDefault(require("./no-non-null-assertion")); | ||
const no_redeclare_1 = __importDefault(require("./no-redeclare")); | ||
const no_redundant_type_constituents_1 = __importDefault(require("./no-redundant-type-constituents")); | ||
const no_require_imports_1 = __importDefault(require("./no-require-imports")); | ||
const no_restricted_imports_1 = __importDefault(require("./no-restricted-imports")); | ||
const no_shadow_1 = __importDefault(require("./no-shadow")); | ||
@@ -83,4 +96,7 @@ const no_this_alias_1 = __importDefault(require("./no-this-alias")); | ||
const no_unnecessary_type_constraint_1 = __importDefault(require("./no-unnecessary-type-constraint")); | ||
const no_unsafe_argument_1 = __importDefault(require("./no-unsafe-argument")); | ||
const no_unsafe_assignment_1 = __importDefault(require("./no-unsafe-assignment")); | ||
const no_unsafe_call_1 = __importDefault(require("./no-unsafe-call")); | ||
const no_unsafe_declaration_merging_1 = __importDefault(require("./no-unsafe-declaration-merging")); | ||
const no_unsafe_enum_comparison_1 = __importDefault(require("./no-unsafe-enum-comparison")); | ||
const no_unsafe_member_access_1 = __importDefault(require("./no-unsafe-member-access")); | ||
@@ -90,8 +106,10 @@ const no_unsafe_return_1 = __importDefault(require("./no-unsafe-return")); | ||
const no_unused_vars_1 = __importDefault(require("./no-unused-vars")); | ||
const no_unused_vars_experimental_1 = __importDefault(require("./no-unused-vars-experimental")); | ||
const no_use_before_define_1 = __importDefault(require("./no-use-before-define")); | ||
const no_useless_constructor_1 = __importDefault(require("./no-useless-constructor")); | ||
const no_useless_empty_export_1 = __importDefault(require("./no-useless-empty-export")); | ||
const no_var_requires_1 = __importDefault(require("./no-var-requires")); | ||
const non_nullable_type_assertion_style_1 = __importDefault(require("./non-nullable-type-assertion-style")); | ||
const object_curly_spacing_1 = __importDefault(require("./object-curly-spacing")); | ||
const padding_line_between_statements_1 = __importDefault(require("./padding-line-between-statements")); | ||
const parameter_properties_1 = __importDefault(require("./parameter-properties")); | ||
const prefer_as_const_1 = __importDefault(require("./prefer-as-const")); | ||
@@ -110,2 +128,3 @@ const prefer_enum_initializers_1 = __importDefault(require("./prefer-enum-initializers")); | ||
const prefer_regexp_exec_1 = __importDefault(require("./prefer-regexp-exec")); | ||
const prefer_return_this_type_1 = __importDefault(require("./prefer-return-this-type")); | ||
const prefer_string_starts_ends_with_1 = __importDefault(require("./prefer-string-starts-ends-with")); | ||
@@ -121,3 +140,5 @@ const prefer_ts_expect_error_1 = __importDefault(require("./prefer-ts-expect-error")); | ||
const semi_1 = __importDefault(require("./semi")); | ||
const sort_type_constituents_1 = __importDefault(require("./sort-type-constituents")); | ||
const sort_type_union_intersection_members_1 = __importDefault(require("./sort-type-union-intersection-members")); | ||
const space_before_blocks_1 = __importDefault(require("./space-before-blocks")); | ||
const space_before_function_paren_1 = __importDefault(require("./space-before-function-paren")); | ||
@@ -139,2 +160,3 @@ const space_infix_ops_1 = __importDefault(require("./space-infix-ops")); | ||
'ban-types': ban_types_1.default, | ||
'block-spacing': block_spacing_1.default, | ||
'brace-style': brace_style_1.default, | ||
@@ -144,5 +166,7 @@ 'class-literal-property-style': class_literal_property_style_1.default, | ||
'comma-spacing': comma_spacing_1.default, | ||
'consistent-generic-constructors': consistent_generic_constructors_1.default, | ||
'consistent-indexed-object-style': consistent_indexed_object_style_1.default, | ||
'consistent-type-assertions': consistent_type_assertions_1.default, | ||
'consistent-type-definitions': consistent_type_definitions_1.default, | ||
'consistent-type-exports': consistent_type_exports_1.default, | ||
'consistent-type-imports': consistent_type_imports_1.default, | ||
@@ -157,3 +181,5 @@ 'default-param-last': default_param_last_1.default, | ||
'init-declarations': init_declarations_1.default, | ||
'key-spacing': key_spacing_1.default, | ||
'keyword-spacing': keyword_spacing_1.default, | ||
'lines-around-comment': lines_around_comment_1.default, | ||
'lines-between-class-members': lines_between_class_members_1.default, | ||
@@ -169,3 +195,5 @@ 'member-delimiter-style': member_delimiter_style_1.default, | ||
'no-dupe-class-members': no_dupe_class_members_1.default, | ||
'no-duplicate-enum-values': no_duplicate_enum_values_1.default, | ||
'no-duplicate-imports': no_duplicate_imports_1.default, | ||
'no-duplicate-type-constituents': no_duplicate_type_constituents_1.default, | ||
'no-dynamic-delete': no_dynamic_delete_1.default, | ||
@@ -183,2 +211,3 @@ 'no-empty-function': no_empty_function_1.default, | ||
'no-implied-eval': no_implied_eval_1.default, | ||
'no-import-type-side-effects': no_import_type_side_effects_1.default, | ||
'no-inferrable-types': no_inferrable_types_1.default, | ||
@@ -190,5 +219,8 @@ 'no-invalid-this': no_invalid_this_1.default, | ||
'no-magic-numbers': no_magic_numbers_1.default, | ||
'no-meaningless-void-operator': no_meaningless_void_operator_1.default, | ||
'no-misused-new': no_misused_new_1.default, | ||
'no-misused-promises': no_misused_promises_1.default, | ||
'no-mixed-enums': no_mixed_enums_1.default, | ||
'no-namespace': no_namespace_1.default, | ||
'no-non-null-asserted-nullish-coalescing': no_non_null_asserted_nullish_coalescing_1.default, | ||
'no-non-null-asserted-optional-chain': no_non_null_asserted_optional_chain_1.default, | ||
@@ -198,3 +230,5 @@ 'no-non-null-assertion': no_non_null_assertion_1.default, | ||
'no-redeclare': no_redeclare_1.default, | ||
'no-redundant-type-constituents': no_redundant_type_constituents_1.default, | ||
'no-require-imports': no_require_imports_1.default, | ||
'no-restricted-imports': no_restricted_imports_1.default, | ||
'no-shadow': no_shadow_1.default, | ||
@@ -210,4 +244,7 @@ 'no-this-alias': no_this_alias_1.default, | ||
'no-unnecessary-type-constraint': no_unnecessary_type_constraint_1.default, | ||
'no-unsafe-argument': no_unsafe_argument_1.default, | ||
'no-unsafe-assignment': no_unsafe_assignment_1.default, | ||
'no-unsafe-call': no_unsafe_call_1.default, | ||
'no-unsafe-declaration-merging': no_unsafe_declaration_merging_1.default, | ||
'no-unsafe-enum-comparison': no_unsafe_enum_comparison_1.default, | ||
'no-unsafe-member-access': no_unsafe_member_access_1.default, | ||
@@ -217,8 +254,10 @@ 'no-unsafe-return': no_unsafe_return_1.default, | ||
'no-unused-vars': no_unused_vars_1.default, | ||
'no-unused-vars-experimental': no_unused_vars_experimental_1.default, | ||
'no-use-before-define': no_use_before_define_1.default, | ||
'no-useless-constructor': no_useless_constructor_1.default, | ||
'no-useless-empty-export': no_useless_empty_export_1.default, | ||
'no-var-requires': no_var_requires_1.default, | ||
'non-nullable-type-assertion-style': non_nullable_type_assertion_style_1.default, | ||
'object-curly-spacing': object_curly_spacing_1.default, | ||
'padding-line-between-statements': padding_line_between_statements_1.default, | ||
'parameter-properties': parameter_properties_1.default, | ||
'prefer-as-const': prefer_as_const_1.default, | ||
@@ -237,2 +276,3 @@ 'prefer-enum-initializers': prefer_enum_initializers_1.default, | ||
'prefer-regexp-exec': prefer_regexp_exec_1.default, | ||
'prefer-return-this-type': prefer_return_this_type_1.default, | ||
'prefer-string-starts-ends-with': prefer_string_starts_ends_with_1.default, | ||
@@ -248,3 +288,5 @@ 'prefer-ts-expect-error': prefer_ts_expect_error_1.default, | ||
semi: semi_1.default, | ||
'sort-type-constituents': sort_type_constituents_1.default, | ||
'sort-type-union-intersection-members': sort_type_union_intersection_members_1.default, | ||
'space-before-blocks': space_before_blocks_1.default, | ||
'space-before-function-paren': space_before_function_paren_1.default, | ||
@@ -251,0 +293,0 @@ 'space-infix-ops': space_infix_ops_1.default, |
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
var _a; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const init_declarations_1 = __importDefault(require("eslint/lib/rules/init-declarations")); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util_1 = require("../util"); | ||
exports.default = util_1.createRule({ | ||
const getESLintCoreRule_1 = require("../util/getESLintCoreRule"); | ||
const baseRule = (0, getESLintCoreRule_1.getESLintCoreRule)('init-declarations'); | ||
exports.default = (0, util_1.createRule)({ | ||
name: 'init-declarations', | ||
@@ -15,20 +12,15 @@ meta: { | ||
docs: { | ||
description: 'require or disallow initialization in variable declarations', | ||
category: 'Variables', | ||
description: 'Require or disallow initialization in variable declarations', | ||
recommended: false, | ||
extendsBaseRule: true, | ||
}, | ||
schema: init_declarations_1.default.meta.schema, | ||
messages: (_a = init_declarations_1.default.meta.messages) !== null && _a !== void 0 ? _a : { | ||
initialized: "Variable '{{idName}}' should be initialized on declaration.", | ||
notInitialized: "Variable '{{idName}}' should not be initialized on declaration.", | ||
}, | ||
hasSuggestions: baseRule.meta.hasSuggestions, | ||
schema: baseRule.meta.schema, | ||
messages: baseRule.meta.messages, | ||
}, | ||
defaultOptions: ['always'], | ||
create(context) { | ||
const rules = init_declarations_1.default.create(context); | ||
const mode = context.options[0] || 'always'; | ||
create(context, [mode]) { | ||
const rules = baseRule.create(context); | ||
return { | ||
'VariableDeclaration:exit'(node) { | ||
var _a, _b, _c; | ||
if (mode === 'always') { | ||
@@ -38,4 +30,3 @@ if (node.declare) { | ||
} | ||
if (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === experimental_utils_1.AST_NODE_TYPES.TSModuleBlock && | ||
((_b = node.parent.parent) === null || _b === void 0 ? void 0 : _b.type) === experimental_utils_1.AST_NODE_TYPES.TSModuleDeclaration && ((_c = node.parent.parent) === null || _c === void 0 ? void 0 : _c.declare)) { | ||
if (isAncestorNamespaceDeclared(node)) { | ||
return; | ||
@@ -47,4 +38,15 @@ } | ||
}; | ||
function isAncestorNamespaceDeclared(node) { | ||
let ancestor = node.parent; | ||
while (ancestor) { | ||
if (ancestor.type === utils_1.AST_NODE_TYPES.TSModuleDeclaration && | ||
ancestor.declare) { | ||
return true; | ||
} | ||
ancestor = ancestor.parent; | ||
} | ||
return false; | ||
} | ||
}, | ||
}); | ||
//# sourceMappingURL=init-declarations.js.map |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -21,10 +25,23 @@ if (k2 === undefined) k2 = k; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
var _a; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const keyword_spacing_1 = __importDefault(require("eslint/lib/rules/keyword-spacing")); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
const getESLintCoreRule_1 = require("../util/getESLintCoreRule"); | ||
const baseRule = (0, getESLintCoreRule_1.getESLintCoreRule)('keyword-spacing'); | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment | ||
const baseSchema = Array.isArray(baseRule.meta.schema) | ||
? baseRule.meta.schema[0] | ||
: baseRule.meta.schema; | ||
const schema = util.deepMerge( | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument -- https://github.com/microsoft/TypeScript/issues/17002 | ||
baseSchema, { | ||
properties: { | ||
overrides: { | ||
properties: { | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access | ||
type: baseSchema.properties.overrides.properties.import, | ||
}, | ||
}, | ||
}, | ||
}); | ||
exports.default = util.createRule({ | ||
@@ -36,3 +53,2 @@ name: 'keyword-spacing', | ||
description: 'Enforce consistent spacing before and after keywords', | ||
category: 'Stylistic Issues', | ||
recommended: false, | ||
@@ -42,14 +58,10 @@ extendsBaseRule: true, | ||
fixable: 'whitespace', | ||
schema: keyword_spacing_1.default.meta.schema, | ||
messages: (_a = keyword_spacing_1.default.meta.messages) !== null && _a !== void 0 ? _a : { | ||
expectedBefore: 'Expected space(s) before "{{value}}".', | ||
expectedAfter: 'Expected space(s) after "{{value}}".', | ||
unexpectedBefore: 'Unexpected space(s) before "{{value}}".', | ||
unexpectedAfter: 'Unexpected space(s) after "{{value}}".', | ||
}, | ||
hasSuggestions: baseRule.meta.hasSuggestions, | ||
schema: [schema], | ||
messages: baseRule.meta.messages, | ||
}, | ||
defaultOptions: [{}], | ||
create(context) { | ||
create(context, [{ after, overrides }]) { | ||
const sourceCode = context.getSourceCode(); | ||
const baseRules = keyword_spacing_1.default.create(context); | ||
const baseRules = baseRule.create(context); | ||
return Object.assign(Object.assign({}, baseRules), { TSAsExpression(node) { | ||
@@ -62,3 +74,3 @@ const asToken = util.nullThrows(sourceCode.getTokenAfter(node.expression, token => token.value === 'as'), util.NullThrowsReasons.MissingToken('as', node.type)); | ||
// so mutating a copy would not change the underlying copy returned by that method | ||
asToken.type = experimental_utils_1.AST_TOKEN_TYPES.Keyword; | ||
asToken.type = utils_1.AST_TOKEN_TYPES.Keyword; | ||
// use this selector just because it is just a call to `checkSpacingAroundFirstToken` | ||
@@ -68,2 +80,37 @@ baseRules.DebuggerStatement(asToken); | ||
asToken.type = oldTokenType; | ||
}, | ||
'ImportDeclaration[importKind=type]'(node) { | ||
var _a, _b, _c, _d; | ||
const { type: typeOptionOverride = {} } = overrides !== null && overrides !== void 0 ? overrides : {}; | ||
const typeToken = sourceCode.getFirstToken(node, { skip: 1 }); | ||
const punctuatorToken = sourceCode.getTokenAfter(typeToken); | ||
if (((_b = (_a = node.specifiers) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.type) === utils_1.AST_NODE_TYPES.ImportDefaultSpecifier) { | ||
return; | ||
} | ||
const spacesBetweenTypeAndPunctuator = punctuatorToken.range[0] - typeToken.range[1]; | ||
if (((_c = typeOptionOverride.after) !== null && _c !== void 0 ? _c : after) === true && | ||
spacesBetweenTypeAndPunctuator === 0) { | ||
context.report({ | ||
loc: typeToken.loc, | ||
messageId: 'expectedAfter', | ||
data: { value: 'type' }, | ||
fix(fixer) { | ||
return fixer.insertTextAfter(typeToken, ' '); | ||
}, | ||
}); | ||
} | ||
if (((_d = typeOptionOverride.after) !== null && _d !== void 0 ? _d : after) === false && | ||
spacesBetweenTypeAndPunctuator > 0) { | ||
context.report({ | ||
loc: typeToken.loc, | ||
messageId: 'unexpectedAfter', | ||
data: { value: 'type' }, | ||
fix(fixer) { | ||
return fixer.removeRange([ | ||
typeToken.range[1], | ||
typeToken.range[1] + spacesBetweenTypeAndPunctuator, | ||
]); | ||
}, | ||
}); | ||
} | ||
} }); | ||
@@ -70,0 +117,0 @@ }, |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -21,11 +25,8 @@ if (k2 === undefined) k2 = k; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
var _a; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const lines_between_class_members_1 = __importDefault(require("eslint/lib/rules/lines-between-class-members")); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
const schema = util.deepMerge(Object.assign({}, lines_between_class_members_1.default.meta.schema), { | ||
const getESLintCoreRule_1 = require("../util/getESLintCoreRule"); | ||
const baseRule = (0, getESLintCoreRule_1.getESLintCoreRule)('lines-between-class-members'); | ||
const schema = util.deepMerge(Object.assign({}, baseRule.meta.schema), { | ||
1: { | ||
@@ -44,3 +45,2 @@ exceptAfterOverload: { | ||
description: 'Require or disallow an empty line between class members', | ||
category: 'Stylistic Issues', | ||
recommended: false, | ||
@@ -50,7 +50,5 @@ extendsBaseRule: true, | ||
fixable: 'whitespace', | ||
hasSuggestions: baseRule.meta.hasSuggestions, | ||
schema, | ||
messages: (_a = lines_between_class_members_1.default.meta.messages) !== null && _a !== void 0 ? _a : { | ||
never: 'Unexpected blank line between class members.', | ||
always: 'Expected blank line between class members.', | ||
}, | ||
messages: baseRule.meta.messages, | ||
}, | ||
@@ -64,9 +62,9 @@ defaultOptions: [ | ||
], | ||
create(context, options) { | ||
var _a; | ||
const rules = lines_between_class_members_1.default.create(context); | ||
const exceptAfterOverload = ((_a = options[1]) === null || _a === void 0 ? void 0 : _a.exceptAfterOverload) && options[0] === 'always'; | ||
create(context, [firstOption, secondOption]) { | ||
const rules = baseRule.create(context); | ||
const exceptAfterOverload = (secondOption === null || secondOption === void 0 ? void 0 : secondOption.exceptAfterOverload) && firstOption === 'always'; | ||
function isOverload(node) { | ||
return (node.type === experimental_utils_1.AST_NODE_TYPES.MethodDefinition && | ||
node.value.type === experimental_utils_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression); | ||
return ((node.type === utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition || | ||
node.type === utils_1.AST_NODE_TYPES.MethodDefinition) && | ||
node.value.type === utils_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression); | ||
} | ||
@@ -73,0 +71,0 @@ return { |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -48,12 +52,47 @@ const definition = { | ||
}; | ||
const isLastTokenEndOfLine = (token, line) => { | ||
const positionInLine = token.loc.start.column; | ||
return positionInLine === line.length - 1; | ||
}; | ||
const isCommentsEndOfLine = (token, comments, line) => { | ||
if (!comments) { | ||
return false; | ||
} | ||
if (comments.loc.end.line > token.loc.end.line) { | ||
return true; | ||
} | ||
const positionInLine = comments.loc.end.column; | ||
return positionInLine === line.length; | ||
}; | ||
const makeFixFunction = ({ optsNone, optsSemi, lastToken, commentsAfterLastToken, missingDelimiter, lastTokenLine, isSingleLine, }) => { | ||
// if removing is the action but last token is not the end of the line | ||
if (optsNone && | ||
!isLastTokenEndOfLine(lastToken, lastTokenLine) && | ||
!isCommentsEndOfLine(lastToken, commentsAfterLastToken, lastTokenLine) && | ||
!isSingleLine) { | ||
return null; | ||
} | ||
return (fixer) => { | ||
if (optsNone) { | ||
// remove the unneeded token | ||
return fixer.remove(lastToken); | ||
} | ||
const token = optsSemi ? ';' : ','; | ||
if (missingDelimiter) { | ||
// add the missing delimiter | ||
return fixer.insertTextAfter(lastToken, token); | ||
} | ||
// correct the current delimiter | ||
return fixer.replaceText(lastToken, token); | ||
}; | ||
}; | ||
exports.default = util.createRule({ | ||
name: 'member-delimiter-style', | ||
meta: { | ||
type: 'suggestion', | ||
type: 'layout', | ||
docs: { | ||
description: 'Require a specific member delimiter style for interfaces and type literals', | ||
category: 'Stylistic Issues', | ||
recommended: false, | ||
}, | ||
fixable: 'code', | ||
fixable: 'whitespace', | ||
messages: { | ||
@@ -77,2 +116,5 @@ unexpectedComma: 'Unexpected separator (,).', | ||
}, | ||
multilineDetection: { | ||
enum: ['brackets', 'last-member'], | ||
}, | ||
}), | ||
@@ -93,2 +135,3 @@ additionalProperties: false, | ||
}, | ||
multilineDetection: 'brackets', | ||
}, | ||
@@ -130,2 +173,7 @@ ], | ||
} | ||
const commentsAfterLastToken = sourceCode | ||
.getCommentsAfter(lastToken) | ||
.pop(); | ||
const sourceCodeLines = sourceCode.getLines(); | ||
const lastTokenLine = sourceCodeLines[(lastToken === null || lastToken === void 0 ? void 0 : lastToken.loc.start.line) - 1]; | ||
const optsSemi = getOption('semi'); | ||
@@ -176,15 +224,11 @@ const optsComma = getOption('comma'); | ||
messageId, | ||
fix(fixer) { | ||
if (optsNone) { | ||
// remove the unneeded token | ||
return fixer.remove(lastToken); | ||
} | ||
const token = optsSemi ? ';' : ','; | ||
if (missingDelimiter) { | ||
// add the missing delimiter | ||
return fixer.insertTextAfter(lastToken, token); | ||
} | ||
// correct the current delimiter | ||
return fixer.replaceText(lastToken, token); | ||
}, | ||
fix: makeFixFunction({ | ||
optsNone, | ||
optsSemi, | ||
lastToken, | ||
commentsAfterLastToken, | ||
missingDelimiter, | ||
lastTokenLine, | ||
isSingleLine: opts.type === 'single-line', | ||
}), | ||
}); | ||
@@ -198,8 +242,17 @@ } | ||
function checkMemberSeparatorStyle(node) { | ||
const isSingleLine = node.loc.start.line === node.loc.end.line; | ||
const members = node.type === experimental_utils_1.AST_NODE_TYPES.TSInterfaceBody ? node.body : node.members; | ||
const typeOpts = node.type === experimental_utils_1.AST_NODE_TYPES.TSInterfaceBody | ||
const members = node.type === utils_1.AST_NODE_TYPES.TSInterfaceBody ? node.body : node.members; | ||
let isSingleLine = node.loc.start.line === node.loc.end.line; | ||
if (options.multilineDetection === 'last-member' && | ||
!isSingleLine && | ||
members.length > 0) { | ||
const lastMember = members[members.length - 1]; | ||
if (lastMember.loc.end.line === node.loc.end.line) { | ||
isSingleLine = true; | ||
} | ||
} | ||
const typeOpts = node.type === utils_1.AST_NODE_TYPES.TSInterfaceBody | ||
? interfaceOptions | ||
: typeLiteralOptions; | ||
const opts = isSingleLine ? typeOpts.singleline : typeOpts.multiline; | ||
const opts = isSingleLine | ||
? Object.assign(Object.assign({}, typeOpts.singleline), { type: 'single-line' }) : Object.assign(Object.assign({}, typeOpts.multiline), { type: 'multi-line' }); | ||
members.forEach((member, index) => { | ||
@@ -206,0 +259,0 @@ checkLastToken(member, opts !== null && opts !== void 0 ? opts : {}, index === members.length - 1); |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -21,5 +25,9 @@ if (k2 === undefined) k2 = k; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.defaultOrder = void 0; | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const natural_compare_lite_1 = __importDefault(require("natural-compare-lite")); | ||
const util = __importStar(require("../util")); | ||
@@ -33,3 +41,13 @@ const neverConfig = { | ||
items: { | ||
enum: memberTypes, | ||
oneOf: [ | ||
{ | ||
enum: memberTypes, | ||
}, | ||
{ | ||
type: 'array', | ||
items: { | ||
enum: memberTypes, | ||
}, | ||
}, | ||
], | ||
}, | ||
@@ -45,4 +63,14 @@ }); | ||
type: 'string', | ||
enum: ['alphabetically', 'as-written'], | ||
enum: [ | ||
'alphabetically', | ||
'alphabetically-case-insensitive', | ||
'as-written', | ||
'natural', | ||
'natural-case-insensitive', | ||
], | ||
}, | ||
optionalityOrder: { | ||
type: 'string', | ||
enum: ['optional-first', 'required-first'], | ||
}, | ||
}, | ||
@@ -54,2 +82,3 @@ additionalProperties: false, | ||
'signature', | ||
'call-signature', | ||
// Fields | ||
@@ -59,2 +88,3 @@ 'public-static-field', | ||
'private-static-field', | ||
'#private-static-field', | ||
'public-decorated-field', | ||
@@ -66,8 +96,9 @@ 'protected-decorated-field', | ||
'private-instance-field', | ||
'#private-instance-field', | ||
'public-abstract-field', | ||
'protected-abstract-field', | ||
'private-abstract-field', | ||
'public-field', | ||
'protected-field', | ||
'private-field', | ||
'#private-field', | ||
'static-field', | ||
@@ -78,2 +109,4 @@ 'instance-field', | ||
'field', | ||
// Static initialization | ||
'static-initialization', | ||
// Constructors | ||
@@ -84,2 +117,48 @@ 'public-constructor', | ||
'constructor', | ||
// Getters | ||
'public-static-get', | ||
'protected-static-get', | ||
'private-static-get', | ||
'#private-static-get', | ||
'public-decorated-get', | ||
'protected-decorated-get', | ||
'private-decorated-get', | ||
'public-instance-get', | ||
'protected-instance-get', | ||
'private-instance-get', | ||
'#private-instance-get', | ||
'public-abstract-get', | ||
'protected-abstract-get', | ||
'public-get', | ||
'protected-get', | ||
'private-get', | ||
'#private-get', | ||
'static-get', | ||
'instance-get', | ||
'abstract-get', | ||
'decorated-get', | ||
'get', | ||
// Setters | ||
'public-static-set', | ||
'protected-static-set', | ||
'private-static-set', | ||
'#private-static-set', | ||
'public-decorated-set', | ||
'protected-decorated-set', | ||
'private-decorated-set', | ||
'public-instance-set', | ||
'protected-instance-set', | ||
'private-instance-set', | ||
'#private-instance-set', | ||
'public-abstract-set', | ||
'protected-abstract-set', | ||
'public-set', | ||
'protected-set', | ||
'private-set', | ||
'#private-set', | ||
'static-set', | ||
'instance-set', | ||
'abstract-set', | ||
'decorated-set', | ||
'set', | ||
// Methods | ||
@@ -89,2 +168,3 @@ 'public-static-method', | ||
'private-static-method', | ||
'#private-static-method', | ||
'public-decorated-method', | ||
@@ -96,8 +176,9 @@ 'protected-decorated-method', | ||
'private-instance-method', | ||
'#private-instance-method', | ||
'public-abstract-method', | ||
'protected-abstract-method', | ||
'private-abstract-method', | ||
'public-method', | ||
'protected-method', | ||
'private-method', | ||
'#private-method', | ||
'static-method', | ||
@@ -109,34 +190,57 @@ 'instance-method', | ||
]; | ||
const allMemberTypes = ['signature', 'field', 'method', 'constructor'].reduce((all, type) => { | ||
all.push(type); | ||
['public', 'protected', 'private'].forEach(accessibility => { | ||
if (type !== 'signature') { | ||
all.push(`${accessibility}-${type}`); // e.g. `public-field` | ||
const allMemberTypes = Array.from([ | ||
'readonly-signature', | ||
'signature', | ||
'readonly-field', | ||
'field', | ||
'method', | ||
'call-signature', | ||
'constructor', | ||
'get', | ||
'set', | ||
'static-initialization', | ||
].reduce((all, type) => { | ||
all.add(type); | ||
['public', 'protected', 'private', '#private'].forEach(accessibility => { | ||
if (type !== 'readonly-signature' && | ||
type !== 'signature' && | ||
type !== 'static-initialization' && | ||
type !== 'call-signature' && | ||
!(type === 'constructor' && accessibility === '#private')) { | ||
all.add(`${accessibility}-${type}`); // e.g. `public-field` | ||
} | ||
// Only class instance fields and methods can have decorators attached to them | ||
if (type === 'field' || type === 'method') { | ||
const decoratedMemberType = `${accessibility}-decorated-${type}`; | ||
const decoratedMemberTypeNoAccessibility = `decorated-${type}`; | ||
if (!all.includes(decoratedMemberType)) { | ||
all.push(decoratedMemberType); | ||
// Only class instance fields, methods, get and set can have decorators attached to them | ||
if (accessibility !== '#private' && | ||
(type === 'readonly-field' || | ||
type === 'field' || | ||
type === 'method' || | ||
type === 'get' || | ||
type === 'set')) { | ||
all.add(`${accessibility}-decorated-${type}`); | ||
all.add(`decorated-${type}`); | ||
} | ||
if (type !== 'constructor' && | ||
type !== 'readonly-signature' && | ||
type !== 'signature' && | ||
type !== 'call-signature') { | ||
// There is no `static-constructor` or `instance-constructor` or `abstract-constructor` | ||
if (accessibility === '#private' || accessibility === 'private') { | ||
['static', 'instance'].forEach(scope => { | ||
all.add(`${scope}-${type}`); | ||
all.add(`${accessibility}-${scope}-${type}`); | ||
}); | ||
} | ||
if (!all.includes(decoratedMemberTypeNoAccessibility)) { | ||
all.push(decoratedMemberTypeNoAccessibility); | ||
else { | ||
['static', 'instance', 'abstract'].forEach(scope => { | ||
all.add(`${scope}-${type}`); | ||
all.add(`${accessibility}-${scope}-${type}`); | ||
}); | ||
} | ||
} | ||
if (type !== 'constructor' && type !== 'signature') { | ||
// There is no `static-constructor` or `instance-constructor` or `abstract-constructor` | ||
['static', 'instance', 'abstract'].forEach(scope => { | ||
if (!all.includes(`${scope}-${type}`)) { | ||
all.push(`${scope}-${type}`); | ||
} | ||
all.push(`${accessibility}-${scope}-${type}`); | ||
}); | ||
} | ||
}); | ||
return all; | ||
}, []); | ||
}, new Set())); | ||
const functionExpressions = [ | ||
experimental_utils_1.AST_NODE_TYPES.FunctionExpression, | ||
experimental_utils_1.AST_NODE_TYPES.ArrowFunctionExpression, | ||
utils_1.AST_NODE_TYPES.FunctionExpression, | ||
utils_1.AST_NODE_TYPES.ArrowFunctionExpression, | ||
]; | ||
@@ -149,20 +253,26 @@ /** | ||
function getNodeType(node) { | ||
// TODO: add missing TSCallSignatureDeclaration | ||
switch (node.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition: | ||
case experimental_utils_1.AST_NODE_TYPES.MethodDefinition: | ||
case utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition: | ||
case utils_1.AST_NODE_TYPES.MethodDefinition: | ||
return node.kind; | ||
case experimental_utils_1.AST_NODE_TYPES.TSMethodSignature: | ||
case utils_1.AST_NODE_TYPES.TSMethodSignature: | ||
return 'method'; | ||
case experimental_utils_1.AST_NODE_TYPES.TSConstructSignatureDeclaration: | ||
case utils_1.AST_NODE_TYPES.TSCallSignatureDeclaration: | ||
return 'call-signature'; | ||
case utils_1.AST_NODE_TYPES.TSConstructSignatureDeclaration: | ||
return 'constructor'; | ||
case experimental_utils_1.AST_NODE_TYPES.TSAbstractClassProperty: | ||
case experimental_utils_1.AST_NODE_TYPES.ClassProperty: | ||
case utils_1.AST_NODE_TYPES.TSAbstractPropertyDefinition: | ||
return node.readonly ? 'readonly-field' : 'field'; | ||
case utils_1.AST_NODE_TYPES.PropertyDefinition: | ||
return node.value && functionExpressions.includes(node.value.type) | ||
? 'method' | ||
: 'field'; | ||
case experimental_utils_1.AST_NODE_TYPES.TSPropertySignature: | ||
return 'field'; | ||
case experimental_utils_1.AST_NODE_TYPES.TSIndexSignature: | ||
return 'signature'; | ||
: node.readonly | ||
? 'readonly-field' | ||
: 'field'; | ||
case utils_1.AST_NODE_TYPES.TSPropertySignature: | ||
return node.readonly ? 'readonly-field' : 'field'; | ||
case utils_1.AST_NODE_TYPES.TSIndexSignature: | ||
return node.readonly ? 'readonly-signature' : 'signature'; | ||
case utils_1.AST_NODE_TYPES.StaticBlock: | ||
return 'static-initialization'; | ||
default: | ||
@@ -173,2 +283,15 @@ return null; | ||
/** | ||
* Gets the raw string value of a member's name | ||
*/ | ||
function getMemberRawName(member, sourceCode) { | ||
const { name, type } = util.getNameFromMember(member, sourceCode); | ||
if (type === util.MemberNameType.Quoted) { | ||
return name.slice(1, -1); | ||
} | ||
if (type === util.MemberNameType.Private) { | ||
return name.slice(1); | ||
} | ||
return name; | ||
} | ||
/** | ||
* Gets the member name based on the member type. | ||
@@ -181,16 +304,20 @@ * | ||
switch (node.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.TSPropertySignature: | ||
case experimental_utils_1.AST_NODE_TYPES.TSMethodSignature: | ||
case experimental_utils_1.AST_NODE_TYPES.TSAbstractClassProperty: | ||
case experimental_utils_1.AST_NODE_TYPES.ClassProperty: | ||
return util.getNameFromMember(node, sourceCode); | ||
case experimental_utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition: | ||
case experimental_utils_1.AST_NODE_TYPES.MethodDefinition: | ||
case utils_1.AST_NODE_TYPES.TSPropertySignature: | ||
case utils_1.AST_NODE_TYPES.TSMethodSignature: | ||
case utils_1.AST_NODE_TYPES.TSAbstractPropertyDefinition: | ||
case utils_1.AST_NODE_TYPES.PropertyDefinition: | ||
return getMemberRawName(node, sourceCode); | ||
case utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition: | ||
case utils_1.AST_NODE_TYPES.MethodDefinition: | ||
return node.kind === 'constructor' | ||
? 'constructor' | ||
: util.getNameFromMember(node, sourceCode); | ||
case experimental_utils_1.AST_NODE_TYPES.TSConstructSignatureDeclaration: | ||
: getMemberRawName(node, sourceCode); | ||
case utils_1.AST_NODE_TYPES.TSConstructSignatureDeclaration: | ||
return 'new'; | ||
case experimental_utils_1.AST_NODE_TYPES.TSIndexSignature: | ||
case utils_1.AST_NODE_TYPES.TSCallSignatureDeclaration: | ||
return 'call'; | ||
case utils_1.AST_NODE_TYPES.TSIndexSignature: | ||
return util.getNameFromIndexSignature(node); | ||
case utils_1.AST_NODE_TYPES.StaticBlock: | ||
return 'static block'; | ||
default: | ||
@@ -201,2 +328,21 @@ return null; | ||
/** | ||
* Returns true if the member is optional based on the member type. | ||
* | ||
* @param node the node to be evaluated. | ||
* | ||
* @returns Whether the member is optional, or false if it cannot be optional at all. | ||
*/ | ||
function isMemberOptional(node) { | ||
switch (node.type) { | ||
case utils_1.AST_NODE_TYPES.TSPropertySignature: | ||
case utils_1.AST_NODE_TYPES.TSMethodSignature: | ||
case utils_1.AST_NODE_TYPES.TSAbstractPropertyDefinition: | ||
case utils_1.AST_NODE_TYPES.PropertyDefinition: | ||
case utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition: | ||
case utils_1.AST_NODE_TYPES.MethodDefinition: | ||
return !!node.optional; | ||
} | ||
return false; | ||
} | ||
/** | ||
* Gets the calculated rank using the provided method definition. | ||
@@ -217,6 +363,19 @@ * The algorithm is as follows: | ||
while (stack.length > 0 && rank === -1) { | ||
rank = orderConfig.indexOf(stack.shift()); | ||
const memberGroup = stack.shift(); | ||
rank = orderConfig.findIndex(memberType => Array.isArray(memberType) | ||
? memberType.includes(memberGroup) | ||
: memberType === memberGroup); | ||
} | ||
return rank; | ||
} | ||
function getAccessibility(node) { | ||
var _a; | ||
if ('accessibility' in node && node.accessibility) { | ||
return node.accessibility; | ||
} | ||
if ('key' in node && ((_a = node.key) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.PrivateIdentifier) { | ||
return '#private'; | ||
} | ||
return 'public'; | ||
} | ||
/** | ||
@@ -230,8 +389,8 @@ * Gets the rank of the node given the order. | ||
const type = getNodeType(node); | ||
if (type === null) { | ||
if (type == null) { | ||
// shouldn't happen but just in case, put it on the end | ||
return orderConfig.length - 1; | ||
} | ||
const abstract = node.type === experimental_utils_1.AST_NODE_TYPES.TSAbstractClassProperty || | ||
node.type === experimental_utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition; | ||
const abstract = node.type === utils_1.AST_NODE_TYPES.TSAbstractPropertyDefinition || | ||
node.type === utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition; | ||
const scope = 'static' in node && node.static | ||
@@ -242,25 +401,51 @@ ? 'static' | ||
: 'instance'; | ||
const accessibility = 'accessibility' in node && node.accessibility | ||
? node.accessibility | ||
: 'public'; | ||
// Collect all existing member groups (e.g. 'public-instance-field', 'instance-field', 'public-field', 'constructor' etc.) | ||
const accessibility = getAccessibility(node); | ||
// Collect all existing member groups that apply to this node... | ||
// (e.g. 'public-instance-field', 'instance-field', 'public-field', 'constructor' etc.) | ||
const memberGroups = []; | ||
if (supportsModifiers) { | ||
const decorated = 'decorators' in node && node.decorators.length > 0; | ||
if (decorated && (type === 'field' || type === 'method')) { | ||
if (decorated && | ||
(type === 'readonly-field' || | ||
type === 'field' || | ||
type === 'method' || | ||
type === 'get' || | ||
type === 'set')) { | ||
memberGroups.push(`${accessibility}-decorated-${type}`); | ||
memberGroups.push(`decorated-${type}`); | ||
if (type === 'readonly-field') { | ||
memberGroups.push(`${accessibility}-decorated-field`); | ||
memberGroups.push(`decorated-field`); | ||
} | ||
} | ||
if (type !== 'constructor') { | ||
// Constructors have no scope | ||
memberGroups.push(`${accessibility}-${scope}-${type}`); | ||
memberGroups.push(`${scope}-${type}`); | ||
if (type !== 'readonly-signature' && | ||
type !== 'signature' && | ||
type !== 'static-initialization') { | ||
if (type !== 'constructor') { | ||
// Constructors have no scope | ||
memberGroups.push(`${accessibility}-${scope}-${type}`); | ||
memberGroups.push(`${scope}-${type}`); | ||
if (type === 'readonly-field') { | ||
memberGroups.push(`${accessibility}-${scope}-field`); | ||
memberGroups.push(`${scope}-field`); | ||
} | ||
} | ||
memberGroups.push(`${accessibility}-${type}`); | ||
if (type === 'readonly-field') { | ||
memberGroups.push(`${accessibility}-field`); | ||
} | ||
} | ||
memberGroups.push(`${accessibility}-${type}`); | ||
} | ||
memberGroups.push(type); | ||
if (type === 'readonly-signature') { | ||
memberGroups.push('signature'); | ||
} | ||
else if (type === 'readonly-field') { | ||
memberGroups.push('field'); | ||
} | ||
// ...then get the rank order for those member groups based on the node | ||
return getRankOrder(memberGroups, orderConfig); | ||
} | ||
/** | ||
* Gets the lowest possible rank higher than target. | ||
* Gets the lowest possible rank(s) higher than target. | ||
* e.g. given the following order: | ||
@@ -278,6 +463,7 @@ * ... | ||
* public-instance-method. | ||
* If a lowest possible rank is a member group, a comma separated list of ranks is returned. | ||
* @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 (-). | ||
* @returns the name(s) of the lowest possible rank without dashes (-). | ||
*/ | ||
@@ -291,3 +477,5 @@ function getLowestRank(ranks, target, order) { | ||
}); | ||
return order[lowest].replace(/-/g, ' '); | ||
const lowestRank = order[lowest]; | ||
const lowestRanks = Array.isArray(lowestRank) ? lowestRank : [lowestRank]; | ||
return lowestRanks.map(rank => rank.replace(/-/g, ' ')).join(', '); | ||
} | ||
@@ -300,8 +488,8 @@ exports.default = util.createRule({ | ||
description: 'Require a consistent member declaration order', | ||
category: 'Stylistic Issues', | ||
recommended: false, | ||
}, | ||
messages: { | ||
incorrectOrder: 'Member "{{member}}" should be declared before member "{{beforeMember}}".', | ||
incorrectOrder: 'Member {{member}} should be declared before member {{beforeMember}}.', | ||
incorrectGroupOrder: 'Member {{name}} should be declared before all {{rank}} definitions.', | ||
incorrectRequiredMembersOrder: `Member {{member}} should be declared after all {{optionalOrRequired}} members.`, | ||
}, | ||
@@ -336,4 +524,18 @@ schema: [ | ||
neverConfig, | ||
arrayConfig(['signature', 'field', 'method', 'constructor']), | ||
objectConfig(['signature', 'field', 'method', 'constructor']), | ||
arrayConfig([ | ||
'readonly-signature', | ||
'signature', | ||
'readonly-field', | ||
'field', | ||
'method', | ||
'constructor', | ||
]), | ||
objectConfig([ | ||
'readonly-signature', | ||
'signature', | ||
'readonly-field', | ||
'field', | ||
'method', | ||
'constructor', | ||
]), | ||
], | ||
@@ -344,4 +546,18 @@ }, | ||
neverConfig, | ||
arrayConfig(['signature', 'field', 'method', 'constructor']), | ||
objectConfig(['signature', 'field', 'method', 'constructor']), | ||
arrayConfig([ | ||
'readonly-signature', | ||
'signature', | ||
'readonly-field', | ||
'field', | ||
'method', | ||
'constructor', | ||
]), | ||
objectConfig([ | ||
'readonly-signature', | ||
'signature', | ||
'readonly-field', | ||
'field', | ||
'method', | ||
'constructor', | ||
]), | ||
], | ||
@@ -409,6 +625,7 @@ }, | ||
* @param members Members to be validated. | ||
* @param caseSensitive indicates if the alpha ordering is case sensitive or not. | ||
* | ||
* @return True if all members are correctly sorted. | ||
*/ | ||
function checkAlphaSort(members) { | ||
function checkAlphaSort(members, order) { | ||
let previousName = ''; | ||
@@ -421,3 +638,3 @@ let isCorrectlySorted = true; | ||
if (name) { | ||
if (name < previousName) { | ||
if (naturalOutOfOrder(name, previousName, order)) { | ||
context.report({ | ||
@@ -438,3 +655,51 @@ node: member, | ||
} | ||
function naturalOutOfOrder(name, previousName, order) { | ||
switch (order) { | ||
case 'alphabetically': | ||
return name < previousName; | ||
case 'alphabetically-case-insensitive': | ||
return name.toLowerCase() < previousName.toLowerCase(); | ||
case 'natural': | ||
return (0, natural_compare_lite_1.default)(name, previousName) !== 1; | ||
case 'natural-case-insensitive': | ||
return ((0, natural_compare_lite_1.default)(name.toLowerCase(), previousName.toLowerCase()) !== 1); | ||
} | ||
} | ||
/** | ||
* Checks if the order of optional and required members is correct based | ||
* on the given 'required' parameter. | ||
* | ||
* @param members Members to be validated. | ||
* @param optionalityOrder Where to place optional members, if not intermixed. | ||
* | ||
* @return True if all required and optional members are correctly sorted. | ||
*/ | ||
function checkRequiredOrder(members, optionalityOrder) { | ||
const switchIndex = members.findIndex((member, i) => i && isMemberOptional(member) !== isMemberOptional(members[i - 1])); | ||
const report = (member) => context.report({ | ||
messageId: 'incorrectRequiredMembersOrder', | ||
loc: member.loc, | ||
data: { | ||
member: getMemberName(member, context.getSourceCode()), | ||
optionalOrRequired: optionalityOrder === 'required-first' ? 'required' : 'optional', | ||
}, | ||
}); | ||
// if the optionality of the first item is correct (based on optionalityOrder) | ||
// then the first 0 inclusive to switchIndex exclusive members all | ||
// have the correct optionality | ||
if (isMemberOptional(members[0]) !== | ||
(optionalityOrder === 'optional-first')) { | ||
report(members[0]); | ||
return false; | ||
} | ||
for (let i = switchIndex + 1; i < members.length; i++) { | ||
if (isMemberOptional(members[i]) !== | ||
isMemberOptional(members[switchIndex])) { | ||
report(members[switchIndex]); | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
/** | ||
* Validates if all members are correctly sorted. | ||
@@ -451,4 +716,23 @@ * | ||
// Standardize config | ||
let order = null; | ||
let order; | ||
let memberTypes; | ||
let optionalityOrder; | ||
// returns true if everything is good and false if an error was reported | ||
const checkOrder = (memberSet) => { | ||
const hasAlphaSort = !!(order && order !== 'as-written'); | ||
// Check order | ||
if (Array.isArray(memberTypes)) { | ||
const grouped = checkGroupSort(memberSet, memberTypes, supportsModifiers); | ||
if (grouped == null) { | ||
return false; | ||
} | ||
if (hasAlphaSort) { | ||
return !grouped.some(groupMember => !checkAlphaSort(groupMember, order)); | ||
} | ||
} | ||
else if (hasAlphaSort) { | ||
return checkAlphaSort(memberSet, order); | ||
} | ||
return true; | ||
}; | ||
if (Array.isArray(orderConfig)) { | ||
@@ -460,15 +744,18 @@ memberTypes = orderConfig; | ||
memberTypes = orderConfig.memberTypes; | ||
optionalityOrder = orderConfig.optionalityOrder; | ||
} | ||
// Check order | ||
if (Array.isArray(memberTypes)) { | ||
const grouped = checkGroupSort(members, memberTypes, supportsModifiers); | ||
if (grouped === null) { | ||
if (!optionalityOrder) { | ||
checkOrder(members); | ||
return; | ||
} | ||
const switchIndex = members.findIndex((member, i) => i && isMemberOptional(member) !== isMemberOptional(members[i - 1])); | ||
if (switchIndex !== -1) { | ||
if (!checkRequiredOrder(members, optionalityOrder)) { | ||
return; | ||
} | ||
if (order === 'alphabetically') { | ||
grouped.some(groupMember => !checkAlphaSort(groupMember)); | ||
} | ||
checkOrder(members.slice(0, switchIndex)); | ||
checkOrder(members.slice(switchIndex)); | ||
} | ||
else if (order === 'alphabetically') { | ||
checkAlphaSort(members); | ||
else { | ||
checkOrder(members); | ||
} | ||
@@ -475,0 +762,0 @@ } |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -30,4 +34,3 @@ exports.default = util.createRule({ | ||
docs: { | ||
description: 'Enforces using a particular method signature syntax.', | ||
category: 'Best Practices', | ||
description: 'Enforce using a particular method signature syntax', | ||
recommended: false, | ||
@@ -94,6 +97,6 @@ }, | ||
} | ||
if (node.parent.type === experimental_utils_1.AST_NODE_TYPES.TSModuleDeclaration) { | ||
if (node.parent.type === utils_1.AST_NODE_TYPES.TSModuleDeclaration) { | ||
return true; | ||
} | ||
if (node.parent.type === experimental_utils_1.AST_NODE_TYPES.Program) { | ||
if (node.parent.type === utils_1.AST_NODE_TYPES.Program) { | ||
return false; | ||
@@ -103,14 +106,14 @@ } | ||
} | ||
return { | ||
return Object.assign(Object.assign({}, (mode === 'property' && { | ||
TSMethodSignature(methodNode) { | ||
if (mode === 'method') { | ||
if (methodNode.kind !== 'method') { | ||
return; | ||
} | ||
const parent = methodNode.parent; | ||
const members = (parent === null || parent === void 0 ? void 0 : parent.type) === experimental_utils_1.AST_NODE_TYPES.TSInterfaceBody | ||
const members = (parent === null || parent === void 0 ? void 0 : parent.type) === utils_1.AST_NODE_TYPES.TSInterfaceBody | ||
? parent.body | ||
: (parent === null || parent === void 0 ? void 0 : parent.type) === experimental_utils_1.AST_NODE_TYPES.TSTypeLiteral | ||
: (parent === null || parent === void 0 ? void 0 : parent.type) === utils_1.AST_NODE_TYPES.TSTypeLiteral | ||
? parent.members | ||
: []; | ||
const duplicatedKeyMethodNodes = members.filter((element) => element.type === experimental_utils_1.AST_NODE_TYPES.TSMethodSignature && | ||
const duplicatedKeyMethodNodes = members.filter((element) => element.type === utils_1.AST_NODE_TYPES.TSMethodSignature && | ||
element !== methodNode && | ||
@@ -180,11 +183,9 @@ getMethodKey(element) === getMethodKey(methodNode)); | ||
}, | ||
})), (mode === 'method' && { | ||
TSPropertySignature(propertyNode) { | ||
var _a; | ||
const typeNode = (_a = propertyNode.typeAnnotation) === null || _a === void 0 ? void 0 : _a.typeAnnotation; | ||
if ((typeNode === null || typeNode === void 0 ? void 0 : typeNode.type) !== experimental_utils_1.AST_NODE_TYPES.TSFunctionType) { | ||
if ((typeNode === null || typeNode === void 0 ? void 0 : typeNode.type) !== utils_1.AST_NODE_TYPES.TSFunctionType) { | ||
return; | ||
} | ||
if (mode === 'property') { | ||
return; | ||
} | ||
context.report({ | ||
@@ -202,5 +203,5 @@ node: propertyNode, | ||
}, | ||
}; | ||
})); | ||
}, | ||
}); | ||
//# sourceMappingURL=method-signature-style.js.map |
@@ -12,4 +12,3 @@ "use strict"; | ||
PredefinedFormats[PredefinedFormats["UPPER_CASE"] = 6] = "UPPER_CASE"; | ||
})(PredefinedFormats || (PredefinedFormats = {})); | ||
exports.PredefinedFormats = PredefinedFormats; | ||
})(PredefinedFormats || (exports.PredefinedFormats = PredefinedFormats = {})); | ||
var UnderscoreOptions; | ||
@@ -24,4 +23,3 @@ (function (UnderscoreOptions) { | ||
UnderscoreOptions[UnderscoreOptions["allowSingleOrDouble"] = 6] = "allowSingleOrDouble"; | ||
})(UnderscoreOptions || (UnderscoreOptions = {})); | ||
exports.UnderscoreOptions = UnderscoreOptions; | ||
})(UnderscoreOptions || (exports.UnderscoreOptions = UnderscoreOptions = {})); | ||
var Selectors; | ||
@@ -49,4 +47,3 @@ (function (Selectors) { | ||
Selectors[Selectors["typeParameter"] = 131072] = "typeParameter"; | ||
})(Selectors || (Selectors = {})); | ||
exports.Selectors = Selectors; | ||
})(Selectors || (exports.Selectors = Selectors = {})); | ||
var MetaSelectors; | ||
@@ -60,4 +57,3 @@ (function (MetaSelectors) { | ||
MetaSelectors[MetaSelectors["property"] = 3584] = "property"; | ||
})(MetaSelectors || (MetaSelectors = {})); | ||
exports.MetaSelectors = MetaSelectors; | ||
})(MetaSelectors || (exports.MetaSelectors = MetaSelectors = {})); | ||
var Modifiers; | ||
@@ -75,25 +71,28 @@ (function (Modifiers) { | ||
Modifiers[Modifiers["private"] = 32] = "private"; | ||
Modifiers[Modifiers["abstract"] = 64] = "abstract"; | ||
Modifiers[Modifiers["#private"] = 64] = "#private"; | ||
Modifiers[Modifiers["abstract"] = 128] = "abstract"; | ||
// destructured variable | ||
Modifiers[Modifiers["destructured"] = 128] = "destructured"; | ||
Modifiers[Modifiers["destructured"] = 256] = "destructured"; | ||
// variables declared in the top-level scope | ||
Modifiers[Modifiers["global"] = 256] = "global"; | ||
Modifiers[Modifiers["global"] = 512] = "global"; | ||
// things that are exported | ||
Modifiers[Modifiers["exported"] = 512] = "exported"; | ||
Modifiers[Modifiers["exported"] = 1024] = "exported"; | ||
// things that are unused | ||
Modifiers[Modifiers["unused"] = 1024] = "unused"; | ||
Modifiers[Modifiers["unused"] = 2048] = "unused"; | ||
// properties that require quoting | ||
Modifiers[Modifiers["requiresQuotes"] = 2048] = "requiresQuotes"; | ||
Modifiers[Modifiers["requiresQuotes"] = 4096] = "requiresQuotes"; | ||
// class members that are overridden | ||
Modifiers[Modifiers["override"] = 8192] = "override"; | ||
// class methods, object function properties, or functions that are async via the `async` keyword | ||
Modifiers[Modifiers["async"] = 16384] = "async"; | ||
// make sure TypeModifiers starts at Modifiers + 1 or else sorting won't work | ||
})(Modifiers || (Modifiers = {})); | ||
exports.Modifiers = Modifiers; | ||
})(Modifiers || (exports.Modifiers = Modifiers = {})); | ||
var TypeModifiers; | ||
(function (TypeModifiers) { | ||
TypeModifiers[TypeModifiers["boolean"] = 4096] = "boolean"; | ||
TypeModifiers[TypeModifiers["string"] = 8192] = "string"; | ||
TypeModifiers[TypeModifiers["number"] = 16384] = "number"; | ||
TypeModifiers[TypeModifiers["function"] = 32768] = "function"; | ||
TypeModifiers[TypeModifiers["array"] = 65536] = "array"; | ||
})(TypeModifiers || (TypeModifiers = {})); | ||
exports.TypeModifiers = TypeModifiers; | ||
TypeModifiers[TypeModifiers["boolean"] = 32768] = "boolean"; | ||
TypeModifiers[TypeModifiers["string"] = 65536] = "string"; | ||
TypeModifiers[TypeModifiers["number"] = 131072] = "number"; | ||
TypeModifiers[TypeModifiers["function"] = 262144] = "function"; | ||
TypeModifiers[TypeModifiers["array"] = 524288] = "array"; | ||
})(TypeModifiers || (exports.TypeModifiers = TypeModifiers = {})); | ||
//# sourceMappingURL=enums.js.map |
@@ -9,3 +9,3 @@ "use strict"; | ||
The licence for the code can be viewed here: | ||
The license for the code can be viewed here: | ||
https://github.com/ajafff/tslint-consistent-codestyle/blob/ab156cc8881bcc401236d999f4ce034b59039e81/LICENSE | ||
@@ -12,0 +12,0 @@ */ |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -76,3 +80,3 @@ if (k2 === undefined) k2 = k; | ||
: [option.selector]; | ||
return selectors.map(selector => (Object.assign({ selector: shared_1.isMetaSelector(selector) | ||
return selectors.map(selector => (Object.assign({ selector: (0, shared_1.isMetaSelector)(selector) | ||
? enums_1.MetaSelectors[selector] | ||
@@ -86,3 +90,3 @@ : enums_1.Selectors[selector] }, normalizedOption))); | ||
return util.getEnumNames(enums_1.Selectors).reduce((acc, k) => { | ||
acc[k] = validator_1.createValidator(k, context, normalizedOptions); | ||
acc[k] = (0, validator_1.createValidator)(k, context, normalizedOptions); | ||
return acc; | ||
@@ -89,0 +93,0 @@ }, {}); |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -23,4 +27,4 @@ if (k2 === undefined) k2 = k; | ||
exports.SCHEMA = void 0; | ||
const util = __importStar(require("../../util")); | ||
const enums_1 = require("./enums"); | ||
const util = __importStar(require("../../util")); | ||
const UNDERSCORE_SCHEMA = { | ||
@@ -167,3 +171,3 @@ type: 'string', | ||
...selectorSchema('default', false, util.getEnumNames(enums_1.Modifiers)), | ||
...selectorSchema('variableLike', false, ['unused']), | ||
...selectorSchema('variableLike', false, ['unused', 'async']), | ||
...selectorSchema('variable', true, [ | ||
@@ -175,4 +179,10 @@ 'const', | ||
'unused', | ||
'async', | ||
]), | ||
...selectorSchema('function', false, ['exported', 'global', 'unused']), | ||
...selectorSchema('function', false, [ | ||
'exported', | ||
'global', | ||
'unused', | ||
'async', | ||
]), | ||
...selectorSchema('parameter', true, ['destructured', 'unused']), | ||
@@ -182,2 +192,3 @@ ...selectorSchema('memberLike', false, [ | ||
'private', | ||
'#private', | ||
'protected', | ||
@@ -188,2 +199,4 @@ 'public', | ||
'static', | ||
'override', | ||
'async', | ||
]), | ||
@@ -193,2 +206,3 @@ ...selectorSchema('classProperty', true, [ | ||
'private', | ||
'#private', | ||
'protected', | ||
@@ -199,2 +213,3 @@ 'public', | ||
'static', | ||
'override', | ||
]), | ||
@@ -219,2 +234,3 @@ ...selectorSchema('objectLiteralProperty', true, [ | ||
'private', | ||
'#private', | ||
'protected', | ||
@@ -225,2 +241,4 @@ 'public', | ||
'static', | ||
'override', | ||
'async', | ||
]), | ||
@@ -230,2 +248,3 @@ ...selectorSchema('classMethod', false, [ | ||
'private', | ||
'#private', | ||
'protected', | ||
@@ -235,2 +254,4 @@ 'public', | ||
'static', | ||
'override', | ||
'async', | ||
]), | ||
@@ -240,2 +261,3 @@ ...selectorSchema('objectLiteralMethod', false, [ | ||
'requiresQuotes', | ||
'async', | ||
]), | ||
@@ -246,2 +268,3 @@ ...selectorSchema('typeMethod', false, ['public', 'requiresQuotes']), | ||
'private', | ||
'#private', | ||
'protected', | ||
@@ -251,2 +274,4 @@ 'public', | ||
'static', | ||
'override', | ||
'async', | ||
]), | ||
@@ -260,2 +285,3 @@ ...selectorSchema('accessor', true, [ | ||
'static', | ||
'override', | ||
]), | ||
@@ -262,0 +288,0 @@ ...selectorSchema('enumMember', false, ['requiresQuotes']), |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -23,7 +27,7 @@ if (k2 === undefined) k2 = k; | ||
exports.createValidator = void 0; | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../../util")); | ||
const enums_1 = require("./enums"); | ||
const format_1 = require("./format"); | ||
const shared_1 = require("./shared"); | ||
const util = __importStar(require("../../util")); | ||
function createValidator(type, context, allConfigs) { | ||
@@ -42,4 +46,4 @@ // make sure the "highest priority" configs are checked first | ||
} | ||
const aIsMeta = shared_1.isMetaSelector(a.selector); | ||
const bIsMeta = shared_1.isMetaSelector(b.selector); | ||
const aIsMeta = (0, shared_1.isMetaSelector)(a.selector); | ||
const bIsMeta = (0, shared_1.isMetaSelector)(b.selector); | ||
// non-meta selectors should go ahead of meta selectors | ||
@@ -52,4 +56,4 @@ if (aIsMeta && !bIsMeta) { | ||
} | ||
const aIsMethodOrProperty = shared_1.isMethodOrPropertySelector(a.selector); | ||
const bIsMethodOrProperty = shared_1.isMethodOrPropertySelector(b.selector); | ||
const aIsMethodOrProperty = (0, shared_1.isMethodOrPropertySelector)(a.selector); | ||
const bIsMethodOrProperty = (0, shared_1.isMethodOrPropertySelector)(b.selector); | ||
// for backward compatibility, method and property have higher precedence than other meta selectors | ||
@@ -68,3 +72,6 @@ if (aIsMethodOrProperty && !bIsMethodOrProperty) { | ||
var _a, _b, _c; | ||
const originalName = node.type === experimental_utils_1.AST_NODE_TYPES.Identifier ? node.name : `${node.value}`; | ||
const originalName = node.type === utils_1.AST_NODE_TYPES.Identifier || | ||
node.type === utils_1.AST_NODE_TYPES.PrivateIdentifier | ||
? node.name | ||
: `${node.value}`; | ||
// return will break the loop and stop checking configs | ||
@@ -87,3 +94,3 @@ // it is only used when the name is known to have failed or succeeded a config. | ||
name = validateUnderscore('leading', config, name, node, originalName); | ||
if (name === null) { | ||
if (name == null) { | ||
// fail | ||
@@ -93,3 +100,3 @@ return; | ||
name = validateUnderscore('trailing', config, name, node, originalName); | ||
if (name === null) { | ||
if (name == null) { | ||
// fail | ||
@@ -99,3 +106,3 @@ return; | ||
name = validateAffix('prefix', config, name, node, originalName); | ||
if (name === null) { | ||
if (name == null) { | ||
// fail | ||
@@ -105,3 +112,3 @@ return; | ||
name = validateAffix('suffix', config, name, node, originalName); | ||
if (name === null) { | ||
if (name == null) { | ||
// fail | ||
@@ -114,3 +121,3 @@ return; | ||
} | ||
if (!validatePredefinedFormat(config, name, node, originalName)) { | ||
if (!validatePredefinedFormat(config, name, node, originalName, modifiers)) { | ||
// fail | ||
@@ -127,3 +134,3 @@ return; | ||
return { | ||
type: shared_1.selectorTypeToMessageString(type), | ||
type: (0, shared_1.selectorTypeToMessageString)(type), | ||
name: originalName, | ||
@@ -294,11 +301,13 @@ processedName, | ||
*/ | ||
function validatePredefinedFormat(config, name, node, originalName) { | ||
function validatePredefinedFormat(config, name, node, originalName, modifiers) { | ||
const formats = config.format; | ||
if (formats === null || formats.length === 0) { | ||
if (!(formats === null || formats === void 0 ? void 0 : formats.length)) { | ||
return true; | ||
} | ||
for (const format of formats) { | ||
const checker = format_1.PredefinedFormatToCheckFunction[format]; | ||
if (checker(name)) { | ||
return true; | ||
if (!modifiers.has(enums_1.Modifiers.requiresQuotes)) { | ||
for (const format of formats) { | ||
const checker = format_1.PredefinedFormatToCheckFunction[format]; | ||
if (checker(name)) { | ||
return true; | ||
} | ||
} | ||
@@ -329,3 +338,3 @@ } | ||
function isCorrectType(node, config, context, selector) { | ||
if (config.types === null) { | ||
if (config.types == null) { | ||
return true; | ||
@@ -332,0 +341,0 @@ } |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,4 +26,4 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const scope_manager_1 = require("@typescript-eslint/scope-manager"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -51,4 +55,3 @@ const naming_convention_utils_1 = require("./naming-convention-utils"); | ||
docs: { | ||
category: 'Variables', | ||
description: 'Enforces naming conventions for everything across a codebase', | ||
description: 'Enforce naming conventions for everything across a codebase', | ||
recommended: false, | ||
@@ -78,3 +81,3 @@ // technically only requires type checking if the user uses "type" modifiers | ||
}, contextWithoutDefaults); | ||
const validators = naming_convention_utils_1.parseOptions(context); | ||
const validators = (0, naming_convention_utils_1.parseOptions)(context); | ||
// getParserServices(context, false) -- dirty hack to work around the docs checker test... | ||
@@ -96,3 +99,6 @@ const compilerOptions = util | ||
const modifiers = new Set(); | ||
if (node.accessibility) { | ||
if ('key' in node && node.key.type === utils_1.AST_NODE_TYPES.PrivateIdentifier) { | ||
modifiers.add(naming_convention_utils_1.Modifiers['#private']); | ||
} | ||
else if (node.accessibility) { | ||
modifiers.add(naming_convention_utils_1.Modifiers[node.accessibility]); | ||
@@ -109,4 +115,7 @@ } | ||
} | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.TSAbstractClassProperty || | ||
node.type === experimental_utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition) { | ||
if ('override' in node && node.override) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.override); | ||
} | ||
if (node.type === utils_1.AST_NODE_TYPES.TSAbstractPropertyDefinition || | ||
node.type === utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.abstract); | ||
@@ -138,115 +147,143 @@ } | ||
// does not match `const { x: y }` | ||
(((_a = id.parent) === null || _a === void 0 ? void 0 : _a.type) === experimental_utils_1.AST_NODE_TYPES.Property && id.parent.shorthand) || | ||
(((_a = id.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.Property && id.parent.shorthand) || | ||
// `const { x = 2 }` | ||
// does not match const `{ x: y = 2 }` | ||
(((_b = id.parent) === null || _b === void 0 ? void 0 : _b.type) === experimental_utils_1.AST_NODE_TYPES.AssignmentPattern && | ||
((_c = id.parent.parent) === null || _c === void 0 ? void 0 : _c.type) === experimental_utils_1.AST_NODE_TYPES.Property && | ||
(((_b = id.parent) === null || _b === void 0 ? void 0 : _b.type) === utils_1.AST_NODE_TYPES.AssignmentPattern && | ||
((_c = id.parent.parent) === null || _c === void 0 ? void 0 : _c.type) === utils_1.AST_NODE_TYPES.Property && | ||
id.parent.parent.shorthand)); | ||
} | ||
return { | ||
function isAsyncMemberOrProperty(propertyOrMemberNode) { | ||
return Boolean('value' in propertyOrMemberNode && | ||
propertyOrMemberNode.value && | ||
'async' in propertyOrMemberNode.value && | ||
propertyOrMemberNode.value.async); | ||
} | ||
function isAsyncVariableIdentifier(id) { | ||
return Boolean(id.parent && | ||
(('async' in id.parent && id.parent.async) || | ||
('init' in id.parent && | ||
id.parent.init && | ||
'async' in id.parent.init && | ||
id.parent.init.async))); | ||
} | ||
const selectors = { | ||
// #region variable | ||
VariableDeclarator(node) { | ||
const validator = validators.variable; | ||
if (!validator) { | ||
return; | ||
} | ||
const identifiers = getIdentifiersFromPattern(node.id); | ||
const baseModifiers = new Set(); | ||
const parent = node.parent; | ||
if ((parent === null || parent === void 0 ? void 0 : parent.type) === experimental_utils_1.AST_NODE_TYPES.VariableDeclaration) { | ||
if (parent.kind === 'const') { | ||
baseModifiers.add(naming_convention_utils_1.Modifiers.const); | ||
VariableDeclarator: { | ||
validator: validators.variable, | ||
handler: (node, validator) => { | ||
const identifiers = getIdentifiersFromPattern(node.id); | ||
const baseModifiers = new Set(); | ||
const parent = node.parent; | ||
if ((parent === null || parent === void 0 ? void 0 : parent.type) === utils_1.AST_NODE_TYPES.VariableDeclaration) { | ||
if (parent.kind === 'const') { | ||
baseModifiers.add(naming_convention_utils_1.Modifiers.const); | ||
} | ||
if (isGlobal(context.getScope())) { | ||
baseModifiers.add(naming_convention_utils_1.Modifiers.global); | ||
} | ||
} | ||
if (isGlobal(context.getScope())) { | ||
baseModifiers.add(naming_convention_utils_1.Modifiers.global); | ||
identifiers.forEach(id => { | ||
const modifiers = new Set(baseModifiers); | ||
if (isDestructured(id)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.destructured); | ||
} | ||
if (isExported(parent, id.name, context.getScope())) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.exported); | ||
} | ||
if (isUnused(id.name)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.unused); | ||
} | ||
if (isAsyncVariableIdentifier(id)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.async); | ||
} | ||
validator(id, modifiers); | ||
}); | ||
}, | ||
}, | ||
// #endregion | ||
// #region function | ||
'FunctionDeclaration, TSDeclareFunction, FunctionExpression': { | ||
validator: validators.function, | ||
handler: (node, validator) => { | ||
if (node.id == null) { | ||
return; | ||
} | ||
} | ||
identifiers.forEach(id => { | ||
const modifiers = new Set(baseModifiers); | ||
if (isDestructured(id)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.destructured); | ||
const modifiers = new Set(); | ||
// functions create their own nested scope | ||
const scope = context.getScope().upper; | ||
if (isGlobal(scope)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.global); | ||
} | ||
if (isExported(parent, id.name, context.getScope())) { | ||
if (isExported(node, node.id.name, scope)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.exported); | ||
} | ||
if (isUnused(id.name)) { | ||
if (isUnused(node.id.name, scope)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.unused); | ||
} | ||
validator(id, modifiers); | ||
}); | ||
if (node.async) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.async); | ||
} | ||
validator(node.id, modifiers); | ||
}, | ||
}, | ||
// #endregion | ||
// #region function | ||
'FunctionDeclaration, TSDeclareFunction, FunctionExpression'(node) { | ||
const validator = validators.function; | ||
if (!validator || node.id === null) { | ||
return; | ||
} | ||
const modifiers = new Set(); | ||
// functions create their own nested scope | ||
const scope = context.getScope().upper; | ||
if (isGlobal(scope)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.global); | ||
} | ||
if (isExported(node, node.id.name, scope)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.exported); | ||
} | ||
if (isUnused(node.id.name, scope)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.unused); | ||
} | ||
validator(node.id, modifiers); | ||
}, | ||
// #endregion function | ||
// #region parameter | ||
'FunctionDeclaration, TSDeclareFunction, TSEmptyBodyFunctionExpression, FunctionExpression, ArrowFunctionExpression'(node) { | ||
const validator = validators.parameter; | ||
if (!validator) { | ||
return; | ||
} | ||
node.params.forEach(param => { | ||
if (param.type === experimental_utils_1.AST_NODE_TYPES.TSParameterProperty) { | ||
return; | ||
} | ||
const identifiers = getIdentifiersFromPattern(param); | ||
identifiers.forEach(i => { | ||
const modifiers = new Set(); | ||
if (isDestructured(i)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.destructured); | ||
'FunctionDeclaration, TSDeclareFunction, TSEmptyBodyFunctionExpression, FunctionExpression, ArrowFunctionExpression': { | ||
validator: validators.parameter, | ||
handler: (node, validator) => { | ||
node.params.forEach(param => { | ||
if (param.type === utils_1.AST_NODE_TYPES.TSParameterProperty) { | ||
return; | ||
} | ||
if (isUnused(i.name)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.unused); | ||
} | ||
validator(i, modifiers); | ||
const identifiers = getIdentifiersFromPattern(param); | ||
identifiers.forEach(i => { | ||
const modifiers = new Set(); | ||
if (isDestructured(i)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.destructured); | ||
} | ||
if (isUnused(i.name)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.unused); | ||
} | ||
validator(i, modifiers); | ||
}); | ||
}); | ||
}); | ||
}, | ||
}, | ||
// #endregion parameter | ||
// #region parameterProperty | ||
TSParameterProperty(node) { | ||
const validator = validators.parameterProperty; | ||
if (!validator) { | ||
return; | ||
} | ||
const modifiers = getMemberModifiers(node); | ||
const identifiers = getIdentifiersFromPattern(node.parameter); | ||
identifiers.forEach(i => { | ||
validator(i, modifiers); | ||
}); | ||
TSParameterProperty: { | ||
validator: validators.parameterProperty, | ||
handler: (node, validator) => { | ||
const modifiers = getMemberModifiers(node); | ||
const identifiers = getIdentifiersFromPattern(node.parameter); | ||
identifiers.forEach(i => { | ||
validator(i, modifiers); | ||
}); | ||
}, | ||
}, | ||
// #endregion parameterProperty | ||
// #region property | ||
':not(ObjectPattern) > Property[computed = false][kind = "init"][value.type != "ArrowFunctionExpression"][value.type != "FunctionExpression"][value.type != "TSEmptyBodyFunctionExpression"]'(node) { | ||
const modifiers = new Set([naming_convention_utils_1.Modifiers.public]); | ||
handleMember(validators.objectLiteralProperty, node, modifiers); | ||
':not(ObjectPattern) > Property[computed = false][kind = "init"][value.type != "ArrowFunctionExpression"][value.type != "FunctionExpression"][value.type != "TSEmptyBodyFunctionExpression"]': { | ||
validator: validators.objectLiteralProperty, | ||
handler: (node, validator) => { | ||
const modifiers = new Set([naming_convention_utils_1.Modifiers.public]); | ||
handleMember(validator, node, modifiers); | ||
}, | ||
}, | ||
':matches(ClassProperty, TSAbstractClassProperty)[computed = false][value.type != "ArrowFunctionExpression"][value.type != "FunctionExpression"][value.type != "TSEmptyBodyFunctionExpression"]'(node) { | ||
const modifiers = getMemberModifiers(node); | ||
handleMember(validators.classProperty, node, modifiers); | ||
':matches(PropertyDefinition, TSAbstractPropertyDefinition)[computed = false][value.type != "ArrowFunctionExpression"][value.type != "FunctionExpression"][value.type != "TSEmptyBodyFunctionExpression"]': { | ||
validator: validators.classProperty, | ||
handler: (node, validator) => { | ||
const modifiers = getMemberModifiers(node); | ||
handleMember(validator, node, modifiers); | ||
}, | ||
}, | ||
'TSPropertySignature[computed = false]'(node) { | ||
const modifiers = new Set([naming_convention_utils_1.Modifiers.public]); | ||
if (node.readonly) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.readonly); | ||
} | ||
handleMember(validators.typeProperty, node, modifiers); | ||
'TSPropertySignature[computed = false][typeAnnotation.typeAnnotation.type != "TSFunctionType"]': { | ||
validator: validators.typeProperty, | ||
handler: (node, validator) => { | ||
const modifiers = new Set([naming_convention_utils_1.Modifiers.public]); | ||
if (node.readonly) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.readonly); | ||
} | ||
handleMember(validator, node, modifiers); | ||
}, | ||
}, | ||
@@ -259,28 +296,52 @@ // #endregion property | ||
'Property[computed = false][kind = "init"][value.type = "TSEmptyBodyFunctionExpression"]', | ||
].join(', ')](node) { | ||
const modifiers = new Set([naming_convention_utils_1.Modifiers.public]); | ||
handleMember(validators.objectLiteralMethod, node, modifiers); | ||
].join(', ')]: { | ||
validator: validators.objectLiteralMethod, | ||
handler: (node, validator) => { | ||
const modifiers = new Set([naming_convention_utils_1.Modifiers.public]); | ||
if (isAsyncMemberOrProperty(node)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.async); | ||
} | ||
handleMember(validator, node, modifiers); | ||
}, | ||
}, | ||
[[ | ||
':matches(ClassProperty, TSAbstractClassProperty)[computed = false][value.type = "ArrowFunctionExpression"]', | ||
':matches(ClassProperty, TSAbstractClassProperty)[computed = false][value.type = "FunctionExpression"]', | ||
':matches(ClassProperty, TSAbstractClassProperty)[computed = false][value.type = "TSEmptyBodyFunctionExpression"]', | ||
':matches(PropertyDefinition, TSAbstractPropertyDefinition)[computed = false][value.type = "ArrowFunctionExpression"]', | ||
':matches(PropertyDefinition, TSAbstractPropertyDefinition)[computed = false][value.type = "FunctionExpression"]', | ||
':matches(PropertyDefinition, TSAbstractPropertyDefinition)[computed = false][value.type = "TSEmptyBodyFunctionExpression"]', | ||
':matches(MethodDefinition, TSAbstractMethodDefinition)[computed = false][kind = "method"]', | ||
].join(', ')](node) { | ||
const modifiers = getMemberModifiers(node); | ||
handleMember(validators.classMethod, node, modifiers); | ||
].join(', ')]: { | ||
validator: validators.classMethod, | ||
handler: (node, validator) => { | ||
const modifiers = getMemberModifiers(node); | ||
if (isAsyncMemberOrProperty(node)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.async); | ||
} | ||
handleMember(validator, node, modifiers); | ||
}, | ||
}, | ||
'TSMethodSignature[computed = false]'(node) { | ||
const modifiers = new Set([naming_convention_utils_1.Modifiers.public]); | ||
handleMember(validators.typeMethod, node, modifiers); | ||
[[ | ||
'TSMethodSignature[computed = false]', | ||
'TSPropertySignature[computed = false][typeAnnotation.typeAnnotation.type = "TSFunctionType"]', | ||
].join(', ')]: { | ||
validator: validators.typeMethod, | ||
handler: (node, validator) => { | ||
const modifiers = new Set([naming_convention_utils_1.Modifiers.public]); | ||
handleMember(validator, node, modifiers); | ||
}, | ||
}, | ||
// #endregion method | ||
// #region accessor | ||
'Property[computed = false]:matches([kind = "get"], [kind = "set"])'(node) { | ||
const modifiers = new Set([naming_convention_utils_1.Modifiers.public]); | ||
handleMember(validators.accessor, node, modifiers); | ||
'Property[computed = false]:matches([kind = "get"], [kind = "set"])': { | ||
validator: validators.accessor, | ||
handler: (node, validator) => { | ||
const modifiers = new Set([naming_convention_utils_1.Modifiers.public]); | ||
handleMember(validator, node, modifiers); | ||
}, | ||
}, | ||
'MethodDefinition[computed = false]:matches([kind = "get"], [kind = "set"])'(node) { | ||
const modifiers = getMemberModifiers(node); | ||
handleMember(validators.accessor, node, modifiers); | ||
'MethodDefinition[computed = false]:matches([kind = "get"], [kind = "set"])': { | ||
validator: validators.accessor, | ||
handler: (node, validator) => { | ||
const modifiers = getMemberModifiers(node); | ||
handleMember(validator, node, modifiers); | ||
}, | ||
}, | ||
@@ -290,106 +351,111 @@ // #endregion accessor | ||
// computed is optional, so can't do [computed = false] | ||
'TSEnumMember[computed != true]'(node) { | ||
const validator = validators.enumMember; | ||
if (!validator) { | ||
return; | ||
} | ||
const id = node.id; | ||
const modifiers = new Set(); | ||
if (requiresQuoting(id, compilerOptions.target)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.requiresQuotes); | ||
} | ||
validator(id, modifiers); | ||
'TSEnumMember[computed != true]': { | ||
validator: validators.enumMember, | ||
handler: (node, validator) => { | ||
const id = node.id; | ||
const modifiers = new Set(); | ||
if (requiresQuoting(id, compilerOptions.target)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.requiresQuotes); | ||
} | ||
validator(id, modifiers); | ||
}, | ||
}, | ||
// #endregion enumMember | ||
// #region class | ||
'ClassDeclaration, ClassExpression'(node) { | ||
const validator = validators.class; | ||
if (!validator) { | ||
return; | ||
} | ||
const id = node.id; | ||
if (id === null) { | ||
return; | ||
} | ||
const modifiers = new Set(); | ||
// classes create their own nested scope | ||
const scope = context.getScope().upper; | ||
if (node.abstract) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.abstract); | ||
} | ||
if (isExported(node, id.name, scope)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.exported); | ||
} | ||
if (isUnused(id.name, scope)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.unused); | ||
} | ||
validator(id, modifiers); | ||
'ClassDeclaration, ClassExpression': { | ||
validator: validators.class, | ||
handler: (node, validator) => { | ||
const id = node.id; | ||
if (id == null) { | ||
return; | ||
} | ||
const modifiers = new Set(); | ||
// classes create their own nested scope | ||
const scope = context.getScope().upper; | ||
if (node.abstract) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.abstract); | ||
} | ||
if (isExported(node, id.name, scope)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.exported); | ||
} | ||
if (isUnused(id.name, scope)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.unused); | ||
} | ||
validator(id, modifiers); | ||
}, | ||
}, | ||
// #endregion class | ||
// #region interface | ||
TSInterfaceDeclaration(node) { | ||
const validator = validators.interface; | ||
if (!validator) { | ||
return; | ||
} | ||
const modifiers = new Set(); | ||
const scope = context.getScope(); | ||
if (isExported(node, node.id.name, scope)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.exported); | ||
} | ||
if (isUnused(node.id.name, scope)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.unused); | ||
} | ||
validator(node.id, modifiers); | ||
TSInterfaceDeclaration: { | ||
validator: validators.interface, | ||
handler: (node, validator) => { | ||
const modifiers = new Set(); | ||
const scope = context.getScope(); | ||
if (isExported(node, node.id.name, scope)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.exported); | ||
} | ||
if (isUnused(node.id.name, scope)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.unused); | ||
} | ||
validator(node.id, modifiers); | ||
}, | ||
}, | ||
// #endregion interface | ||
// #region typeAlias | ||
TSTypeAliasDeclaration(node) { | ||
const validator = validators.typeAlias; | ||
if (!validator) { | ||
return; | ||
} | ||
const modifiers = new Set(); | ||
const scope = context.getScope(); | ||
if (isExported(node, node.id.name, scope)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.exported); | ||
} | ||
if (isUnused(node.id.name, scope)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.unused); | ||
} | ||
validator(node.id, modifiers); | ||
TSTypeAliasDeclaration: { | ||
validator: validators.typeAlias, | ||
handler: (node, validator) => { | ||
const modifiers = new Set(); | ||
const scope = context.getScope(); | ||
if (isExported(node, node.id.name, scope)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.exported); | ||
} | ||
if (isUnused(node.id.name, scope)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.unused); | ||
} | ||
validator(node.id, modifiers); | ||
}, | ||
}, | ||
// #endregion typeAlias | ||
// #region enum | ||
TSEnumDeclaration(node) { | ||
const validator = validators.enum; | ||
if (!validator) { | ||
return; | ||
} | ||
const modifiers = new Set(); | ||
// enums create their own nested scope | ||
const scope = context.getScope().upper; | ||
if (isExported(node, node.id.name, scope)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.exported); | ||
} | ||
if (isUnused(node.id.name, scope)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.unused); | ||
} | ||
validator(node.id, modifiers); | ||
TSEnumDeclaration: { | ||
validator: validators.enum, | ||
handler: (node, validator) => { | ||
const modifiers = new Set(); | ||
// enums create their own nested scope | ||
const scope = context.getScope().upper; | ||
if (isExported(node, node.id.name, scope)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.exported); | ||
} | ||
if (isUnused(node.id.name, scope)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.unused); | ||
} | ||
validator(node.id, modifiers); | ||
}, | ||
}, | ||
// #endregion enum | ||
// #region typeParameter | ||
'TSTypeParameterDeclaration > TSTypeParameter'(node) { | ||
const validator = validators.typeParameter; | ||
if (!validator) { | ||
return; | ||
} | ||
const modifiers = new Set(); | ||
const scope = context.getScope(); | ||
if (isUnused(node.name.name, scope)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.unused); | ||
} | ||
validator(node.name, modifiers); | ||
'TSTypeParameterDeclaration > TSTypeParameter': { | ||
validator: validators.typeParameter, | ||
handler: (node, validator) => { | ||
const modifiers = new Set(); | ||
const scope = context.getScope(); | ||
if (isUnused(node.name.name, scope)) { | ||
modifiers.add(naming_convention_utils_1.Modifiers.unused); | ||
} | ||
validator(node.name, modifiers); | ||
}, | ||
}, | ||
// #endregion typeParameter | ||
}; | ||
return Object.fromEntries(Object.entries(selectors) | ||
.map(([selector, { validator, handler }]) => { | ||
return [ | ||
selector, | ||
(node) => { | ||
handler(node, validator); | ||
}, | ||
]; | ||
}) | ||
.filter((s) => s != null)); | ||
}, | ||
@@ -405,4 +471,4 @@ }); | ||
var _a, _b; | ||
if (((_a = node === null || node === void 0 ? void 0 : node.parent) === null || _a === void 0 ? void 0 : _a.type) === experimental_utils_1.AST_NODE_TYPES.ExportDefaultDeclaration || | ||
((_b = node === null || node === void 0 ? void 0 : node.parent) === null || _b === void 0 ? void 0 : _b.type) === experimental_utils_1.AST_NODE_TYPES.ExportNamedDeclaration) { | ||
if (((_a = node === null || node === void 0 ? void 0 : node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.ExportDefaultDeclaration || | ||
((_b = node === null || node === void 0 ? void 0 : node.parent) === null || _b === void 0 ? void 0 : _b.type) === utils_1.AST_NODE_TYPES.ExportNamedDeclaration) { | ||
return true; | ||
@@ -417,4 +483,4 @@ } | ||
const refParent = ref.identifier.parent; | ||
if ((refParent === null || refParent === void 0 ? void 0 : refParent.type) === experimental_utils_1.AST_NODE_TYPES.ExportDefaultDeclaration || | ||
(refParent === null || refParent === void 0 ? void 0 : refParent.type) === experimental_utils_1.AST_NODE_TYPES.ExportSpecifier) { | ||
if ((refParent === null || refParent === void 0 ? void 0 : refParent.type) === utils_1.AST_NODE_TYPES.ExportDefaultDeclaration || | ||
(refParent === null || refParent === void 0 ? void 0 : refParent.type) === utils_1.AST_NODE_TYPES.ExportSpecifier) { | ||
return true; | ||
@@ -430,9 +496,12 @@ } | ||
} | ||
return (scope.type === experimental_utils_1.TSESLint.Scope.ScopeType.global || | ||
scope.type === experimental_utils_1.TSESLint.Scope.ScopeType.module); | ||
return (scope.type === utils_1.TSESLint.Scope.ScopeType.global || | ||
scope.type === utils_1.TSESLint.Scope.ScopeType.module); | ||
} | ||
function requiresQuoting(node, target) { | ||
const name = node.type === experimental_utils_1.AST_NODE_TYPES.Identifier ? node.name : `${node.value}`; | ||
const name = node.type === utils_1.AST_NODE_TYPES.Identifier || | ||
node.type === utils_1.AST_NODE_TYPES.PrivateIdentifier | ||
? node.name | ||
: `${node.value}`; | ||
return util.requiresQuoting(name, target); | ||
} | ||
//# sourceMappingURL=naming-convention.js.map |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -31,3 +35,2 @@ exports.default = util.createRule({ | ||
description: 'Disallow generic `Array` constructors', | ||
category: 'Stylistic Issues', | ||
recommended: 'error', | ||
@@ -50,3 +53,3 @@ extendsBaseRule: true, | ||
if (node.arguments.length !== 1 && | ||
node.callee.type === experimental_utils_1.AST_NODE_TYPES.Identifier && | ||
node.callee.type === utils_1.AST_NODE_TYPES.Identifier && | ||
node.callee.name === 'Array' && | ||
@@ -53,0 +56,0 @@ !node.typeParameters && |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const ts = __importStar(require("typescript")); | ||
@@ -36,9 +40,8 @@ const util = __importStar(require("../util")); | ||
docs: { | ||
description: 'Requires that `.toString()` is only called on objects which provide useful information when stringified', | ||
category: 'Best Practices', | ||
recommended: false, | ||
description: 'Require `.toString()` to only be called on objects which provide useful information when stringified', | ||
recommended: 'strict', | ||
requiresTypeChecking: true, | ||
}, | ||
messages: { | ||
baseToString: "'{{name}} {{certainty}} evaluate to '[object Object]' when stringified.", | ||
baseToString: "'{{name}}' {{certainty}} evaluate to '[object Object]' when stringified.", | ||
}, | ||
@@ -63,3 +66,3 @@ schema: [ | ||
{ | ||
ignoredTypeNames: ['RegExp'], | ||
ignoredTypeNames: ['Error', 'RegExp', 'URL', 'URLSearchParams'], | ||
}, | ||
@@ -73,3 +76,3 @@ ], | ||
function checkExpression(node, type) { | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.Literal) { | ||
if (node.type === utils_1.AST_NODE_TYPES.Literal) { | ||
return; | ||
@@ -145,3 +148,4 @@ } | ||
} | ||
else if (util.getTypeName(typeChecker, rightType) === 'string') { | ||
else if (util.getTypeName(typeChecker, rightType) === 'string' && | ||
node.left.type !== utils_1.AST_NODE_TYPES.PrivateIdentifier) { | ||
checkExpression(node.left, leftType); | ||
@@ -156,3 +160,3 @@ } | ||
if (node.parent && | ||
node.parent.type === experimental_utils_1.AST_NODE_TYPES.TaggedTemplateExpression) { | ||
node.parent.type === utils_1.AST_NODE_TYPES.TaggedTemplateExpression) { | ||
return; | ||
@@ -159,0 +163,0 @@ } |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -31,12 +35,12 @@ exports.default = util.createRule({ | ||
description: 'Disallow non-null assertion in locations that may be confusing', | ||
category: 'Stylistic Issues', | ||
recommended: false, | ||
recommended: 'strict', | ||
}, | ||
fixable: 'code', | ||
hasSuggestions: true, | ||
messages: { | ||
confusingEqual: 'Confusing combinations of non-null assertion and equal test like "a! == b", which looks very similar to not equal "a !== b"', | ||
confusingAssign: 'Confusing combinations of non-null assertion and equal test like "a! = b", which looks very similar to not equal "a != b"', | ||
notNeedInEqualTest: 'Unnecessary non-null assertion (!) in equal test', | ||
notNeedInAssign: 'Unnecessary non-null assertion (!) in assignment left hand', | ||
wrapUpLeft: 'Wrap up left hand to avoid putting non-null assertion "!" and "=" together', | ||
confusingEqual: 'Confusing combinations of non-null assertion and equal test like "a! == b", which looks very similar to not equal "a !== b".', | ||
confusingAssign: 'Confusing combinations of non-null assertion and equal test like "a! = b", which looks very similar to not equal "a != b".', | ||
notNeedInEqualTest: 'Unnecessary non-null assertion (!) in equal test.', | ||
notNeedInAssign: 'Unnecessary non-null assertion (!) in assignment left hand.', | ||
wrapUpLeft: 'Wrap up left hand to avoid putting non-null assertion "!" and "=" together.', | ||
}, | ||
@@ -51,3 +55,3 @@ schema: [], | ||
function isLeftHandPrimaryExpression(node) { | ||
return node.type === experimental_utils_1.AST_NODE_TYPES.TSNonNullExpression; | ||
return node.type === utils_1.AST_NODE_TYPES.TSNonNullExpression; | ||
} | ||
@@ -60,3 +64,3 @@ if (node.operator === '==' || | ||
const tokenAfterLeft = sourceCode.getTokenAfter(node.left); | ||
if ((leftHandFinalToken === null || leftHandFinalToken === void 0 ? void 0 : leftHandFinalToken.type) === experimental_utils_1.AST_TOKEN_TYPES.Punctuator && | ||
if ((leftHandFinalToken === null || leftHandFinalToken === void 0 ? void 0 : leftHandFinalToken.type) === utils_1.AST_TOKEN_TYPES.Punctuator && | ||
(leftHandFinalToken === null || leftHandFinalToken === void 0 ? void 0 : leftHandFinalToken.value) === '!' && | ||
@@ -63,0 +67,0 @@ (tokenAfterLeft === null || tokenAfterLeft === void 0 ? void 0 : tokenAfterLeft.value) !== ')') { |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const tsutils = __importStar(require("tsutils")); | ||
@@ -31,4 +35,3 @@ const ts = __importStar(require("typescript")); | ||
docs: { | ||
description: 'Requires expressions of type void to appear in statement position', | ||
category: 'Best Practices', | ||
description: 'Require expressions of type void to appear in statement position', | ||
recommended: false, | ||
@@ -53,3 +56,3 @@ requiresTypeChecking: true, | ||
'must be marked explicitly with the `void` operator.', | ||
voidExprWrapVoid: 'Mark with an explicit `void` operator', | ||
voidExprWrapVoid: 'Mark with an explicit `void` operator.', | ||
}, | ||
@@ -68,2 +71,3 @@ schema: [ | ||
fixable: 'code', | ||
hasSuggestions: true, | ||
}, | ||
@@ -93,3 +97,3 @@ defaultOptions: [{}], | ||
}; | ||
if (invalidAncestor.type === experimental_utils_1.AST_NODE_TYPES.ArrowFunctionExpression) { | ||
if (invalidAncestor.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) { | ||
// handle arrow function shorthand | ||
@@ -122,3 +126,3 @@ if (options.ignoreVoidOperator) { | ||
} | ||
if (invalidAncestor.type === experimental_utils_1.AST_NODE_TYPES.ReturnStatement) { | ||
if (invalidAncestor.type === utils_1.AST_NODE_TYPES.ReturnStatement) { | ||
// handle return statement | ||
@@ -164,3 +168,3 @@ if (options.ignoreVoidOperator) { | ||
} | ||
if (((_a = returnStmt.parent) === null || _a === void 0 ? void 0 : _a.type) !== experimental_utils_1.AST_NODE_TYPES.BlockStatement) { | ||
if (((_a = returnStmt.parent) === null || _a === void 0 ? void 0 : _a.type) !== utils_1.AST_NODE_TYPES.BlockStatement) { | ||
// e.g. `if (cond) return console.error();` | ||
@@ -199,3 +203,8 @@ // add braces if not inside a block | ||
const parent = util.nullThrows(node.parent, util.NullThrowsReasons.MissingParent); | ||
if (parent.type === experimental_utils_1.AST_NODE_TYPES.ExpressionStatement) { | ||
if (parent.type === utils_1.AST_NODE_TYPES.SequenceExpression) { | ||
if (node !== parent.expressions[parent.expressions.length - 1]) { | ||
return null; | ||
} | ||
} | ||
if (parent.type === utils_1.AST_NODE_TYPES.ExpressionStatement) { | ||
// e.g. `{ console.log("foo"); }` | ||
@@ -205,3 +214,3 @@ // this is always valid | ||
} | ||
if (parent.type === experimental_utils_1.AST_NODE_TYPES.LogicalExpression) { | ||
if (parent.type === utils_1.AST_NODE_TYPES.LogicalExpression) { | ||
if (parent.right === node) { | ||
@@ -213,3 +222,3 @@ // e.g. `x && console.log(x)` | ||
} | ||
if (parent.type === experimental_utils_1.AST_NODE_TYPES.ConditionalExpression) { | ||
if (parent.type === utils_1.AST_NODE_TYPES.ConditionalExpression) { | ||
if (parent.consequent === node || parent.alternate === node) { | ||
@@ -221,3 +230,3 @@ // e.g. `cond ? console.log(true) : console.log(false)` | ||
} | ||
if (parent.type === experimental_utils_1.AST_NODE_TYPES.ArrowFunctionExpression) { | ||
if (parent.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) { | ||
// e.g. `() => console.log("foo")` | ||
@@ -229,3 +238,3 @@ // this is valid with an appropriate option | ||
} | ||
if (parent.type === experimental_utils_1.AST_NODE_TYPES.UnaryExpression) { | ||
if (parent.type === utils_1.AST_NODE_TYPES.UnaryExpression) { | ||
if (parent.operator === 'void') { | ||
@@ -239,2 +248,6 @@ // e.g. `void console.log("foo")` | ||
} | ||
if (parent.type === utils_1.AST_NODE_TYPES.ChainExpression) { | ||
// e.g. `console?.log('foo')` | ||
return findInvalidAncestor(parent); | ||
} | ||
// any other parent is invalid | ||
@@ -247,3 +260,3 @@ return parent; | ||
const block = util.nullThrows(node.parent, util.NullThrowsReasons.MissingParent); | ||
if (block.type !== experimental_utils_1.AST_NODE_TYPES.BlockStatement) { | ||
if (block.type !== utils_1.AST_NODE_TYPES.BlockStatement) { | ||
// e.g. `if (cond) return;` (not in a block) | ||
@@ -255,5 +268,5 @@ return false; | ||
if (![ | ||
experimental_utils_1.AST_NODE_TYPES.FunctionDeclaration, | ||
experimental_utils_1.AST_NODE_TYPES.FunctionExpression, | ||
experimental_utils_1.AST_NODE_TYPES.ArrowFunctionExpression, | ||
utils_1.AST_NODE_TYPES.FunctionDeclaration, | ||
utils_1.AST_NODE_TYPES.FunctionExpression, | ||
utils_1.AST_NODE_TYPES.ArrowFunctionExpression, | ||
].includes(blockParent.type)) { | ||
@@ -260,0 +273,0 @@ // e.g. `if (cond) { return; }` |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -21,9 +25,7 @@ if (k2 === undefined) k2 = k; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const no_dupe_class_members_1 = __importDefault(require("eslint/lib/rules/no-dupe-class-members")); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
const getESLintCoreRule_1 = require("../util/getESLintCoreRule"); | ||
const baseRule = (0, getESLintCoreRule_1.getESLintCoreRule)('no-dupe-class-members'); | ||
exports.default = util.createRule({ | ||
@@ -35,23 +37,35 @@ name: 'no-dupe-class-members', | ||
description: 'Disallow duplicate class members', | ||
category: 'Possible Errors', | ||
recommended: false, | ||
extendsBaseRule: true, | ||
}, | ||
schema: no_dupe_class_members_1.default.meta.schema, | ||
messages: no_dupe_class_members_1.default.meta.messages, | ||
hasSuggestions: baseRule.meta.hasSuggestions, | ||
schema: baseRule.meta.schema, | ||
messages: baseRule.meta.messages, | ||
}, | ||
defaultOptions: [], | ||
create(context) { | ||
const rules = no_dupe_class_members_1.default.create(context); | ||
return Object.assign(Object.assign({}, rules), { MethodDefinition(node) { | ||
const rules = baseRule.create(context); | ||
function wrapMemberDefinitionListener(coreListener) { | ||
return (node) => { | ||
if (node.computed) { | ||
return; | ||
} | ||
if (node.value.type === experimental_utils_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression) { | ||
if (node.value && | ||
node.value.type === utils_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression) { | ||
return; | ||
} | ||
return rules.MethodDefinition(node); | ||
} }); | ||
return coreListener(node); | ||
}; | ||
} | ||
return Object.assign(Object.assign(Object.assign({}, rules), (rules.MethodDefinition | ||
? { | ||
MethodDefinition: wrapMemberDefinitionListener(rules.MethodDefinition), | ||
} | ||
: {})), (rules['MethodDefinition, PropertyDefinition'] | ||
? { | ||
'MethodDefinition, PropertyDefinition': wrapMemberDefinitionListener(rules['MethodDefinition, PropertyDefinition']), | ||
} | ||
: {})); | ||
}, | ||
}); | ||
//# sourceMappingURL=no-dupe-class-members.js.map |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -21,21 +25,21 @@ if (k2 === undefined) k2 = k; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const no_duplicate_imports_1 = __importDefault(require("eslint/lib/rules/no-duplicate-imports")); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
const getESLintCoreRule_1 = require("../util/getESLintCoreRule"); | ||
const baseRule = (0, getESLintCoreRule_1.getESLintCoreRule)('no-duplicate-imports'); | ||
exports.default = util.createRule({ | ||
name: 'no-duplicate-imports', | ||
meta: { | ||
deprecated: true, | ||
replacedBy: ['import/no-duplicates'], | ||
type: 'problem', | ||
docs: { | ||
description: 'Disallow duplicate imports', | ||
category: 'Best Practices', | ||
recommended: false, | ||
extendsBaseRule: true, | ||
}, | ||
schema: no_duplicate_imports_1.default.meta.schema, | ||
messages: Object.assign(Object.assign({}, no_duplicate_imports_1.default.meta.messages), { importType: '{{module}} type import is duplicated', importTypeAs: '{{module}} type import is duplicated as type export', exportType: '{{module}} type export is duplicated', exportTypeAs: '{{module}} type export is duplicated as type import' }), | ||
hasSuggestions: baseRule.meta.hasSuggestions, | ||
schema: baseRule.meta.schema, | ||
messages: Object.assign(Object.assign({}, baseRule.meta.messages), { importType: '{{module}} type import is duplicated.', importTypeAs: '{{module}} type import is duplicated as type export.', exportType: '{{module}} type export is duplicated.', exportTypeAs: '{{module}} type export is duplicated as type import.' }), | ||
}, | ||
@@ -47,5 +51,4 @@ defaultOptions: [ | ||
], | ||
create(context, [option]) { | ||
const rules = no_duplicate_imports_1.default.create(context); | ||
const includeExports = option.includeExports; | ||
create(context, [{ includeExports }]) { | ||
const rules = baseRule.create(context); | ||
const typeMemberImports = new Set(); | ||
@@ -63,12 +66,7 @@ const typeDefaultImports = new Set(); | ||
} | ||
function isStringLiteral(node) { | ||
return (!!node && | ||
node.type === experimental_utils_1.AST_NODE_TYPES.Literal && | ||
typeof node.value === 'string'); | ||
} | ||
function isAllMemberImport(node) { | ||
return node.specifiers.every(specifier => specifier.type === experimental_utils_1.AST_NODE_TYPES.ImportSpecifier); | ||
return node.specifiers.every(specifier => specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier); | ||
} | ||
function checkTypeImport(node) { | ||
if (isStringLiteral(node.source)) { | ||
if (node.source) { | ||
const value = node.source.value; | ||
@@ -93,3 +91,3 @@ const isMemberImport = isAllMemberImport(node); | ||
function checkTypeExport(node) { | ||
if (isStringLiteral(node.source)) { | ||
if (node.source) { | ||
const value = node.source.value; | ||
@@ -96,0 +94,0 @@ if (typeExports.has(value)) { |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const tsutils = __importStar(require("tsutils")); | ||
@@ -30,5 +34,4 @@ const util = __importStar(require("../util")); | ||
docs: { | ||
category: 'Best Practices', | ||
description: 'Disallow the delete operator with computed key expressions', | ||
recommended: false, | ||
description: 'Disallow using the `delete` operator on computed key expressions', | ||
recommended: 'strict', | ||
}, | ||
@@ -45,3 +48,3 @@ fixable: 'code', | ||
function createFixer(member) { | ||
if (member.property.type === experimental_utils_1.AST_NODE_TYPES.Literal && | ||
if (member.property.type === utils_1.AST_NODE_TYPES.Literal && | ||
typeof member.property.value === 'string') { | ||
@@ -54,3 +57,3 @@ return createPropertyReplacement(member.property, `.${member.property.value}`); | ||
'UnaryExpression[operator=delete]'(node) { | ||
if (node.argument.type !== experimental_utils_1.AST_NODE_TYPES.MemberExpression || | ||
if (node.argument.type !== utils_1.AST_NODE_TYPES.MemberExpression || | ||
!node.argument.computed || | ||
@@ -80,3 +83,3 @@ isNecessaryDynamicAccess(diveIntoWrapperExpressions(node.argument.property))) { | ||
function diveIntoWrapperExpressions(node) { | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.UnaryExpression) { | ||
if (node.type === utils_1.AST_NODE_TYPES.UnaryExpression) { | ||
return diveIntoWrapperExpressions(node.argument); | ||
@@ -87,3 +90,3 @@ } | ||
function isNecessaryDynamicAccess(property) { | ||
if (property.type !== experimental_utils_1.AST_NODE_TYPES.Literal) { | ||
if (property.type !== utils_1.AST_NODE_TYPES.Literal) { | ||
return false; | ||
@@ -90,0 +93,0 @@ } |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -21,12 +25,12 @@ if (k2 === undefined) k2 = k; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const no_empty_function_1 = __importDefault(require("eslint/lib/rules/no-empty-function")); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
const schema = util.deepMerge(Array.isArray(no_empty_function_1.default.meta.schema) | ||
? no_empty_function_1.default.meta.schema[0] | ||
: no_empty_function_1.default.meta.schema, { | ||
const getESLintCoreRule_1 = require("../util/getESLintCoreRule"); | ||
const baseRule = (0, getESLintCoreRule_1.getESLintCoreRule)('no-empty-function'); | ||
const schema = util.deepMerge( | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument -- https://github.com/microsoft/TypeScript/issues/17002 | ||
Array.isArray(baseRule.meta.schema) | ||
? baseRule.meta.schema[0] | ||
: baseRule.meta.schema, { | ||
properties: { | ||
@@ -49,2 +53,3 @@ allow: { | ||
'decoratedFunctions', | ||
'overrideMethods', | ||
], | ||
@@ -61,8 +66,8 @@ }, | ||
description: 'Disallow empty functions', | ||
category: 'Best Practices', | ||
recommended: 'error', | ||
extendsBaseRule: true, | ||
}, | ||
hasSuggestions: baseRule.meta.hasSuggestions, | ||
schema: [schema], | ||
messages: no_empty_function_1.default.meta.messages, | ||
messages: baseRule.meta.messages, | ||
}, | ||
@@ -75,6 +80,7 @@ defaultOptions: [ | ||
create(context, [{ allow = [] }]) { | ||
const rules = no_empty_function_1.default.create(context); | ||
const rules = baseRule.create(context); | ||
const isAllowedProtectedConstructors = allow.includes('protected-constructors'); | ||
const isAllowedPrivateConstructors = allow.includes('private-constructors'); | ||
const isAllowedDecoratedFunctions = allow.includes('decoratedFunctions'); | ||
const isAllowedOverrideMethods = allow.includes('overrideMethods'); | ||
/** | ||
@@ -97,3 +103,3 @@ * Check if the method body is empty | ||
var _a; | ||
return (_a = node.params) === null || _a === void 0 ? void 0 : _a.some(param => param.type === experimental_utils_1.AST_NODE_TYPES.TSParameterProperty); | ||
return (_a = node.params) === null || _a === void 0 ? void 0 : _a.some(param => param.type === utils_1.AST_NODE_TYPES.TSParameterProperty); | ||
} | ||
@@ -108,3 +114,3 @@ /** | ||
if (isBodyEmpty(node) && | ||
(parent === null || parent === void 0 ? void 0 : parent.type) === experimental_utils_1.AST_NODE_TYPES.MethodDefinition && | ||
(parent === null || parent === void 0 ? void 0 : parent.type) === utils_1.AST_NODE_TYPES.MethodDefinition && | ||
parent.kind === 'constructor') { | ||
@@ -130,3 +136,3 @@ const { accessibility } = parent; | ||
if (isAllowedDecoratedFunctions && isBodyEmpty(node)) { | ||
const decorators = ((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === experimental_utils_1.AST_NODE_TYPES.MethodDefinition | ||
const decorators = ((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.MethodDefinition | ||
? node.parent.decorators | ||
@@ -138,5 +144,13 @@ : undefined; | ||
} | ||
function isAllowedEmptyOverrideMethod(node) { | ||
var _a; | ||
return (isAllowedOverrideMethods && | ||
isBodyEmpty(node) && | ||
((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.MethodDefinition && | ||
node.parent.override === true); | ||
} | ||
return Object.assign(Object.assign({}, rules), { FunctionExpression(node) { | ||
if (isAllowedEmptyConstructor(node) || | ||
isAllowedEmptyDecoratedFunctions(node)) { | ||
isAllowedEmptyDecoratedFunctions(node) || | ||
isAllowedEmptyOverrideMethod(node)) { | ||
return; | ||
@@ -143,0 +157,0 @@ } |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,2 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -30,7 +35,6 @@ exports.default = util.createRule({ | ||
description: 'Disallow the declaration of empty interfaces', | ||
category: 'Best Practices', | ||
recommended: 'error', | ||
suggestion: true, | ||
}, | ||
fixable: 'code', | ||
hasSuggestions: true, | ||
messages: { | ||
@@ -60,2 +64,3 @@ noEmpty: 'An empty interface is equivalent to `{}`.', | ||
TSInterfaceDeclaration(node) { | ||
var _a, _b; | ||
const sourceCode = context.getSourceCode(); | ||
@@ -84,20 +89,21 @@ const filename = context.getFilename(); | ||
}; | ||
// Check if interface is within ambient declaration | ||
let useAutoFix = true; | ||
if (util.isDefinitionFile(filename)) { | ||
const scope = context.getScope(); | ||
if (scope.type === 'tsModule' && scope.block.declare) { | ||
useAutoFix = false; | ||
} | ||
} | ||
const scope = context.getScope(); | ||
const mergedWithClassDeclaration = (_b = (_a = scope.set | ||
.get(node.id.name)) === null || _a === void 0 ? void 0 : _a.defs) === null || _b === void 0 ? void 0 : _b.some(def => def.node.type === utils_1.AST_NODE_TYPES.ClassDeclaration); | ||
const isInAmbientDeclaration = !!(util.isDefinitionFile(filename) && | ||
scope.type === 'tsModule' && | ||
scope.block.declare); | ||
const useAutoFix = !(isInAmbientDeclaration || mergedWithClassDeclaration); | ||
context.report(Object.assign({ node: node.id, messageId: 'noEmptyWithSuper' }, (useAutoFix | ||
? { fix } | ||
: { | ||
suggest: [ | ||
{ | ||
messageId: 'noEmptyWithSuper', | ||
fix, | ||
}, | ||
], | ||
}))); | ||
: !mergedWithClassDeclaration | ||
? { | ||
suggest: [ | ||
{ | ||
messageId: 'noEmptyWithSuper', | ||
fix, | ||
}, | ||
], | ||
} | ||
: null))); | ||
} | ||
@@ -104,0 +110,0 @@ } |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -30,8 +34,7 @@ exports.default = util.createRule({ | ||
docs: { | ||
description: 'Disallow usage of the `any` type', | ||
category: 'Best Practices', | ||
description: 'Disallow the `any` type', | ||
recommended: 'warn', | ||
suggestion: true, | ||
}, | ||
fixable: 'code', | ||
hasSuggestions: true, | ||
messages: { | ||
@@ -48,5 +51,7 @@ unexpectedAny: 'Unexpected any. Specify a different type.', | ||
fixToUnknown: { | ||
description: 'Whether to enable auto-fixing in which the `any` type is converted to the `unknown` type.', | ||
type: 'boolean', | ||
}, | ||
ignoreRestArgs: { | ||
description: 'Whether to ignore rest parameter arrays.', | ||
type: 'boolean', | ||
@@ -73,12 +78,12 @@ }, | ||
return [ | ||
experimental_utils_1.AST_NODE_TYPES.ArrowFunctionExpression, | ||
experimental_utils_1.AST_NODE_TYPES.FunctionDeclaration, | ||
experimental_utils_1.AST_NODE_TYPES.FunctionExpression, | ||
experimental_utils_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression, | ||
experimental_utils_1.AST_NODE_TYPES.TSFunctionType, | ||
experimental_utils_1.AST_NODE_TYPES.TSConstructorType, | ||
experimental_utils_1.AST_NODE_TYPES.TSCallSignatureDeclaration, | ||
experimental_utils_1.AST_NODE_TYPES.TSConstructSignatureDeclaration, | ||
experimental_utils_1.AST_NODE_TYPES.TSMethodSignature, | ||
experimental_utils_1.AST_NODE_TYPES.TSDeclareFunction, | ||
utils_1.AST_NODE_TYPES.ArrowFunctionExpression, | ||
utils_1.AST_NODE_TYPES.FunctionDeclaration, | ||
utils_1.AST_NODE_TYPES.FunctionExpression, | ||
utils_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression, | ||
utils_1.AST_NODE_TYPES.TSFunctionType, | ||
utils_1.AST_NODE_TYPES.TSConstructorType, | ||
utils_1.AST_NODE_TYPES.TSCallSignatureDeclaration, | ||
utils_1.AST_NODE_TYPES.TSConstructSignatureDeclaration, | ||
utils_1.AST_NODE_TYPES.TSMethodSignature, | ||
utils_1.AST_NODE_TYPES.TSDeclareFunction, // declare function _8(...args: any[]): unknown; | ||
].includes(node.type); | ||
@@ -93,4 +98,4 @@ } | ||
function isNodeRestElementInFunction(node) { | ||
return (node.type === experimental_utils_1.AST_NODE_TYPES.RestElement && | ||
typeof node.parent !== 'undefined' && | ||
return (node.type === utils_1.AST_NODE_TYPES.RestElement && | ||
node.parent !== undefined && | ||
isNodeValidFunction(node.parent)); | ||
@@ -105,3 +110,3 @@ } | ||
function isNodeReadonlyTSTypeOperator(node) { | ||
return (node.type === experimental_utils_1.AST_NODE_TYPES.TSTypeOperator && | ||
return (node.type === utils_1.AST_NODE_TYPES.TSTypeOperator && | ||
node.operator === 'readonly'); | ||
@@ -116,5 +121,5 @@ } | ||
function isNodeValidArrayTSTypeReference(node) { | ||
return (node.type === experimental_utils_1.AST_NODE_TYPES.TSTypeReference && | ||
typeof node.typeName !== 'undefined' && | ||
node.typeName.type === experimental_utils_1.AST_NODE_TYPES.Identifier && | ||
return (node.type === utils_1.AST_NODE_TYPES.TSTypeReference && | ||
node.typeName !== undefined && | ||
node.typeName.type === utils_1.AST_NODE_TYPES.Identifier && | ||
['Array', 'ReadonlyArray'].includes(node.typeName.name)); | ||
@@ -188,3 +193,3 @@ } | ||
if (fixToUnknown) { | ||
fixOrSuggest.fix = (fixer => fixer.replaceText(node, 'unknown')); | ||
fixOrSuggest.fix = (fixer) => fixer.replaceText(node, 'unknown'); | ||
} | ||
@@ -191,0 +196,0 @@ context.report(Object.assign({ node, messageId: 'unexpectedAny' }, fixOrSuggest)); |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -28,4 +32,3 @@ if (k2 === undefined) k2 = k; | ||
docs: { | ||
description: 'Disallow extra non-null assertion', | ||
category: 'Stylistic Issues', | ||
description: 'Disallow extra non-null assertions', | ||
recommended: 'error', | ||
@@ -32,0 +35,0 @@ }, |
"use strict"; | ||
// any is required to work around manipulating the AST in weird ways | ||
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment */ | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -23,9 +27,7 @@ if (k2 === undefined) k2 = k; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const no_extra_parens_1 = __importDefault(require("eslint/lib/rules/no-extra-parens")); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
const getESLintCoreRule_1 = require("../util/getESLintCoreRule"); | ||
const baseRule = (0, getESLintCoreRule_1.getESLintCoreRule)('no-extra-parens'); | ||
exports.default = util.createRule({ | ||
@@ -37,3 +39,2 @@ name: 'no-extra-parens', | ||
description: 'Disallow unnecessary parentheses', | ||
category: 'Possible Errors', | ||
recommended: false, | ||
@@ -43,8 +44,9 @@ extendsBaseRule: true, | ||
fixable: 'code', | ||
schema: no_extra_parens_1.default.meta.schema, | ||
messages: no_extra_parens_1.default.meta.messages, | ||
hasSuggestions: baseRule.meta.hasSuggestions, | ||
schema: baseRule.meta.schema, | ||
messages: baseRule.meta.messages, | ||
}, | ||
defaultOptions: ['all'], | ||
create(context) { | ||
const rules = no_extra_parens_1.default.create(context); | ||
const rules = baseRule.create(context); | ||
function binaryExp(node) { | ||
@@ -59,6 +61,6 @@ const rule = rules.BinaryExpression; | ||
if (isLeftTypeAssertion) { | ||
return rule(Object.assign(Object.assign({}, node), { left: Object.assign(Object.assign({}, node.left), { type: experimental_utils_1.AST_NODE_TYPES.SequenceExpression }) })); | ||
return rule(Object.assign(Object.assign({}, node), { left: Object.assign(Object.assign({}, node.left), { type: utils_1.AST_NODE_TYPES.SequenceExpression }) })); | ||
} | ||
if (isRightTypeAssertion) { | ||
return rule(Object.assign(Object.assign({}, node), { right: Object.assign(Object.assign({}, node.right), { type: experimental_utils_1.AST_NODE_TYPES.SequenceExpression }) })); | ||
return rule(Object.assign(Object.assign({}, node), { right: Object.assign(Object.assign({}, node.right), { type: utils_1.AST_NODE_TYPES.SequenceExpression }) })); | ||
} | ||
@@ -72,8 +74,9 @@ return rule(node); | ||
// reduces the precedence of the node so the rule thinks it needs to be wrapped | ||
return rule(Object.assign(Object.assign({}, node), { callee: Object.assign(Object.assign({}, node.callee), { type: experimental_utils_1.AST_NODE_TYPES.SequenceExpression }) })); | ||
return rule(Object.assign(Object.assign({}, node), { callee: Object.assign(Object.assign({}, node.callee), { type: utils_1.AST_NODE_TYPES.SequenceExpression }) })); | ||
} | ||
if (node.arguments.length === 1 && ((_a = node.typeParameters) === null || _a === void 0 ? void 0 : _a.params.some(param => param.type === experimental_utils_1.AST_NODE_TYPES.TSParenthesizedType || | ||
param.type === experimental_utils_1.AST_NODE_TYPES.TSImportType))) { | ||
if (node.arguments.length === 1 && | ||
((_a = node.typeParameters) === null || _a === void 0 ? void 0 : _a.params.some(param => param.type === utils_1.AST_NODE_TYPES.TSImportType || | ||
param.type === utils_1.AST_NODE_TYPES.TSArrayType))) { | ||
return rule(Object.assign(Object.assign({}, node), { arguments: [ | ||
Object.assign(Object.assign({}, node.arguments[0]), { type: experimental_utils_1.AST_NODE_TYPES.SequenceExpression }), | ||
Object.assign(Object.assign({}, node.arguments[0]), { type: utils_1.AST_NODE_TYPES.SequenceExpression }), | ||
] })); | ||
@@ -87,3 +90,3 @@ } | ||
// reduces the precedence of the node so the rule thinks it needs to be wrapped | ||
return rule(Object.assign(Object.assign({}, node), { argument: Object.assign(Object.assign({}, node.argument), { type: experimental_utils_1.AST_NODE_TYPES.SequenceExpression }) })); | ||
return rule(Object.assign(Object.assign({}, node), { argument: Object.assign(Object.assign({}, node.argument), { type: utils_1.AST_NODE_TYPES.SequenceExpression }) })); | ||
} | ||
@@ -100,18 +103,36 @@ return rule(node); | ||
// AssignmentExpression | ||
// AwaitExpression | ||
AwaitExpression(node) { | ||
if (util.isTypeAssertion(node.argument)) { | ||
// reduces the precedence of the node so the rule thinks it needs to be wrapped | ||
return rules.AwaitExpression(Object.assign(Object.assign({}, node), { argument: Object.assign(Object.assign({}, node.argument), { type: utils_1.AST_NODE_TYPES.SequenceExpression }) })); | ||
} | ||
return rules.AwaitExpression(node); | ||
}, | ||
BinaryExpression: binaryExp, | ||
CallExpression: callExp, | ||
// ClassDeclaration | ||
// ClassExpression | ||
ClassDeclaration(node) { | ||
var _a; | ||
if (((_a = node.superClass) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.TSAsExpression) { | ||
return rules.ClassDeclaration(Object.assign(Object.assign({}, node), { superClass: Object.assign(Object.assign({}, node.superClass), { type: utils_1.AST_NODE_TYPES.SequenceExpression }) })); | ||
} | ||
return rules.ClassDeclaration(node); | ||
}, | ||
ClassExpression(node) { | ||
var _a; | ||
if (((_a = node.superClass) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.TSAsExpression) { | ||
return rules.ClassExpression(Object.assign(Object.assign({}, node), { superClass: Object.assign(Object.assign({}, node.superClass), { type: utils_1.AST_NODE_TYPES.SequenceExpression }) })); | ||
} | ||
return rules.ClassExpression(node); | ||
}, | ||
ConditionalExpression(node) { | ||
// reduces the precedence of the node so the rule thinks it needs to be wrapped | ||
if (util.isTypeAssertion(node.test)) { | ||
return rules.ConditionalExpression(Object.assign(Object.assign({}, node), { test: Object.assign(Object.assign({}, node.test), { type: experimental_utils_1.AST_NODE_TYPES.SequenceExpression }) })); | ||
return rules.ConditionalExpression(Object.assign(Object.assign({}, node), { test: Object.assign(Object.assign({}, node.test), { type: utils_1.AST_NODE_TYPES.SequenceExpression }) })); | ||
} | ||
if (util.isTypeAssertion(node.consequent)) { | ||
return rules.ConditionalExpression(Object.assign(Object.assign({}, node), { consequent: Object.assign(Object.assign({}, node.consequent), { type: experimental_utils_1.AST_NODE_TYPES.SequenceExpression }) })); | ||
return rules.ConditionalExpression(Object.assign(Object.assign({}, node), { consequent: Object.assign(Object.assign({}, node.consequent), { type: utils_1.AST_NODE_TYPES.SequenceExpression }) })); | ||
} | ||
if (util.isTypeAssertion(node.alternate)) { | ||
// reduces the precedence of the node so the rule thinks it needs to be rapped | ||
return rules.ConditionalExpression(Object.assign(Object.assign({}, node), { alternate: Object.assign(Object.assign({}, node.alternate), { type: experimental_utils_1.AST_NODE_TYPES.SequenceExpression }) })); | ||
// reduces the precedence of the node so the rule thinks it needs to be wrapped | ||
return rules.ConditionalExpression(Object.assign(Object.assign({}, node), { alternate: Object.assign(Object.assign({}, node.alternate), { type: utils_1.AST_NODE_TYPES.SequenceExpression }) })); | ||
} | ||
@@ -145,3 +166,3 @@ return rules.ConditionalExpression(node); | ||
// reduces the precedence of the node so the rule thinks it needs to be wrapped | ||
return rules.MemberExpression(Object.assign(Object.assign({}, node), { object: Object.assign(Object.assign({}, node.object), { type: experimental_utils_1.AST_NODE_TYPES.SequenceExpression }) })); | ||
return rules.MemberExpression(Object.assign(Object.assign({}, node), { object: Object.assign(Object.assign({}, node.object), { type: utils_1.AST_NODE_TYPES.SequenceExpression }) })); | ||
} | ||
@@ -193,3 +214,3 @@ return rules.MemberExpression(node); | ||
// makes the rule skip checking of the right | ||
return rules.ForOfStatement(Object.assign(Object.assign({}, node), { type: experimental_utils_1.AST_NODE_TYPES.ForOfStatement, right: Object.assign(Object.assign({}, node.right), { type: experimental_utils_1.AST_NODE_TYPES.SequenceExpression }) })); | ||
return rules.ForOfStatement(Object.assign(Object.assign({}, node), { type: utils_1.AST_NODE_TYPES.ForOfStatement, right: Object.assign(Object.assign({}, node.right), { type: utils_1.AST_NODE_TYPES.SequenceExpression }) })); | ||
} | ||
@@ -203,3 +224,3 @@ return rules.ForOfStatement(node); | ||
// makes the rule skip checking of the right | ||
return rules['ForInStatement, ForOfStatement'](Object.assign(Object.assign({}, node), { type: experimental_utils_1.AST_NODE_TYPES.ForOfStatement, right: Object.assign(Object.assign({}, node.right), { type: experimental_utils_1.AST_NODE_TYPES.SequenceExpression }) })); | ||
return rules['ForInStatement, ForOfStatement'](Object.assign(Object.assign({}, node), { type: utils_1.AST_NODE_TYPES.ForOfStatement, right: Object.assign(Object.assign({}, node.right), { type: utils_1.AST_NODE_TYPES.SequenceExpression }) })); | ||
} | ||
@@ -206,0 +227,0 @@ return rules['ForInStatement, ForOfStatement'](node); |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -21,8 +25,6 @@ if (k2 === undefined) k2 = k; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const no_extra_semi_1 = __importDefault(require("eslint/lib/rules/no-extra-semi")); | ||
const util = __importStar(require("../util")); | ||
const getESLintCoreRule_1 = require("../util/getESLintCoreRule"); | ||
const baseRule = (0, getESLintCoreRule_1.getESLintCoreRule)('no-extra-semi'); | ||
exports.default = util.createRule({ | ||
@@ -34,3 +36,2 @@ name: 'no-extra-semi', | ||
description: 'Disallow unnecessary semicolons', | ||
category: 'Possible Errors', | ||
recommended: 'error', | ||
@@ -40,10 +41,23 @@ extendsBaseRule: true, | ||
fixable: 'code', | ||
schema: no_extra_semi_1.default.meta.schema, | ||
messages: no_extra_semi_1.default.meta.messages, | ||
hasSuggestions: baseRule.meta.hasSuggestions, | ||
schema: baseRule.meta.schema, | ||
messages: baseRule.meta.messages, | ||
}, | ||
defaultOptions: [], | ||
create(context) { | ||
const rules = no_extra_semi_1.default.create(context); | ||
return Object.assign(Object.assign({}, rules), { ClassProperty(node) { | ||
rules.MethodDefinition(node); | ||
const rules = baseRule.create(context); | ||
return Object.assign(Object.assign({}, rules), { 'TSAbstractMethodDefinition, TSAbstractPropertyDefinition'(node) { | ||
var _a; | ||
if (rules.MethodDefinition) { | ||
// for ESLint <= v7 | ||
rules.MethodDefinition(node); | ||
} | ||
else if (rules['MethodDefinition, PropertyDefinition']) { | ||
// for ESLint >= v8 < v8.3.0 | ||
rules['MethodDefinition, PropertyDefinition'](node); | ||
} | ||
else { | ||
// for ESLint >= v8.3.0 | ||
(_a = rules['MethodDefinition, PropertyDefinition, StaticBlock']) === null || _a === void 0 ? void 0 : _a.call(rules, node); | ||
} | ||
} }); | ||
@@ -50,0 +64,0 @@ }, |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -30,5 +34,4 @@ exports.default = util.createRule({ | ||
docs: { | ||
description: 'Forbids the use of classes as namespaces', | ||
category: 'Best Practices', | ||
recommended: false, | ||
description: 'Disallow classes used as namespaces', | ||
recommended: 'strict', | ||
}, | ||
@@ -41,11 +44,15 @@ schema: [ | ||
allowConstructorOnly: { | ||
description: 'Whether to allow extraneous classes that contain only a constructor.', | ||
type: 'boolean', | ||
}, | ||
allowEmpty: { | ||
description: 'Whether to allow extraneous classes that have no body (i.e. are empty).', | ||
type: 'boolean', | ||
}, | ||
allowStaticOnly: { | ||
description: 'Whether to allow extraneous classes that only contain static members.', | ||
type: 'boolean', | ||
}, | ||
allowWithDecorator: { | ||
description: 'Whether to allow extraneous classes that include a decorator.', | ||
type: 'boolean', | ||
@@ -80,3 +87,3 @@ }, | ||
const parent = node.parent; | ||
if (!parent || parent.superClass) { | ||
if (!parent || parent.superClass || isAllowWithDecorator(parent)) { | ||
return; | ||
@@ -86,3 +93,3 @@ } | ||
if (node.body.length === 0) { | ||
if (allowEmpty || isAllowWithDecorator(parent)) { | ||
if (allowEmpty) { | ||
return; | ||
@@ -100,3 +107,3 @@ } | ||
if ('kind' in prop && prop.kind === 'constructor') { | ||
if (prop.value.params.some(param => param.type === experimental_utils_1.AST_NODE_TYPES.TSParameterProperty)) { | ||
if (prop.value.params.some(param => param.type === utils_1.AST_NODE_TYPES.TSParameterProperty)) { | ||
onlyConstructor = false; | ||
@@ -103,0 +110,0 @@ onlyStatic = false; |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,6 +26,7 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const tsutils = __importStar(require("tsutils")); | ||
const ts = __importStar(require("typescript")); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const util = __importStar(require("../util")); | ||
const util_1 = require("../util"); | ||
exports.default = util.createRule({ | ||
@@ -31,12 +36,12 @@ name: 'no-floating-promises', | ||
docs: { | ||
description: 'Requires Promise-like values to be handled appropriately', | ||
category: 'Best Practices', | ||
description: 'Require Promise-like statements to be handled appropriately', | ||
recommended: 'error', | ||
suggestion: true, | ||
requiresTypeChecking: true, | ||
}, | ||
hasSuggestions: true, | ||
messages: { | ||
floating: 'Promises must be handled appropriately.', | ||
floatingVoid: 'Promises must be handled appropriately' + | ||
' or explicitly marked as ignored with the `void` operator.', | ||
floating: 'Promises must be awaited, end with a call to .catch, or end with a call to .then with a rejection handler.', | ||
floatingFixAwait: 'Add await operator.', | ||
floatingVoid: 'Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler' + | ||
' or be explicitly marked as ignored with the `void` operator.', | ||
floatingFixVoid: 'Add void operator to ignore.', | ||
@@ -48,4 +53,10 @@ }, | ||
properties: { | ||
ignoreVoid: { type: 'boolean' }, | ||
ignoreIIFE: { type: 'boolean' }, | ||
ignoreVoid: { | ||
description: 'Whether to ignore `void` expressions.', | ||
type: 'boolean', | ||
}, | ||
ignoreIIFE: { | ||
description: 'Whether to ignore async IIFEs (Immediately Invocated Function Expressions).', | ||
type: 'boolean', | ||
}, | ||
}, | ||
@@ -66,9 +77,11 @@ additionalProperties: false, | ||
const checker = parserServices.program.getTypeChecker(); | ||
const sourceCode = context.getSourceCode(); | ||
return { | ||
ExpressionStatement(node) { | ||
const { expression } = parserServices.esTreeNodeToTSNodeMap.get(node); | ||
if (options.ignoreIIFE && isAsyncIife(node)) { | ||
return; | ||
} | ||
let expression = node.expression; | ||
if (expression.type === utils_1.AST_NODE_TYPES.ChainExpression) { | ||
expression = expression.expression; | ||
} | ||
if (isUnhandledPromise(checker, expression)) { | ||
@@ -83,5 +96,12 @@ if (options.ignoreVoid) { | ||
fix(fixer) { | ||
let code = sourceCode.getText(node); | ||
code = `void ${code}`; | ||
return fixer.replaceText(node, code); | ||
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node.expression); | ||
if (isHigherPrecedenceThanUnary(tsNode)) { | ||
return fixer.insertTextBefore(node, 'void '); | ||
} | ||
else { | ||
return [ | ||
fixer.insertTextBefore(node, 'void ('), | ||
fixer.insertTextAfterRange([expression.range[1], expression.range[1]], ')'), | ||
]; | ||
} | ||
}, | ||
@@ -96,2 +116,23 @@ }, | ||
messageId: 'floating', | ||
suggest: [ | ||
{ | ||
messageId: 'floatingFixAwait', | ||
fix(fixer) { | ||
if (expression.type === utils_1.AST_NODE_TYPES.UnaryExpression && | ||
expression.operator === 'void') { | ||
return fixer.replaceTextRange([expression.range[0], expression.range[0] + 4], 'await'); | ||
} | ||
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node.expression); | ||
if (isHigherPrecedenceThanUnary(tsNode)) { | ||
return fixer.insertTextBefore(node, 'await '); | ||
} | ||
else { | ||
return [ | ||
fixer.insertTextBefore(node, 'await ('), | ||
fixer.insertTextAfterRange([expression.range[1], expression.range[1]], ')'), | ||
]; | ||
} | ||
}, | ||
}, | ||
], | ||
}); | ||
@@ -102,31 +143,38 @@ } | ||
}; | ||
function isHigherPrecedenceThanUnary(node) { | ||
const operator = tsutils.isBinaryExpression(node) | ||
? node.operatorToken.kind | ||
: ts.SyntaxKind.Unknown; | ||
const nodePrecedence = util.getOperatorPrecedence(node.kind, operator); | ||
return nodePrecedence > util_1.OperatorPrecedence.Unary; | ||
} | ||
function isAsyncIife(node) { | ||
if (node.expression.type !== experimental_utils_1.AST_NODE_TYPES.CallExpression) { | ||
if (node.expression.type !== utils_1.AST_NODE_TYPES.CallExpression) { | ||
return false; | ||
} | ||
return (node.expression.type === experimental_utils_1.AST_NODE_TYPES.CallExpression && | ||
return (node.expression.type === utils_1.AST_NODE_TYPES.CallExpression && | ||
(node.expression.callee.type === | ||
experimental_utils_1.AST_NODE_TYPES.ArrowFunctionExpression || | ||
node.expression.callee.type === experimental_utils_1.AST_NODE_TYPES.FunctionExpression)); | ||
utils_1.AST_NODE_TYPES.ArrowFunctionExpression || | ||
node.expression.callee.type === utils_1.AST_NODE_TYPES.FunctionExpression)); | ||
} | ||
function isUnhandledPromise(checker, node) { | ||
// First, check expressions whose resulting types may not be promise-like | ||
if (ts.isBinaryExpression(node) && | ||
node.operatorToken.kind === ts.SyntaxKind.CommaToken) { | ||
if (node.type === utils_1.AST_NODE_TYPES.SequenceExpression) { | ||
// Any child in a comma expression could return a potentially unhandled | ||
// promise, so we check them all regardless of whether the final returned | ||
// value is promise-like. | ||
return (isUnhandledPromise(checker, node.left) || | ||
isUnhandledPromise(checker, node.right)); | ||
return node.expressions.some(item => isUnhandledPromise(checker, item)); | ||
} | ||
if (ts.isVoidExpression(node) && !options.ignoreVoid) { | ||
if (!options.ignoreVoid && | ||
node.type === utils_1.AST_NODE_TYPES.UnaryExpression && | ||
node.operator === 'void') { | ||
// Similarly, a `void` expression always returns undefined, so we need to | ||
// see what's inside it without checking the type of the overall expression. | ||
return isUnhandledPromise(checker, node.expression); | ||
return isUnhandledPromise(checker, node.argument); | ||
} | ||
// Check the type. At this point it can't be unhandled if it isn't a promise | ||
if (!isPromiseLike(checker, node)) { | ||
if (!isPromiseLike(checker, parserServices.esTreeNodeToTSNodeMap.get(node))) { | ||
return false; | ||
} | ||
if (ts.isCallExpression(node)) { | ||
if (node.type === utils_1.AST_NODE_TYPES.CallExpression) { | ||
// If the outer expression is a call, it must be either a `.then()` or | ||
@@ -138,11 +186,11 @@ // `.catch()` that handles the promise. | ||
} | ||
else if (ts.isConditionalExpression(node)) { | ||
else if (node.type === utils_1.AST_NODE_TYPES.ConditionalExpression) { | ||
// We must be getting the promise-like value from one of the branches of the | ||
// ternary. Check them directly. | ||
return (isUnhandledPromise(checker, node.whenFalse) || | ||
isUnhandledPromise(checker, node.whenTrue)); | ||
return (isUnhandledPromise(checker, node.alternate) || | ||
isUnhandledPromise(checker, node.consequent)); | ||
} | ||
else if (ts.isPropertyAccessExpression(node) || | ||
ts.isIdentifier(node) || | ||
ts.isNewExpression(node)) { | ||
else if (node.type === utils_1.AST_NODE_TYPES.MemberExpression || | ||
node.type === utils_1.AST_NODE_TYPES.Identifier || | ||
node.type === utils_1.AST_NODE_TYPES.NewExpression) { | ||
// If it is just a property access chain or a `new` call (e.g. `foo.bar` or | ||
@@ -153,2 +201,6 @@ // `new Promise()`), the promise is not handled because it doesn't have the | ||
} | ||
else if (node.type === utils_1.AST_NODE_TYPES.LogicalExpression) { | ||
return (isUnhandledPromise(checker, node.left) || | ||
isUnhandledPromise(checker, node.right)); | ||
} | ||
// We conservatively return false for all other types of expressions because | ||
@@ -199,16 +251,19 @@ // we don't want to accidentally fail if the promise is handled internally but | ||
function isPromiseCatchCallWithHandler(expression) { | ||
return (tsutils.isPropertyAccessExpression(expression.expression) && | ||
expression.expression.name.text === 'catch' && | ||
return (expression.callee.type === utils_1.AST_NODE_TYPES.MemberExpression && | ||
expression.callee.property.type === utils_1.AST_NODE_TYPES.Identifier && | ||
expression.callee.property.name === 'catch' && | ||
expression.arguments.length >= 1); | ||
} | ||
function isPromiseThenCallWithRejectionHandler(expression) { | ||
return (tsutils.isPropertyAccessExpression(expression.expression) && | ||
expression.expression.name.text === 'then' && | ||
return (expression.callee.type === utils_1.AST_NODE_TYPES.MemberExpression && | ||
expression.callee.property.type === utils_1.AST_NODE_TYPES.Identifier && | ||
expression.callee.property.name === 'then' && | ||
expression.arguments.length >= 2); | ||
} | ||
function isPromiseFinallyCallWithHandler(expression) { | ||
return (tsutils.isPropertyAccessExpression(expression.expression) && | ||
expression.expression.name.text === 'finally' && | ||
return (expression.callee.type === utils_1.AST_NODE_TYPES.MemberExpression && | ||
expression.callee.property.type === utils_1.AST_NODE_TYPES.Identifier && | ||
expression.callee.property.name === 'finally' && | ||
expression.arguments.length >= 1); | ||
} | ||
//# sourceMappingURL=no-floating-promises.js.map |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -29,3 +33,2 @@ if (k2 === undefined) k2 = k; | ||
description: 'Disallow iterating over an array with a for-in loop', | ||
category: 'Best Practices', | ||
recommended: 'error', | ||
@@ -32,0 +35,0 @@ requiresTypeChecking: true, |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,18 +26,18 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
exports.default = util.createRule({ | ||
name: 'no-implicit-any-catch', | ||
meta: { | ||
deprecated: true, | ||
type: 'suggestion', | ||
docs: { | ||
description: 'Disallow usage of the implicit `any` type in catch clauses', | ||
category: 'Best Practices', | ||
recommended: false, | ||
suggestion: true, | ||
}, | ||
fixable: 'code', | ||
hasSuggestions: true, | ||
messages: { | ||
implicitAnyInCatch: 'Implicit any in catch clause', | ||
explicitAnyInCatch: 'Explicit any in catch clause', | ||
implicitAnyInCatch: 'Implicit any in catch clause.', | ||
explicitAnyInCatch: 'Explicit any in catch clause.', | ||
suggestExplicitUnknown: 'Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct.', | ||
@@ -47,2 +51,3 @@ }, | ||
allowExplicitAny: { | ||
description: 'Whether to disallow specifying `: any` as the error type as well. See also `no-explicit-any`.', | ||
type: 'boolean', | ||
@@ -81,3 +86,3 @@ }, | ||
node.param.typeAnnotation.typeAnnotation.type === | ||
experimental_utils_1.AST_NODE_TYPES.TSAnyKeyword) { | ||
utils_1.AST_NODE_TYPES.TSAnyKeyword) { | ||
context.report({ | ||
@@ -84,0 +89,0 @@ node, |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,5 +26,5 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const tsutils = __importStar(require("tsutils")); | ||
const ts = __importStar(require("typescript")); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const tsutils = __importStar(require("tsutils")); | ||
const util = __importStar(require("../util")); | ||
@@ -40,3 +44,2 @@ const FUNCTION_CONSTRUCTOR = 'Function'; | ||
description: 'Disallow the use of `eval()`-like methods', | ||
category: 'Best Practices', | ||
recommended: 'error', | ||
@@ -59,12 +62,12 @@ extendsBaseRule: true, | ||
function getCalleeName(node) { | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.Identifier) { | ||
if (node.type === utils_1.AST_NODE_TYPES.Identifier) { | ||
return node.name; | ||
} | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.MemberExpression && | ||
node.object.type === experimental_utils_1.AST_NODE_TYPES.Identifier && | ||
if (node.type === utils_1.AST_NODE_TYPES.MemberExpression && | ||
node.object.type === utils_1.AST_NODE_TYPES.Identifier && | ||
GLOBAL_CANDIDATES.has(node.object.name)) { | ||
if (node.property.type === experimental_utils_1.AST_NODE_TYPES.Identifier) { | ||
if (node.property.type === utils_1.AST_NODE_TYPES.Identifier) { | ||
return node.property.name; | ||
} | ||
if (node.property.type === experimental_utils_1.AST_NODE_TYPES.Literal && | ||
if (node.property.type === utils_1.AST_NODE_TYPES.Literal && | ||
typeof node.property.value === 'string') { | ||
@@ -97,28 +100,38 @@ return node.property.value; | ||
} | ||
function isBind(node) { | ||
return node.type === utils_1.AST_NODE_TYPES.MemberExpression | ||
? isBind(node.property) | ||
: node.type === utils_1.AST_NODE_TYPES.Identifier && node.name === 'bind'; | ||
} | ||
function isFunction(node) { | ||
switch (node.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.ArrowFunctionExpression: | ||
case experimental_utils_1.AST_NODE_TYPES.FunctionDeclaration: | ||
case experimental_utils_1.AST_NODE_TYPES.FunctionExpression: | ||
case utils_1.AST_NODE_TYPES.ArrowFunctionExpression: | ||
case utils_1.AST_NODE_TYPES.FunctionDeclaration: | ||
case utils_1.AST_NODE_TYPES.FunctionExpression: | ||
return true; | ||
case experimental_utils_1.AST_NODE_TYPES.MemberExpression: | ||
case experimental_utils_1.AST_NODE_TYPES.Identifier: | ||
case utils_1.AST_NODE_TYPES.Literal: | ||
case utils_1.AST_NODE_TYPES.TemplateLiteral: | ||
return false; | ||
case utils_1.AST_NODE_TYPES.CallExpression: | ||
return isBind(node.callee) || isFunctionType(node); | ||
default: | ||
return isFunctionType(node); | ||
case experimental_utils_1.AST_NODE_TYPES.CallExpression: | ||
return ((node.callee.type === experimental_utils_1.AST_NODE_TYPES.Identifier && | ||
node.callee.name === 'bind') || | ||
isFunctionType(node)); | ||
default: | ||
return false; | ||
} | ||
} | ||
function isReferenceToGlobalFunction(calleeName) { | ||
const ref = context | ||
.getScope() | ||
.references.find(ref => ref.identifier.name === calleeName); | ||
// ensure it's the "global" version | ||
return !(ref === null || ref === void 0 ? void 0 : ref.resolved) || ref.resolved.defs.length === 0; | ||
} | ||
function checkImpliedEval(node) { | ||
var _a; | ||
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node.callee); | ||
const type = checker.getTypeAtLocation(tsNode); | ||
const calleeName = getCalleeName(node.callee); | ||
if (calleeName === null) { | ||
if (calleeName == null) { | ||
return; | ||
} | ||
if (calleeName === FUNCTION_CONSTRUCTOR) { | ||
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node.callee); | ||
const type = checker.getTypeAtLocation(tsNode); | ||
const symbol = type.getSymbol(); | ||
@@ -144,3 +157,5 @@ if (symbol) { | ||
const [handler] = node.arguments; | ||
if (EVAL_LIKE_METHODS.has(calleeName) && !isFunction(handler)) { | ||
if (EVAL_LIKE_METHODS.has(calleeName) && | ||
!isFunction(handler) && | ||
isReferenceToGlobalFunction(calleeName)) { | ||
context.report({ node: handler, messageId: 'noImpliedEvalError' }); | ||
@@ -147,0 +162,0 @@ } |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -30,4 +34,3 @@ exports.default = util.createRule({ | ||
docs: { | ||
description: 'Disallows explicit type declarations for variables or parameters initialized to a number, string, or boolean', | ||
category: 'Best Practices', | ||
description: 'Disallow explicit type declarations for variables or parameters initialized to a number, string, or boolean', | ||
recommended: 'error', | ||
@@ -61,28 +64,29 @@ }, | ||
create(context, [{ ignoreParameters, ignoreProperties }]) { | ||
const sourceCode = context.getSourceCode(); | ||
function isFunctionCall(init, callName) { | ||
if (init.type === experimental_utils_1.AST_NODE_TYPES.ChainExpression) { | ||
if (init.type === utils_1.AST_NODE_TYPES.ChainExpression) { | ||
return isFunctionCall(init.expression, callName); | ||
} | ||
return (init.type === experimental_utils_1.AST_NODE_TYPES.CallExpression && | ||
init.callee.type === experimental_utils_1.AST_NODE_TYPES.Identifier && | ||
return (init.type === utils_1.AST_NODE_TYPES.CallExpression && | ||
init.callee.type === utils_1.AST_NODE_TYPES.Identifier && | ||
init.callee.name === callName); | ||
} | ||
function isLiteral(init, typeName) { | ||
return (init.type === experimental_utils_1.AST_NODE_TYPES.Literal && typeof init.value === typeName); | ||
return (init.type === utils_1.AST_NODE_TYPES.Literal && typeof init.value === typeName); | ||
} | ||
function isIdentifier(init, ...names) { | ||
return (init.type === experimental_utils_1.AST_NODE_TYPES.Identifier && names.includes(init.name)); | ||
return (init.type === utils_1.AST_NODE_TYPES.Identifier && names.includes(init.name)); | ||
} | ||
function hasUnaryPrefix(init, ...operators) { | ||
return (init.type === experimental_utils_1.AST_NODE_TYPES.UnaryExpression && | ||
return (init.type === utils_1.AST_NODE_TYPES.UnaryExpression && | ||
operators.includes(init.operator)); | ||
} | ||
const keywordMap = { | ||
[experimental_utils_1.AST_NODE_TYPES.TSBigIntKeyword]: 'bigint', | ||
[experimental_utils_1.AST_NODE_TYPES.TSBooleanKeyword]: 'boolean', | ||
[experimental_utils_1.AST_NODE_TYPES.TSNumberKeyword]: 'number', | ||
[experimental_utils_1.AST_NODE_TYPES.TSNullKeyword]: 'null', | ||
[experimental_utils_1.AST_NODE_TYPES.TSStringKeyword]: 'string', | ||
[experimental_utils_1.AST_NODE_TYPES.TSSymbolKeyword]: 'symbol', | ||
[experimental_utils_1.AST_NODE_TYPES.TSUndefinedKeyword]: 'undefined', | ||
[utils_1.AST_NODE_TYPES.TSBigIntKeyword]: 'bigint', | ||
[utils_1.AST_NODE_TYPES.TSBooleanKeyword]: 'boolean', | ||
[utils_1.AST_NODE_TYPES.TSNumberKeyword]: 'number', | ||
[utils_1.AST_NODE_TYPES.TSNullKeyword]: 'null', | ||
[utils_1.AST_NODE_TYPES.TSStringKeyword]: 'string', | ||
[utils_1.AST_NODE_TYPES.TSSymbolKeyword]: 'symbol', | ||
[utils_1.AST_NODE_TYPES.TSUndefinedKeyword]: 'undefined', | ||
}; | ||
@@ -94,3 +98,3 @@ /** | ||
switch (annotation.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.TSBigIntKeyword: { | ||
case utils_1.AST_NODE_TYPES.TSBigIntKeyword: { | ||
// note that bigint cannot have + prefixed to it | ||
@@ -101,11 +105,10 @@ const unwrappedInit = hasUnaryPrefix(init, '-') | ||
return (isFunctionCall(unwrappedInit, 'BigInt') || | ||
(unwrappedInit.type === experimental_utils_1.AST_NODE_TYPES.Literal && | ||
(unwrappedInit.type === utils_1.AST_NODE_TYPES.Literal && | ||
'bigint' in unwrappedInit)); | ||
} | ||
case experimental_utils_1.AST_NODE_TYPES.TSBooleanKeyword: | ||
case utils_1.AST_NODE_TYPES.TSBooleanKeyword: | ||
return (hasUnaryPrefix(init, '!') || | ||
// eslint-disable-next-line @typescript-eslint/internal/prefer-ast-types-enum | ||
isFunctionCall(init, 'Boolean') || | ||
isLiteral(init, 'boolean')); | ||
case experimental_utils_1.AST_NODE_TYPES.TSNumberKeyword: { | ||
case utils_1.AST_NODE_TYPES.TSNumberKeyword: { | ||
const unwrappedInit = hasUnaryPrefix(init, '+', '-') | ||
@@ -118,19 +121,17 @@ ? init.argument | ||
} | ||
case experimental_utils_1.AST_NODE_TYPES.TSNullKeyword: | ||
return init.type === experimental_utils_1.AST_NODE_TYPES.Literal && init.value === null; | ||
case experimental_utils_1.AST_NODE_TYPES.TSStringKeyword: | ||
return ( | ||
// eslint-disable-next-line @typescript-eslint/internal/prefer-ast-types-enum | ||
isFunctionCall(init, 'String') || | ||
case utils_1.AST_NODE_TYPES.TSNullKeyword: | ||
return init.type === utils_1.AST_NODE_TYPES.Literal && init.value == null; | ||
case utils_1.AST_NODE_TYPES.TSStringKeyword: | ||
return (isFunctionCall(init, 'String') || | ||
isLiteral(init, 'string') || | ||
init.type === experimental_utils_1.AST_NODE_TYPES.TemplateLiteral); | ||
case experimental_utils_1.AST_NODE_TYPES.TSSymbolKeyword: | ||
init.type === utils_1.AST_NODE_TYPES.TemplateLiteral); | ||
case utils_1.AST_NODE_TYPES.TSSymbolKeyword: | ||
return isFunctionCall(init, 'Symbol'); | ||
case experimental_utils_1.AST_NODE_TYPES.TSTypeReference: { | ||
if (annotation.typeName.type === experimental_utils_1.AST_NODE_TYPES.Identifier && | ||
case utils_1.AST_NODE_TYPES.TSTypeReference: { | ||
if (annotation.typeName.type === utils_1.AST_NODE_TYPES.Identifier && | ||
annotation.typeName.name === 'RegExp') { | ||
const isRegExpLiteral = init.type === experimental_utils_1.AST_NODE_TYPES.Literal && | ||
const isRegExpLiteral = init.type === utils_1.AST_NODE_TYPES.Literal && | ||
init.value instanceof RegExp; | ||
const isRegExpNewCall = init.type === experimental_utils_1.AST_NODE_TYPES.NewExpression && | ||
init.callee.type === experimental_utils_1.AST_NODE_TYPES.Identifier && | ||
const isRegExpNewCall = init.type === utils_1.AST_NODE_TYPES.NewExpression && | ||
init.callee.type === utils_1.AST_NODE_TYPES.Identifier && | ||
init.callee.name === 'RegExp'; | ||
@@ -142,3 +143,3 @@ const isRegExpCall = isFunctionCall(init, 'RegExp'); | ||
} | ||
case experimental_utils_1.AST_NODE_TYPES.TSUndefinedKeyword: | ||
case utils_1.AST_NODE_TYPES.TSUndefinedKeyword: | ||
return (hasUnaryPrefix(init, 'void') || isIdentifier(init, 'undefined')); | ||
@@ -158,3 +159,3 @@ } | ||
} | ||
const type = typeNode.typeAnnotation.type === experimental_utils_1.AST_NODE_TYPES.TSTypeReference | ||
const type = typeNode.typeAnnotation.type === utils_1.AST_NODE_TYPES.TSTypeReference | ||
? // TODO - if we add more references | ||
@@ -169,3 +170,10 @@ 'RegExp' | ||
}, | ||
fix: fixer => fixer.remove(typeNode), | ||
*fix(fixer) { | ||
if ((node.type === utils_1.AST_NODE_TYPES.AssignmentPattern && | ||
node.left.optional) || | ||
(node.type === utils_1.AST_NODE_TYPES.PropertyDefinition && node.definite)) { | ||
yield fixer.remove(sourceCode.getTokenBefore(typeNode)); | ||
} | ||
yield fixer.remove(typeNode); | ||
}, | ||
}); | ||
@@ -183,3 +191,3 @@ } | ||
} | ||
node.params.filter(param => param.type === experimental_utils_1.AST_NODE_TYPES.AssignmentPattern && | ||
node.params.filter(param => param.type === utils_1.AST_NODE_TYPES.AssignmentPattern && | ||
param.left && | ||
@@ -205,3 +213,3 @@ param.right).forEach(param => { | ||
ArrowFunctionExpression: inferrableParameterVisitor, | ||
ClassProperty: inferrablePropertyVisitor, | ||
PropertyDefinition: inferrablePropertyVisitor, | ||
}; | ||
@@ -208,0 +216,0 @@ }, |
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
var _a; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const no_invalid_this_1 = __importDefault(require("eslint/lib/rules/no-invalid-this")); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util_1 = require("../util"); | ||
exports.default = util_1.createRule({ | ||
const getESLintCoreRule_1 = require("../util/getESLintCoreRule"); | ||
const baseRule = (0, getESLintCoreRule_1.getESLintCoreRule)('no-invalid-this'); | ||
exports.default = (0, util_1.createRule)({ | ||
name: 'no-invalid-this', | ||
@@ -16,14 +14,15 @@ meta: { | ||
description: 'Disallow `this` keywords outside of classes or class-like objects', | ||
category: 'Best Practices', | ||
recommended: false, | ||
extendsBaseRule: true, | ||
}, | ||
messages: (_a = no_invalid_this_1.default.meta.messages) !== null && _a !== void 0 ? _a : { | ||
// TODO: this rule has only had messages since v7.0 - remove this when we remove support for v6 | ||
messages: (_a = baseRule.meta.messages) !== null && _a !== void 0 ? _a : { | ||
unexpectedThis: "Unexpected 'this'.", | ||
}, | ||
schema: no_invalid_this_1.default.meta.schema, | ||
hasSuggestions: baseRule.meta.hasSuggestions, | ||
schema: baseRule.meta.schema, | ||
}, | ||
defaultOptions: [{ capIsConstructor: true }], | ||
create(context) { | ||
const rules = no_invalid_this_1.default.create(context); | ||
const rules = baseRule.create(context); | ||
/** | ||
@@ -44,27 +43,31 @@ * Since function definitions can be nested we use a stack storing if "this" is valid in the current context. | ||
const thisIsValidStack = []; | ||
return Object.assign(Object.assign({}, rules), { ClassProperty() { | ||
return Object.assign(Object.assign({}, rules), { PropertyDefinition() { | ||
thisIsValidStack.push(true); | ||
}, | ||
'ClassProperty:exit'() { | ||
'PropertyDefinition:exit'() { | ||
thisIsValidStack.pop(); | ||
}, | ||
FunctionDeclaration(node) { | ||
thisIsValidStack.push(node.params.some(param => param.type === experimental_utils_1.AST_NODE_TYPES.Identifier && param.name === 'this')); | ||
var _a; | ||
thisIsValidStack.push(node.params.some(param => param.type === utils_1.AST_NODE_TYPES.Identifier && param.name === 'this')); | ||
// baseRule's work | ||
rules.FunctionDeclaration(node); | ||
(_a = rules.FunctionDeclaration) === null || _a === void 0 ? void 0 : _a.call(rules, node); | ||
}, | ||
'FunctionDeclaration:exit'(node) { | ||
var _a; | ||
thisIsValidStack.pop(); | ||
// baseRule's work | ||
rules['FunctionDeclaration:exit'](node); | ||
(_a = rules['FunctionDeclaration:exit']) === null || _a === void 0 ? void 0 : _a.call(rules, node); | ||
}, | ||
FunctionExpression(node) { | ||
thisIsValidStack.push(node.params.some(param => param.type === experimental_utils_1.AST_NODE_TYPES.Identifier && param.name === 'this')); | ||
var _a; | ||
thisIsValidStack.push(node.params.some(param => param.type === utils_1.AST_NODE_TYPES.Identifier && param.name === 'this')); | ||
// baseRule's work | ||
rules.FunctionExpression(node); | ||
(_a = rules.FunctionExpression) === null || _a === void 0 ? void 0 : _a.call(rules, node); | ||
}, | ||
'FunctionExpression:exit'(node) { | ||
var _a; | ||
thisIsValidStack.pop(); | ||
// baseRule's work | ||
rules['FunctionExpression:exit'](node); | ||
(_a = rules['FunctionExpression:exit']) === null || _a === void 0 ? void 0 : _a.call(rules, node); | ||
}, | ||
@@ -71,0 +74,0 @@ ThisExpression(node) { |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -30,12 +34,12 @@ exports.default = util.createRule({ | ||
docs: { | ||
description: 'Disallows usage of `void` type outside of generic or return types', | ||
category: 'Best Practices', | ||
recommended: false, | ||
description: 'Disallow `void` type outside of generic or return types', | ||
recommended: 'strict', | ||
}, | ||
messages: { | ||
invalidVoidForGeneric: '{{ generic }} may not have void as a type variable', | ||
invalidVoidNotReturnOrGeneric: 'void is only valid as a return type or generic type variable', | ||
invalidVoidNotReturn: 'void is only valid as a return type', | ||
invalidVoidNotReturnOrThisParam: 'void is only valid as return type or type of `this` parameter', | ||
invalidVoidNotReturnOrThisParamOrGeneric: 'void is only valid as a return type or generic type variable or the type of a `this` parameter', | ||
invalidVoidForGeneric: '{{ generic }} may not have void as a type argument.', | ||
invalidVoidNotReturnOrGeneric: 'void is only valid as a return type or generic type argument.', | ||
invalidVoidNotReturn: 'void is only valid as a return type.', | ||
invalidVoidNotReturnOrThisParam: 'void is only valid as return type or type of `this` parameter.', | ||
invalidVoidNotReturnOrThisParamOrGeneric: 'void is only valid as a return type or generic type argument or the type of a `this` parameter.', | ||
invalidVoidUnionConstituent: 'void is not valid as a constituent in a union type', | ||
}, | ||
@@ -69,16 +73,16 @@ schema: [ | ||
const validParents = [ | ||
experimental_utils_1.AST_NODE_TYPES.TSTypeAnnotation, | ||
utils_1.AST_NODE_TYPES.TSTypeAnnotation, // | ||
]; | ||
const invalidGrandParents = [ | ||
experimental_utils_1.AST_NODE_TYPES.TSPropertySignature, | ||
experimental_utils_1.AST_NODE_TYPES.CallExpression, | ||
experimental_utils_1.AST_NODE_TYPES.ClassProperty, | ||
experimental_utils_1.AST_NODE_TYPES.Identifier, | ||
utils_1.AST_NODE_TYPES.TSPropertySignature, | ||
utils_1.AST_NODE_TYPES.CallExpression, | ||
utils_1.AST_NODE_TYPES.PropertyDefinition, | ||
utils_1.AST_NODE_TYPES.Identifier, | ||
]; | ||
const validUnionMembers = [ | ||
experimental_utils_1.AST_NODE_TYPES.TSVoidKeyword, | ||
experimental_utils_1.AST_NODE_TYPES.TSNeverKeyword, | ||
utils_1.AST_NODE_TYPES.TSVoidKeyword, | ||
utils_1.AST_NODE_TYPES.TSNeverKeyword, | ||
]; | ||
if (allowInGenericTypeArguments === true) { | ||
validParents.push(experimental_utils_1.AST_NODE_TYPES.TSTypeParameterInstantiation); | ||
validParents.push(utils_1.AST_NODE_TYPES.TSTypeParameterInstantiation); | ||
} | ||
@@ -97,4 +101,4 @@ /** | ||
/* istanbul ignore next */ | ||
if (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) !== experimental_utils_1.AST_NODE_TYPES.TSTypeParameterInstantiation || | ||
((_b = node.parent.parent) === null || _b === void 0 ? void 0 : _b.type) !== experimental_utils_1.AST_NODE_TYPES.TSTypeReference) { | ||
if (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) !== utils_1.AST_NODE_TYPES.TSTypeParameterInstantiation || | ||
((_b = node.parent.parent) === null || _b === void 0 ? void 0 : _b.type) !== utils_1.AST_NODE_TYPES.TSTypeReference) { | ||
return; | ||
@@ -129,2 +133,13 @@ } | ||
/** | ||
* @brief checks if the generic type parameter defaults to void | ||
*/ | ||
function checkDefaultVoid(node, parentNode) { | ||
if (parentNode.default !== node) { | ||
context.report({ | ||
messageId: getNotReturnOrGenericMessageId(node), | ||
node, | ||
}); | ||
} | ||
} | ||
/** | ||
* @brief checks that a union containing void is valid | ||
@@ -139,5 +154,6 @@ * @return true if every member of the union is specified as a valid type in | ||
// allows any T<..., void, ...> here, checked by checkGenericTypeArgument | ||
(member.type === experimental_utils_1.AST_NODE_TYPES.TSTypeReference && | ||
(member.type === utils_1.AST_NODE_TYPES.TSTypeReference && | ||
((_a = member.typeParameters) === null || _a === void 0 ? void 0 : _a.type) === | ||
experimental_utils_1.AST_NODE_TYPES.TSTypeParameterInstantiation && ((_b = member.typeParameters) === null || _b === void 0 ? void 0 : _b.params.map(param => param.type).includes(experimental_utils_1.AST_NODE_TYPES.TSVoidKeyword))); | ||
utils_1.AST_NODE_TYPES.TSTypeParameterInstantiation && | ||
((_b = member.typeParameters) === null || _b === void 0 ? void 0 : _b.params.map(param => param.type).includes(utils_1.AST_NODE_TYPES.TSVoidKeyword))); | ||
}); | ||
@@ -147,3 +163,3 @@ } | ||
TSVoidKeyword(node) { | ||
var _a; | ||
var _a, _b; | ||
/* istanbul ignore next */ | ||
@@ -154,9 +170,16 @@ if (!((_a = node.parent) === null || _a === void 0 ? void 0 : _a.parent)) { | ||
// checks T<..., void, ...> against specification of allowInGenericArguments option | ||
if (node.parent.type === experimental_utils_1.AST_NODE_TYPES.TSTypeParameterInstantiation && | ||
node.parent.parent.type === experimental_utils_1.AST_NODE_TYPES.TSTypeReference) { | ||
if (node.parent.type === utils_1.AST_NODE_TYPES.TSTypeParameterInstantiation && | ||
node.parent.parent.type === utils_1.AST_NODE_TYPES.TSTypeReference) { | ||
checkGenericTypeArgument(node); | ||
return; | ||
} | ||
// allow <T = void> if allowInGenericTypeArguments is specified, and report if the generic type parameter extends void | ||
if (allowInGenericTypeArguments && | ||
node.parent.type === utils_1.AST_NODE_TYPES.TSTypeParameter && | ||
((_b = node.parent.default) === null || _b === void 0 ? void 0 : _b.type) === utils_1.AST_NODE_TYPES.TSVoidKeyword) { | ||
checkDefaultVoid(node, node.parent); | ||
return; | ||
} | ||
// union w/ void must contain types from validUnionMembers, or a valid generic void type | ||
if (node.parent.type === experimental_utils_1.AST_NODE_TYPES.TSUnionType && | ||
if (node.parent.type === utils_1.AST_NODE_TYPES.TSUnionType && | ||
isValidUnionType(node.parent)) { | ||
@@ -167,4 +190,4 @@ return; | ||
if (allowAsThisParameter && | ||
node.parent.type === experimental_utils_1.AST_NODE_TYPES.TSTypeAnnotation && | ||
node.parent.parent.type === experimental_utils_1.AST_NODE_TYPES.Identifier && | ||
node.parent.type === utils_1.AST_NODE_TYPES.TSTypeAnnotation && | ||
node.parent.parent.type === utils_1.AST_NODE_TYPES.Identifier && | ||
node.parent.parent.name === 'this') { | ||
@@ -182,3 +205,3 @@ return; | ||
: allowInGenericTypeArguments | ||
? 'invalidVoidNotReturnOrGeneric' | ||
? getNotReturnOrGenericMessageId(node) | ||
: allowAsThisParameter | ||
@@ -193,2 +216,7 @@ ? 'invalidVoidNotReturnOrThisParam' | ||
}); | ||
function getNotReturnOrGenericMessageId(node) { | ||
return node.parent.type === utils_1.AST_NODE_TYPES.TSUnionType | ||
? 'invalidVoidUnionConstituent' | ||
: 'invalidVoidNotReturnOrGeneric'; | ||
} | ||
//# sourceMappingURL=no-invalid-void-type.js.map |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -21,10 +25,7 @@ if (k2 === undefined) k2 = k; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
var _a; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const no_loop_func_1 = __importDefault(require("eslint/lib/rules/no-loop-func")); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
const getESLintCoreRule_1 = require("../util/getESLintCoreRule"); | ||
const baseRule = (0, getESLintCoreRule_1.getESLintCoreRule)('no-loop-func'); | ||
exports.default = util.createRule({ | ||
@@ -36,10 +37,8 @@ name: 'no-loop-func', | ||
description: 'Disallow function declarations that contain unsafe references inside loop statements', | ||
category: 'Best Practices', | ||
recommended: false, | ||
extendsBaseRule: true, | ||
}, | ||
hasSuggestions: baseRule.meta.hasSuggestions, | ||
schema: [], | ||
messages: (_a = no_loop_func_1.default === null || no_loop_func_1.default === void 0 ? void 0 : no_loop_func_1.default.meta.messages) !== null && _a !== void 0 ? _a : { | ||
unsafeRefs: 'Function declared in a loop contains unsafe references to variable(s) {{ varNames }}.', | ||
}, | ||
messages: baseRule.meta.messages, | ||
}, | ||
@@ -93,6 +92,6 @@ defaultOptions: [], | ||
switch (parent.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.WhileStatement: | ||
case experimental_utils_1.AST_NODE_TYPES.DoWhileStatement: | ||
case utils_1.AST_NODE_TYPES.WhileStatement: | ||
case utils_1.AST_NODE_TYPES.DoWhileStatement: | ||
return parent; | ||
case experimental_utils_1.AST_NODE_TYPES.ForStatement: | ||
case utils_1.AST_NODE_TYPES.ForStatement: | ||
// `init` is outside of the loop. | ||
@@ -103,4 +102,4 @@ if (parent.init !== currentNode) { | ||
break; | ||
case experimental_utils_1.AST_NODE_TYPES.ForInStatement: | ||
case experimental_utils_1.AST_NODE_TYPES.ForOfStatement: | ||
case utils_1.AST_NODE_TYPES.ForInStatement: | ||
case utils_1.AST_NODE_TYPES.ForOfStatement: | ||
// `right` is outside of the loop. | ||
@@ -111,5 +110,5 @@ if (parent.right !== currentNode) { | ||
break; | ||
case experimental_utils_1.AST_NODE_TYPES.ArrowFunctionExpression: | ||
case experimental_utils_1.AST_NODE_TYPES.FunctionExpression: | ||
case experimental_utils_1.AST_NODE_TYPES.FunctionDeclaration: | ||
case utils_1.AST_NODE_TYPES.ArrowFunctionExpression: | ||
case utils_1.AST_NODE_TYPES.FunctionExpression: | ||
case utils_1.AST_NODE_TYPES.FunctionDeclaration: | ||
// We don't need to check nested functions. | ||
@@ -152,3 +151,3 @@ return null; | ||
const declaration = definition === null || definition === void 0 ? void 0 : definition.parent; | ||
const kind = (declaration === null || declaration === void 0 ? void 0 : declaration.type) === experimental_utils_1.AST_NODE_TYPES.VariableDeclaration | ||
const kind = (declaration === null || declaration === void 0 ? void 0 : declaration.type) === utils_1.AST_NODE_TYPES.VariableDeclaration | ||
? declaration.kind | ||
@@ -155,0 +154,0 @@ : ''; |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -24,11 +28,4 @@ if (k2 === undefined) k2 = k; | ||
const util = __importStar(require("../util")); | ||
const baseRule = (() => { | ||
try { | ||
return require('eslint/lib/rules/no-loss-of-precision'); | ||
} | ||
catch (_a) { | ||
/* istanbul ignore next */ | ||
return null; | ||
} | ||
})(); | ||
const getESLintCoreRule_1 = require("../util/getESLintCoreRule"); | ||
const baseRule = (0, getESLintCoreRule_1.maybeGetESLintCoreRule)('no-loss-of-precision'); | ||
exports.default = util.createRule({ | ||
@@ -40,6 +37,6 @@ name: 'no-loss-of-precision', | ||
description: 'Disallow literal numbers that lose precision', | ||
category: 'Possible Errors', | ||
recommended: false, | ||
recommended: 'error', | ||
extendsBaseRule: true, | ||
}, | ||
hasSuggestions: baseRule === null || baseRule === void 0 ? void 0 : baseRule.meta.hasSuggestions, | ||
schema: [], | ||
@@ -50,3 +47,3 @@ messages: (_a = baseRule === null || baseRule === void 0 ? void 0 : baseRule.meta.messages) !== null && _a !== void 0 ? _a : { noLossOfPrecision: '' }, | ||
create(context) { | ||
/* istanbul ignore if */ if (baseRule === null) { | ||
/* istanbul ignore if */ if (baseRule == null) { | ||
throw new Error('@typescript-eslint/no-loss-of-precision requires at least ESLint v7.1.0'); | ||
@@ -56,3 +53,3 @@ } | ||
const rules = baseRule.create(context); | ||
function isSeperatedNumeric(node) { | ||
function isSeparatedNumeric(node) { | ||
return typeof node.value === 'number' && node.raw.includes('_'); | ||
@@ -62,3 +59,3 @@ } | ||
Literal(node) { | ||
rules.Literal(Object.assign(Object.assign({}, node), { raw: isSeperatedNumeric(node) ? node.raw.replace(/_/g, '') : node.raw })); | ||
rules.Literal(Object.assign(Object.assign({}, node), { raw: isSeparatedNumeric(node) ? node.raw.replace(/_/g, '') : node.raw })); | ||
}, | ||
@@ -65,0 +62,0 @@ }; |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -21,13 +25,28 @@ if (k2 === undefined) k2 = k; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
var _a; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const no_magic_numbers_1 = __importDefault(require("eslint/lib/rules/no-magic-numbers")); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
const baseRuleSchema = Array.isArray(no_magic_numbers_1.default.meta.schema) | ||
? no_magic_numbers_1.default.meta.schema[0] | ||
: no_magic_numbers_1.default.meta.schema; | ||
const getESLintCoreRule_1 = require("../util/getESLintCoreRule"); | ||
const baseRule = (0, getESLintCoreRule_1.getESLintCoreRule)('no-magic-numbers'); | ||
// Extend base schema with additional property to ignore TS numeric literal types | ||
const schema = util.deepMerge( | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument -- https://github.com/microsoft/TypeScript/issues/17002 | ||
Array.isArray(baseRule.meta.schema) | ||
? baseRule.meta.schema[0] | ||
: baseRule.meta.schema, { | ||
properties: { | ||
ignoreNumericLiteralTypes: { | ||
type: 'boolean', | ||
}, | ||
ignoreEnums: { | ||
type: 'boolean', | ||
}, | ||
ignoreReadonlyClassProperties: { | ||
type: 'boolean', | ||
}, | ||
ignoreTypeIndexes: { | ||
type: 'boolean', | ||
}, | ||
}, | ||
}); | ||
exports.default = util.createRule({ | ||
@@ -39,20 +58,7 @@ name: 'no-magic-numbers', | ||
description: 'Disallow magic numbers', | ||
category: 'Best Practices', | ||
recommended: false, | ||
extendsBaseRule: true, | ||
}, | ||
// Extend base schema with additional property to ignore TS numeric literal types | ||
schema: [ | ||
Object.assign(Object.assign({}, baseRuleSchema), { properties: Object.assign(Object.assign({}, baseRuleSchema.properties), { ignoreNumericLiteralTypes: { | ||
type: 'boolean', | ||
}, ignoreEnums: { | ||
type: 'boolean', | ||
}, ignoreReadonlyClassProperties: { | ||
type: 'boolean', | ||
} }) }), | ||
], | ||
messages: (_a = no_magic_numbers_1.default.meta.messages) !== null && _a !== void 0 ? _a : { | ||
useConst: "Number constants declarations must use 'const'.", | ||
noMagic: 'No magic number: {{raw}}.', | ||
}, | ||
schema: [schema], | ||
messages: baseRule.meta.messages, | ||
}, | ||
@@ -71,25 +77,40 @@ defaultOptions: [ | ||
create(context, [options]) { | ||
const rules = no_magic_numbers_1.default.create(context); | ||
const rules = baseRule.create(context); | ||
return { | ||
Literal(node) { | ||
var _a; | ||
// Check if the node is a TypeScript enum declaration | ||
if (options.ignoreEnums && isParentTSEnumDeclaration(node)) { | ||
// If it’s not a numeric literal we’re not interested | ||
if (typeof node.value !== 'number' && typeof node.value !== 'bigint') { | ||
return; | ||
} | ||
// This will be `true` if we’re configured to ignore this case (eg. it’s | ||
// an enum and `ignoreEnums` is `true`). It will be `false` if we’re not | ||
// configured to ignore this case. It will remain `undefined` if this is | ||
// not one of our exception cases, and we’ll fall back to the base rule. | ||
let isAllowed; | ||
// Check if the node is a TypeScript enum declaration | ||
if (isParentTSEnumDeclaration(node)) { | ||
isAllowed = options.ignoreEnums === true; | ||
} | ||
// Check TypeScript specific nodes for Numeric Literal | ||
if (options.ignoreNumericLiteralTypes && | ||
typeof node.value === 'number' && | ||
isTSNumericLiteralType(node)) { | ||
else if (isTSNumericLiteralType(node)) { | ||
isAllowed = options.ignoreNumericLiteralTypes === true; | ||
} | ||
// Check if the node is a type index | ||
else if (isAncestorTSIndexedAccessType(node)) { | ||
isAllowed = options.ignoreTypeIndexes === true; | ||
} | ||
// Check if the node is a readonly class property | ||
else if (isParentTSReadonlyPropertyDefinition(node)) { | ||
isAllowed = options.ignoreReadonlyClassProperties === true; | ||
} | ||
// If we’ve hit a case where the ignore option is true we can return now | ||
if (isAllowed === true) { | ||
return; | ||
} | ||
// Check if the node is a readonly class property | ||
if (typeof node.value === 'number' && | ||
isParentTSReadonlyClassProperty(node)) { | ||
if (options.ignoreReadonlyClassProperties) { | ||
return; | ||
} | ||
// If the ignore option is *not* set we can report it now | ||
else if (isAllowed === false) { | ||
let fullNumberNode = node; | ||
let raw = node.raw; | ||
if (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === experimental_utils_1.AST_NODE_TYPES.UnaryExpression && | ||
if (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.UnaryExpression && | ||
// the base rule only shows the operator for negative numbers | ||
@@ -119,3 +140,3 @@ // https://github.com/eslint/eslint/blob/9dfc8501fb1956c90dc11e6377b4cb38a6bea65d/lib/rules/no-magic-numbers.js#L126 | ||
var _a; | ||
if (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === experimental_utils_1.AST_NODE_TYPES.UnaryExpression && | ||
if (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.UnaryExpression && | ||
['-', '+'].includes(node.parent.operator)) { | ||
@@ -134,3 +155,3 @@ return node.parent.parent; | ||
var _a, _b; | ||
return ((_b = (_a = node.parent) === null || _a === void 0 ? void 0 : _a.parent) === null || _b === void 0 ? void 0 : _b.type) === experimental_utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration; | ||
return ((_b = (_a = node.parent) === null || _a === void 0 ? void 0 : _a.parent) === null || _b === void 0 ? void 0 : _b.type) === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration; | ||
} | ||
@@ -145,3 +166,3 @@ /** | ||
var _a, _b; | ||
if (((_b = (_a = node.parent) === null || _a === void 0 ? void 0 : _a.parent) === null || _b === void 0 ? void 0 : _b.type) === experimental_utils_1.AST_NODE_TYPES.TSUnionType) { | ||
if (((_b = (_a = node.parent) === null || _a === void 0 ? void 0 : _a.parent) === null || _b === void 0 ? void 0 : _b.type) === utils_1.AST_NODE_TYPES.TSUnionType) { | ||
return isGrandparentTSTypeAliasDeclaration(node.parent); | ||
@@ -159,3 +180,3 @@ } | ||
const parent = getLiteralParent(node); | ||
return (parent === null || parent === void 0 ? void 0 : parent.type) === experimental_utils_1.AST_NODE_TYPES.TSEnumMember; | ||
return (parent === null || parent === void 0 ? void 0 : parent.type) === utils_1.AST_NODE_TYPES.TSEnumMember; | ||
} | ||
@@ -170,3 +191,3 @@ /** | ||
var _a; | ||
return ((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === experimental_utils_1.AST_NODE_TYPES.TSLiteralType; | ||
return ((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.TSLiteralType; | ||
} | ||
@@ -182,3 +203,3 @@ /** | ||
// For negative numbers, use the parent node | ||
if (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === experimental_utils_1.AST_NODE_TYPES.UnaryExpression && | ||
if (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.UnaryExpression && | ||
node.parent.operator === '-') { | ||
@@ -207,5 +228,5 @@ node = node.parent; | ||
*/ | ||
function isParentTSReadonlyClassProperty(node) { | ||
function isParentTSReadonlyPropertyDefinition(node) { | ||
const parent = getLiteralParent(node); | ||
if ((parent === null || parent === void 0 ? void 0 : parent.type) === experimental_utils_1.AST_NODE_TYPES.ClassProperty && parent.readonly) { | ||
if ((parent === null || parent === void 0 ? void 0 : parent.type) === utils_1.AST_NODE_TYPES.PropertyDefinition && parent.readonly) { | ||
return true; | ||
@@ -215,2 +236,20 @@ } | ||
} | ||
/** | ||
* Checks if the node is part of a type indexed access (eg. Foo[4]) | ||
* @param node the node to be validated. | ||
* @returns true if the node is part of an indexed access | ||
* @private | ||
*/ | ||
function isAncestorTSIndexedAccessType(node) { | ||
var _a, _b, _c; | ||
// Handle unary expressions (eg. -4) | ||
let ancestor = getLiteralParent(node); | ||
// Go up another level while we’re part of a type union (eg. 1 | 2) or | ||
// intersection (eg. 1 & 2) | ||
while (((_a = ancestor === null || ancestor === void 0 ? void 0 : ancestor.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.TSUnionType || | ||
((_b = ancestor === null || ancestor === void 0 ? void 0 : ancestor.parent) === null || _b === void 0 ? void 0 : _b.type) === utils_1.AST_NODE_TYPES.TSIntersectionType) { | ||
ancestor = ancestor.parent; | ||
} | ||
return ((_c = ancestor === null || ancestor === void 0 ? void 0 : ancestor.parent) === null || _c === void 0 ? void 0 : _c.type) === utils_1.AST_NODE_TYPES.TSIndexedAccessType; | ||
} | ||
//# sourceMappingURL=no-magic-numbers.js.map |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -31,3 +35,2 @@ exports.default = util.createRule({ | ||
description: 'Enforce valid definition of `new` and `constructor`', | ||
category: 'Best Practices', | ||
recommended: 'error', | ||
@@ -50,7 +53,7 @@ }, | ||
switch (node.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.TSTypeAnnotation: | ||
case utils_1.AST_NODE_TYPES.TSTypeAnnotation: | ||
return getTypeReferenceName(node.typeAnnotation); | ||
case experimental_utils_1.AST_NODE_TYPES.TSTypeReference: | ||
case utils_1.AST_NODE_TYPES.TSTypeReference: | ||
return getTypeReferenceName(node.typeName); | ||
case experimental_utils_1.AST_NODE_TYPES.Identifier: | ||
case utils_1.AST_NODE_TYPES.Identifier: | ||
return node.name; | ||
@@ -71,3 +74,3 @@ default: | ||
parent.id && | ||
parent.id.type === experimental_utils_1.AST_NODE_TYPES.Identifier) { | ||
parent.id.type === utils_1.AST_NODE_TYPES.Identifier) { | ||
return getTypeReferenceName(returnType) === parent.id.name; | ||
@@ -94,3 +97,3 @@ } | ||
"ClassBody > MethodDefinition[key.name='new']"(node) { | ||
if (node.value.type === experimental_utils_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression) { | ||
if (node.value.type === utils_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression) { | ||
if (node.parent && | ||
@@ -97,0 +100,0 @@ isMatchingParentType(node.parent.parent, node.value.returnType)) { |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,6 +26,30 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const tsutils = __importStar(require("tsutils")); | ||
const ts = __importStar(require("typescript")); | ||
const util = __importStar(require("../util")); | ||
function parseChecksVoidReturn(checksVoidReturn) { | ||
var _a, _b, _c, _d, _e; | ||
switch (checksVoidReturn) { | ||
case false: | ||
return false; | ||
case true: | ||
case undefined: | ||
return { | ||
arguments: true, | ||
attributes: true, | ||
properties: true, | ||
returns: true, | ||
variables: true, | ||
}; | ||
default: | ||
return { | ||
arguments: (_a = checksVoidReturn.arguments) !== null && _a !== void 0 ? _a : true, | ||
attributes: (_b = checksVoidReturn.attributes) !== null && _b !== void 0 ? _b : true, | ||
properties: (_c = checksVoidReturn.properties) !== null && _c !== void 0 ? _c : true, | ||
returns: (_d = checksVoidReturn.returns) !== null && _d !== void 0 ? _d : true, | ||
variables: (_e = checksVoidReturn.variables) !== null && _e !== void 0 ? _e : true, | ||
}; | ||
} | ||
} | ||
exports.default = util.createRule({ | ||
@@ -31,4 +59,3 @@ name: 'no-misused-promises', | ||
docs: { | ||
description: 'Avoid using promises in places not designed to handle them', | ||
category: 'Best Practices', | ||
description: 'Disallow Promises in places not designed to handle them', | ||
recommended: 'error', | ||
@@ -38,4 +65,9 @@ requiresTypeChecking: true, | ||
messages: { | ||
voidReturn: 'Promise returned in function argument where a void return was expected.', | ||
voidReturnArgument: 'Promise returned in function argument where a void return was expected.', | ||
voidReturnVariable: 'Promise-returning function provided to variable where a void return was expected.', | ||
voidReturnProperty: 'Promise-returning function provided to property where a void return was expected.', | ||
voidReturnReturnValue: 'Promise-returning function provided to return value where a void return was expected.', | ||
voidReturnAttribute: 'Promise-returning function provided to attribute where a void return was expected.', | ||
conditional: 'Expected non-Promise value in a boolean conditional.', | ||
spread: 'Expected a non-Promise value to be spreaded in an object.', | ||
}, | ||
@@ -50,2 +82,18 @@ schema: [ | ||
checksVoidReturn: { | ||
oneOf: [ | ||
{ type: 'boolean' }, | ||
{ | ||
additionalProperties: false, | ||
properties: { | ||
arguments: { type: 'boolean' }, | ||
attributes: { type: 'boolean' }, | ||
properties: { type: 'boolean' }, | ||
returns: { type: 'boolean' }, | ||
variables: { type: 'boolean' }, | ||
}, | ||
type: 'object', | ||
}, | ||
], | ||
}, | ||
checksSpreads: { | ||
type: 'boolean', | ||
@@ -62,5 +110,6 @@ }, | ||
checksVoidReturn: true, | ||
checksSpreads: true, | ||
}, | ||
], | ||
create(context, [{ checksConditionals, checksVoidReturn }]) { | ||
create(context, [{ checksConditionals, checksVoidReturn, checksSpreads }]) { | ||
const parserServices = util.getParserServices(context); | ||
@@ -80,5 +129,19 @@ const checker = parserServices.program.getTypeChecker(); | ||
}; | ||
const voidReturnChecks = { | ||
CallExpression: checkArguments, | ||
NewExpression: checkArguments, | ||
checksVoidReturn = parseChecksVoidReturn(checksVoidReturn); | ||
const voidReturnChecks = checksVoidReturn | ||
? Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (checksVoidReturn.arguments && { | ||
CallExpression: checkArguments, | ||
NewExpression: checkArguments, | ||
})), (checksVoidReturn.attributes && { | ||
JSXAttribute: checkJSXAttribute, | ||
})), (checksVoidReturn.properties && { | ||
Property: checkProperty, | ||
})), (checksVoidReturn.returns && { | ||
ReturnStatement: checkReturnStatement, | ||
})), (checksVoidReturn.variables && { | ||
AssignmentExpression: checkAssignment, | ||
VariableDeclarator: checkVariableDeclaration, | ||
})) : {}; | ||
const spreadChecks = { | ||
SpreadElement: checkSpread, | ||
}; | ||
@@ -102,3 +165,3 @@ function checkTestConditional(node) { | ||
checkedNodes.add(node); | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.LogicalExpression) { | ||
if (node.type === utils_1.AST_NODE_TYPES.LogicalExpression) { | ||
// ignore the left operand for nullish coalescing expressions not in a context of a test expression | ||
@@ -124,8 +187,8 @@ if (node.operator !== '??' || isTestExpr) { | ||
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node); | ||
const voidParams = voidFunctionParams(checker, tsNode); | ||
if (voidParams.size === 0) { | ||
const voidArgs = voidFunctionArguments(checker, tsNode); | ||
if (voidArgs.size === 0) { | ||
return; | ||
} | ||
for (const [index, argument] of node.arguments.entries()) { | ||
if (!voidParams.has(index)) { | ||
if (!voidArgs.has(index)) { | ||
continue; | ||
@@ -136,3 +199,3 @@ } | ||
context.report({ | ||
messageId: 'voidReturn', | ||
messageId: 'voidReturnArgument', | ||
node: argument, | ||
@@ -143,5 +206,143 @@ }); | ||
} | ||
return Object.assign(Object.assign({}, (checksConditionals ? conditionalChecks : {})), (checksVoidReturn ? voidReturnChecks : {})); | ||
function checkAssignment(node) { | ||
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node); | ||
const varType = checker.getTypeAtLocation(tsNode.left); | ||
if (!isVoidReturningFunctionType(checker, tsNode.left, varType)) { | ||
return; | ||
} | ||
if (returnsThenable(checker, tsNode.right)) { | ||
context.report({ | ||
messageId: 'voidReturnVariable', | ||
node: node.right, | ||
}); | ||
} | ||
} | ||
function checkVariableDeclaration(node) { | ||
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node); | ||
if (tsNode.initializer === undefined || node.init == null) { | ||
return; | ||
} | ||
const varType = checker.getTypeAtLocation(tsNode.name); | ||
if (!isVoidReturningFunctionType(checker, tsNode.initializer, varType)) { | ||
return; | ||
} | ||
if (returnsThenable(checker, tsNode.initializer)) { | ||
context.report({ | ||
messageId: 'voidReturnVariable', | ||
node: node.init, | ||
}); | ||
} | ||
} | ||
function checkProperty(node) { | ||
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node); | ||
if (ts.isPropertyAssignment(tsNode)) { | ||
const contextualType = checker.getContextualType(tsNode.initializer); | ||
if (contextualType !== undefined && | ||
isVoidReturningFunctionType(checker, tsNode.initializer, contextualType) && | ||
returnsThenable(checker, tsNode.initializer)) { | ||
context.report({ | ||
messageId: 'voidReturnProperty', | ||
node: node.value, | ||
}); | ||
} | ||
} | ||
else if (ts.isShorthandPropertyAssignment(tsNode)) { | ||
const contextualType = checker.getContextualType(tsNode.name); | ||
if (contextualType !== undefined && | ||
isVoidReturningFunctionType(checker, tsNode.name, contextualType) && | ||
returnsThenable(checker, tsNode.name)) { | ||
context.report({ | ||
messageId: 'voidReturnProperty', | ||
node: node.value, | ||
}); | ||
} | ||
} | ||
else if (ts.isMethodDeclaration(tsNode)) { | ||
if (ts.isComputedPropertyName(tsNode.name)) { | ||
return; | ||
} | ||
const obj = tsNode.parent; | ||
// Below condition isn't satisfied unless something goes wrong, | ||
// but is needed for type checking. | ||
// 'node' does not include class method declaration so 'obj' is | ||
// always an object literal expression, but after converting 'node' | ||
// to TypeScript AST, its type includes MethodDeclaration which | ||
// does include the case of class method declaration. | ||
if (!ts.isObjectLiteralExpression(obj)) { | ||
return; | ||
} | ||
if (!returnsThenable(checker, tsNode)) { | ||
return; | ||
} | ||
const objType = checker.getContextualType(obj); | ||
if (objType === undefined) { | ||
return; | ||
} | ||
const propertySymbol = checker.getPropertyOfType(objType, tsNode.name.text); | ||
if (propertySymbol === undefined) { | ||
return; | ||
} | ||
const contextualType = checker.getTypeOfSymbolAtLocation(propertySymbol, tsNode.name); | ||
if (isVoidReturningFunctionType(checker, tsNode.name, contextualType)) { | ||
context.report({ | ||
messageId: 'voidReturnProperty', | ||
node: node.value, | ||
}); | ||
} | ||
return; | ||
} | ||
} | ||
function checkReturnStatement(node) { | ||
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node); | ||
if (tsNode.expression === undefined || node.argument == null) { | ||
return; | ||
} | ||
const contextualType = checker.getContextualType(tsNode.expression); | ||
if (contextualType !== undefined && | ||
isVoidReturningFunctionType(checker, tsNode.expression, contextualType) && | ||
returnsThenable(checker, tsNode.expression)) { | ||
context.report({ | ||
messageId: 'voidReturnReturnValue', | ||
node: node.argument, | ||
}); | ||
} | ||
} | ||
function checkJSXAttribute(node) { | ||
if (node.value == null || | ||
node.value.type !== utils_1.AST_NODE_TYPES.JSXExpressionContainer) { | ||
return; | ||
} | ||
const expressionContainer = parserServices.esTreeNodeToTSNodeMap.get(node.value); | ||
const expression = parserServices.esTreeNodeToTSNodeMap.get(node.value.expression); | ||
const contextualType = checker.getContextualType(expressionContainer); | ||
if (contextualType !== undefined && | ||
isVoidReturningFunctionType(checker, expressionContainer, contextualType) && | ||
returnsThenable(checker, expression)) { | ||
context.report({ | ||
messageId: 'voidReturnAttribute', | ||
node: node.value, | ||
}); | ||
} | ||
} | ||
function checkSpread(node) { | ||
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node); | ||
if (isSometimesThenable(checker, tsNode.expression)) { | ||
context.report({ | ||
messageId: 'spread', | ||
node: node.argument, | ||
}); | ||
} | ||
} | ||
return Object.assign(Object.assign(Object.assign({}, (checksConditionals ? conditionalChecks : {})), (checksVoidReturn ? voidReturnChecks : {})), (checksSpreads ? spreadChecks : {})); | ||
}, | ||
}); | ||
function isSometimesThenable(checker, node) { | ||
const type = checker.getTypeAtLocation(node); | ||
for (const subType of tsutils.unionTypeParts(checker.getApparentType(type))) { | ||
if (tsutils.isThenableType(checker, node, subType)) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
// Variation on the thenable check which requires all forms of the type (read: | ||
@@ -198,9 +399,31 @@ // alternates in a union) to be thenable. Otherwise, you might be trying to | ||
} | ||
// Get the positions of parameters which are void functions (and not also | ||
function checkThenableOrVoidArgument(checker, node, type, index, thenableReturnIndices, voidReturnIndices) { | ||
if (isThenableReturningFunctionType(checker, node.expression, type)) { | ||
thenableReturnIndices.add(index); | ||
} | ||
else if (isVoidReturningFunctionType(checker, node.expression, type)) { | ||
// If a certain argument accepts both thenable and void returns, | ||
// a promise-returning function is valid | ||
if (!thenableReturnIndices.has(index)) { | ||
voidReturnIndices.add(index); | ||
} | ||
} | ||
} | ||
// Get the positions of arguments which are void functions (and not also | ||
// thenable functions). These are the candidates for the void-return check at | ||
// the current call site. | ||
function voidFunctionParams(checker, node) { | ||
// If the function parameters end with a 'rest' parameter, then we consider | ||
// the array type parameter (e.g. '...args:Array<SomeType>') when determining | ||
// if trailing arguments are candidates. | ||
function voidFunctionArguments(checker, node) { | ||
// 'new' can be used without any arguments, as in 'let b = new Object;' | ||
// In this case, there are no argument positions to check, so return early. | ||
if (!node.arguments) { | ||
return new Set(); | ||
} | ||
const thenableReturnIndices = new Set(); | ||
const voidReturnIndices = new Set(); | ||
const thenableReturnIndices = new Set(); | ||
const type = checker.getTypeAtLocation(node.expression); | ||
// We can't use checker.getResolvedSignature because it prefers an early '() => void' over a later '() => Promise<void>' | ||
// See https://github.com/microsoft/TypeScript/issues/48077 | ||
for (const subType of tsutils.unionTypeParts(type)) { | ||
@@ -213,37 +436,89 @@ // Standard function calls and `new` have two different types of signatures | ||
for (const [index, parameter] of signature.parameters.entries()) { | ||
const type = checker.getTypeOfSymbolAtLocation(parameter, node.expression); | ||
for (const subType of tsutils.unionTypeParts(type)) { | ||
for (const signature of subType.getCallSignatures()) { | ||
const returnType = signature.getReturnType(); | ||
if (tsutils.isTypeFlagSet(returnType, ts.TypeFlags.Void)) { | ||
voidReturnIndices.add(index); | ||
const decl = parameter.valueDeclaration; | ||
let type = checker.getTypeOfSymbolAtLocation(parameter, node.expression); | ||
// If this is a array 'rest' parameter, check all of the argument indices | ||
// from the current argument to the end. | ||
// Note - we currently do not support 'spread' arguments - adding support for them | ||
// is tracked in https://github.com/typescript-eslint/typescript-eslint/issues/5744 | ||
if (decl && ts.isParameter(decl) && decl.dotDotDotToken) { | ||
if (checker.isArrayType(type)) { | ||
// Unwrap 'Array<MaybeVoidFunction>' to 'MaybeVoidFunction', | ||
// so that we'll handle it in the same way as a non-rest | ||
// 'param: MaybeVoidFunction' | ||
type = util.getTypeArguments(type, checker)[0]; | ||
for (let i = index; i < node.arguments.length; i++) { | ||
checkThenableOrVoidArgument(checker, node, type, i, thenableReturnIndices, voidReturnIndices); | ||
} | ||
else if (tsutils.isThenableType(checker, node.expression, returnType)) { | ||
thenableReturnIndices.add(index); | ||
} | ||
else if (checker.isTupleType(type)) { | ||
// Check each type in the tuple - for example, [boolean, () => void] would | ||
// add the index of the second tuple parameter to 'voidReturnIndices' | ||
const typeArgs = util.getTypeArguments(type, checker); | ||
for (let i = index; i < node.arguments.length && i - index < typeArgs.length; i++) { | ||
checkThenableOrVoidArgument(checker, node, typeArgs[i - index], i, thenableReturnIndices, voidReturnIndices); | ||
} | ||
} | ||
} | ||
else { | ||
checkThenableOrVoidArgument(checker, node, type, index, thenableReturnIndices, voidReturnIndices); | ||
} | ||
} | ||
} | ||
} | ||
// If a certain positional argument accepts both thenable and void returns, | ||
// a promise-returning function is valid | ||
for (const thenable of thenableReturnIndices) { | ||
voidReturnIndices.delete(thenable); | ||
for (const index of thenableReturnIndices) { | ||
voidReturnIndices.delete(index); | ||
} | ||
return voidReturnIndices; | ||
} | ||
// Returns true if the expression is a function that returns a thenable | ||
function returnsThenable(checker, node) { | ||
const type = checker.getApparentType(checker.getTypeAtLocation(node)); | ||
/** | ||
* @returns Whether any call signature of the type has a thenable return type. | ||
*/ | ||
function anySignatureIsThenableType(checker, node, type) { | ||
for (const signature of type.getCallSignatures()) { | ||
const returnType = signature.getReturnType(); | ||
if (tsutils.isThenableType(checker, node, returnType)) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
/** | ||
* @returns Whether type is a thenable-returning function. | ||
*/ | ||
function isThenableReturningFunctionType(checker, node, type) { | ||
for (const subType of tsutils.unionTypeParts(type)) { | ||
if (anySignatureIsThenableType(checker, node, subType)) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
/** | ||
* @returns Whether type is a void-returning function. | ||
*/ | ||
function isVoidReturningFunctionType(checker, node, type) { | ||
let hadVoidReturn = false; | ||
for (const subType of tsutils.unionTypeParts(type)) { | ||
for (const signature of subType.getCallSignatures()) { | ||
const returnType = signature.getReturnType(); | ||
// If a certain positional argument accepts both thenable and void returns, | ||
// a promise-returning function is valid | ||
if (tsutils.isThenableType(checker, node, returnType)) { | ||
return true; | ||
return false; | ||
} | ||
hadVoidReturn || (hadVoidReturn = tsutils.isTypeFlagSet(returnType, ts.TypeFlags.Void)); | ||
} | ||
} | ||
return hadVoidReturn; | ||
} | ||
/** | ||
* @returns Whether expression is a function that returns a thenable. | ||
*/ | ||
function returnsThenable(checker, node) { | ||
const type = checker.getApparentType(checker.getTypeAtLocation(node)); | ||
if (anySignatureIsThenableType(checker, node, type)) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
//# sourceMappingURL=no-misused-promises.js.map |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -30,8 +34,7 @@ exports.default = util.createRule({ | ||
docs: { | ||
description: 'Disallow the use of custom TypeScript modules and namespaces', | ||
category: 'Best Practices', | ||
description: 'Disallow TypeScript namespaces', | ||
recommended: 'error', | ||
}, | ||
messages: { | ||
moduleSyntaxIsPreferred: 'ES2015 module syntax is preferred over custom TypeScript modules and namespaces.', | ||
moduleSyntaxIsPreferred: 'ES2015 module syntax is preferred over namespaces.', | ||
}, | ||
@@ -43,5 +46,7 @@ schema: [ | ||
allowDeclarations: { | ||
description: 'Whether to allow `declare` with custom TypeScript namespaces.', | ||
type: 'boolean', | ||
}, | ||
allowDefinitionFiles: { | ||
description: 'Whether to allow `declare` with custom TypeScript namespaces inside definition files.', | ||
type: 'boolean', | ||
@@ -63,6 +68,7 @@ }, | ||
function isDeclaration(node) { | ||
var _a; | ||
return (node.declare === true || | ||
(((_a = node.parent.parent) === null || _a === void 0 ? void 0 : _a.type) === experimental_utils_1.AST_NODE_TYPES.TSModuleDeclaration && | ||
isDeclaration(node.parent.parent))); | ||
if (node.type === utils_1.AST_NODE_TYPES.TSModuleDeclaration && | ||
node.declare === true) { | ||
return true; | ||
} | ||
return node.parent != null && isDeclaration(node.parent); | ||
} | ||
@@ -72,3 +78,3 @@ return { | ||
if ((node.parent && | ||
node.parent.type === experimental_utils_1.AST_NODE_TYPES.TSModuleDeclaration) || | ||
node.parent.type === utils_1.AST_NODE_TYPES.TSModuleDeclaration) || | ||
(allowDefinitionFiles && util.isDefinitionFile(filename)) || | ||
@@ -75,0 +81,0 @@ (allowDeclarations && isDeclaration(node))) { |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,5 +26,5 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const semver = __importStar(require("semver")); | ||
const ts = __importStar(require("typescript")); | ||
const semver = __importStar(require("semver")); | ||
const util = __importStar(require("../util")); | ||
@@ -35,7 +39,6 @@ const is3dot9 = semver.satisfies(ts.version, `>= 3.9.0 || >= 3.9.1-rc || >= 3.9.0-beta`, { | ||
docs: { | ||
description: 'Disallows using a non-null assertion after an optional chain expression', | ||
category: 'Possible Errors', | ||
description: 'Disallow non-null assertions after an optional chain expression', | ||
recommended: 'error', | ||
suggestion: true, | ||
}, | ||
hasSuggestions: true, | ||
messages: { | ||
@@ -113,3 +116,3 @@ noNonNullOptionalChain: 'Optional chain expressions can return undefined by design - using a non-null assertion is unsafe and wrong.', | ||
switch (current.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.MemberExpression: | ||
case utils_1.AST_NODE_TYPES.MemberExpression: | ||
if (current.optional) { | ||
@@ -121,3 +124,3 @@ // found an optional chain! stop traversing | ||
continue; | ||
case experimental_utils_1.AST_NODE_TYPES.CallExpression: | ||
case utils_1.AST_NODE_TYPES.CallExpression: | ||
if (current.optional) { | ||
@@ -124,0 +127,0 @@ // found an optional chain! stop traversing |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -30,7 +34,6 @@ exports.default = util.createRule({ | ||
docs: { | ||
description: 'Disallows non-null assertions using the `!` postfix operator', | ||
category: 'Stylistic Issues', | ||
description: 'Disallow non-null assertions using the `!` postfix operator', | ||
recommended: 'warn', | ||
suggestion: true, | ||
}, | ||
hasSuggestions: true, | ||
messages: { | ||
@@ -67,3 +70,3 @@ noNonNull: 'Forbidden non-null assertion.', | ||
} | ||
if (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === experimental_utils_1.AST_NODE_TYPES.MemberExpression && | ||
if (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.MemberExpression && | ||
node.parent.object === node) { | ||
@@ -103,3 +106,3 @@ if (!node.parent.optional) { | ||
} | ||
else if (((_b = node.parent) === null || _b === void 0 ? void 0 : _b.type) === experimental_utils_1.AST_NODE_TYPES.CallExpression && | ||
else if (((_b = node.parent) === null || _b === void 0 ? void 0 : _b.type) === utils_1.AST_NODE_TYPES.CallExpression && | ||
node.parent.callee === node) { | ||
@@ -106,0 +109,0 @@ if (!node.parent.optional) { |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -28,6 +32,7 @@ exports.default = util.createRule({ | ||
meta: { | ||
deprecated: true, | ||
replacedBy: ['@typescript-eslint/parameter-properties'], | ||
type: 'problem', | ||
docs: { | ||
description: 'Disallow the use of parameter properties in class constructors', | ||
category: 'Stylistic Issues', | ||
// too opinionated to be recommended | ||
@@ -88,7 +93,7 @@ recommended: false, | ||
// HAS to be an identifier or assignment or TSC will throw | ||
if (node.parameter.type !== experimental_utils_1.AST_NODE_TYPES.Identifier && | ||
node.parameter.type !== experimental_utils_1.AST_NODE_TYPES.AssignmentPattern) { | ||
if (node.parameter.type !== utils_1.AST_NODE_TYPES.Identifier && | ||
node.parameter.type !== utils_1.AST_NODE_TYPES.AssignmentPattern) { | ||
return; | ||
} | ||
const name = node.parameter.type === experimental_utils_1.AST_NODE_TYPES.Identifier | ||
const name = node.parameter.type === utils_1.AST_NODE_TYPES.Identifier | ||
? node.parameter.name | ||
@@ -95,0 +100,0 @@ : // has to be an Identifier or TSC will throw an error |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -31,3 +35,2 @@ exports.default = util.createRule({ | ||
description: 'Disallow variable redeclaration', | ||
category: 'Best Practices', | ||
recommended: false, | ||
@@ -65,10 +68,14 @@ extendsBaseRule: true, | ||
const CLASS_DECLARATION_MERGE_NODES = new Set([ | ||
experimental_utils_1.AST_NODE_TYPES.TSInterfaceDeclaration, | ||
experimental_utils_1.AST_NODE_TYPES.TSModuleDeclaration, | ||
experimental_utils_1.AST_NODE_TYPES.ClassDeclaration, | ||
utils_1.AST_NODE_TYPES.TSInterfaceDeclaration, | ||
utils_1.AST_NODE_TYPES.TSModuleDeclaration, | ||
utils_1.AST_NODE_TYPES.ClassDeclaration, | ||
]); | ||
const FUNCTION_DECLARATION_MERGE_NODES = new Set([ | ||
experimental_utils_1.AST_NODE_TYPES.TSModuleDeclaration, | ||
experimental_utils_1.AST_NODE_TYPES.FunctionDeclaration, | ||
utils_1.AST_NODE_TYPES.TSModuleDeclaration, | ||
utils_1.AST_NODE_TYPES.FunctionDeclaration, | ||
]); | ||
const ENUM_DECLARATION_MERGE_NODES = new Set([ | ||
utils_1.AST_NODE_TYPES.TSEnumDeclaration, | ||
utils_1.AST_NODE_TYPES.TSModuleDeclaration, | ||
]); | ||
function* iterateDeclarations(variable) { | ||
@@ -97,7 +104,7 @@ if ((options === null || options === void 0 ? void 0 : options.builtinGlobals) && | ||
// ignore function declarations because TS will treat them as an overload | ||
.filter(({ parent }) => parent.type !== experimental_utils_1.AST_NODE_TYPES.TSDeclareFunction); | ||
.filter(({ parent }) => parent.type !== utils_1.AST_NODE_TYPES.TSDeclareFunction); | ||
if (options.ignoreDeclarationMerge && identifiers.length > 1) { | ||
if ( | ||
// interfaces merging | ||
identifiers.every(({ parent }) => parent.type === experimental_utils_1.AST_NODE_TYPES.TSInterfaceDeclaration)) { | ||
identifiers.every(({ parent }) => parent.type === utils_1.AST_NODE_TYPES.TSInterfaceDeclaration)) { | ||
return; | ||
@@ -107,3 +114,3 @@ } | ||
// namespace/module merging | ||
identifiers.every(({ parent }) => parent.type === experimental_utils_1.AST_NODE_TYPES.TSModuleDeclaration)) { | ||
identifiers.every(({ parent }) => parent.type === utils_1.AST_NODE_TYPES.TSModuleDeclaration)) { | ||
return; | ||
@@ -114,3 +121,3 @@ } | ||
identifiers.every(({ parent }) => CLASS_DECLARATION_MERGE_NODES.has(parent.type))) { | ||
const classDecls = identifiers.filter(({ parent }) => parent.type === experimental_utils_1.AST_NODE_TYPES.ClassDeclaration); | ||
const classDecls = identifiers.filter(({ parent }) => parent.type === utils_1.AST_NODE_TYPES.ClassDeclaration); | ||
if (classDecls.length === 1) { | ||
@@ -129,3 +136,3 @@ // safe declaration merging | ||
identifiers.every(({ parent }) => FUNCTION_DECLARATION_MERGE_NODES.has(parent.type))) { | ||
const functionDecls = identifiers.filter(({ parent }) => parent.type === experimental_utils_1.AST_NODE_TYPES.FunctionDeclaration); | ||
const functionDecls = identifiers.filter(({ parent }) => parent.type === utils_1.AST_NODE_TYPES.FunctionDeclaration); | ||
if (functionDecls.length === 1) { | ||
@@ -135,3 +142,3 @@ // safe declaration merging | ||
} | ||
// there's more than one class declaration, which needs to be reported | ||
// there's more than one function declaration, which needs to be reported | ||
for (const { identifier } of functionDecls) { | ||
@@ -142,2 +149,16 @@ yield { type: 'syntax', node: identifier, loc: identifier.loc }; | ||
} | ||
if ( | ||
// enum + namespace merging | ||
identifiers.every(({ parent }) => ENUM_DECLARATION_MERGE_NODES.has(parent.type))) { | ||
const enumDecls = identifiers.filter(({ parent }) => parent.type === utils_1.AST_NODE_TYPES.TSEnumDeclaration); | ||
if (enumDecls.length === 1) { | ||
// safe declaration merging | ||
return; | ||
} | ||
// there's more than one enum declaration, which needs to be reported | ||
for (const { identifier } of enumDecls) { | ||
yield { type: 'syntax', node: identifier, loc: identifier.loc }; | ||
} | ||
return; | ||
} | ||
} | ||
@@ -144,0 +165,0 @@ for (const { identifier } of identifiers) { |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,2 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -29,4 +34,3 @@ exports.default = util.createRule({ | ||
docs: { | ||
description: 'Disallows invocation of `require()`', | ||
category: 'Best Practices', | ||
description: 'Disallow invocation of `require()`', | ||
recommended: false, | ||
@@ -42,7 +46,12 @@ }, | ||
return { | ||
'CallExpression > Identifier[name="require"]'(node) { | ||
context.report({ | ||
node: node.parent, | ||
messageId: 'noRequireImports', | ||
}); | ||
'CallExpression[callee.name="require"]'(node) { | ||
const variable = utils_1.ASTUtils.findVariable(context.getScope(), 'require'); | ||
// ignore non-global require usage as it's something user-land custom instead | ||
// of the commonjs standard | ||
if (!(variable === null || variable === void 0 ? void 0 : variable.identifiers.length)) { | ||
context.report({ | ||
node, | ||
messageId: 'noRequireImports', | ||
}); | ||
} | ||
}, | ||
@@ -49,0 +58,0 @@ TSExternalModuleReference(node) { |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,5 +26,10 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const scope_manager_1 = require("@typescript-eslint/scope-manager"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
const allowedFunctionVariableDefTypes = new Set([ | ||
utils_1.AST_NODE_TYPES.TSCallSignatureDeclaration, | ||
utils_1.AST_NODE_TYPES.TSFunctionType, | ||
utils_1.AST_NODE_TYPES.TSMethodSignature, | ||
]); | ||
exports.default = util.createRule({ | ||
@@ -32,3 +41,2 @@ name: 'no-shadow', | ||
description: 'Disallow variable declarations from shadowing variables declared in the outer scope', | ||
category: 'Variables', | ||
recommended: false, | ||
@@ -53,2 +61,5 @@ extendsBaseRule: true, | ||
}, | ||
ignoreOnInitialization: { | ||
type: 'boolean', | ||
}, | ||
ignoreTypeValueShadow: { | ||
@@ -65,3 +76,4 @@ type: 'boolean', | ||
messages: { | ||
noShadow: "'{{name}}' is already declared in the upper scope.", | ||
noShadow: "'{{name}}' is already declared in the upper scope on line {{shadowedLine}} column {{shadowedColumn}}.", | ||
noShadowGlobal: "'{{name}}' is already a global variable.", | ||
}, | ||
@@ -74,2 +86,3 @@ }, | ||
hoist: 'functions', | ||
ignoreOnInitialization: false, | ||
ignoreTypeValueShadow: true, | ||
@@ -91,4 +104,11 @@ ignoreFunctionTypeParameterNameValueShadow: true, | ||
function isThisParam(variable) { | ||
return variable.defs[0].type === 'Parameter' && variable.name === 'this'; | ||
return (variable.defs[0].type === scope_manager_1.DefinitionType.Parameter && | ||
variable.name === 'this'); | ||
} | ||
function isTypeImport(definition) { | ||
return ((definition === null || definition === void 0 ? void 0 : definition.type) === scope_manager_1.DefinitionType.ImportBinding && | ||
(definition.parent.importKind === 'type' || | ||
(definition.node.type === utils_1.AST_NODE_TYPES.ImportSpecifier && | ||
definition.node.importKind === 'type'))); | ||
} | ||
function isTypeValueShadow(variable, shadowed) { | ||
@@ -102,3 +122,6 @@ if (options.ignoreTypeValueShadow !== true) { | ||
} | ||
const isShadowedValue = 'isValueVariable' in shadowed ? shadowed.isValueVariable : true; | ||
const [firstDefinition] = shadowed.defs; | ||
const isShadowedValue = !('isValueVariable' in shadowed) || | ||
!firstDefinition || | ||
(!isTypeImport(firstDefinition) && shadowed.isValueVariable); | ||
return variable.isValueVariable !== isShadowedValue; | ||
@@ -118,5 +141,80 @@ } | ||
} | ||
const id = variable.identifiers[0]; | ||
return util.isFunctionType(id.parent); | ||
return variable.defs.every(def => allowedFunctionVariableDefTypes.has(def.node.type)); | ||
} | ||
function isGenericOfStaticMethod(variable) { | ||
if (!('isTypeVariable' in variable)) { | ||
// this shouldn't happen... | ||
return false; | ||
} | ||
if (!variable.isTypeVariable) { | ||
return false; | ||
} | ||
if (variable.identifiers.length === 0) { | ||
return false; | ||
} | ||
const typeParameter = variable.identifiers[0].parent; | ||
if ((typeParameter === null || typeParameter === void 0 ? void 0 : typeParameter.type) !== utils_1.AST_NODE_TYPES.TSTypeParameter) { | ||
return false; | ||
} | ||
const typeParameterDecl = typeParameter.parent; | ||
if ((typeParameterDecl === null || typeParameterDecl === void 0 ? void 0 : typeParameterDecl.type) !== utils_1.AST_NODE_TYPES.TSTypeParameterDeclaration) { | ||
return false; | ||
} | ||
const functionExpr = typeParameterDecl.parent; | ||
if (!functionExpr || | ||
(functionExpr.type !== utils_1.AST_NODE_TYPES.FunctionExpression && | ||
functionExpr.type !== utils_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression)) { | ||
return false; | ||
} | ||
const methodDefinition = functionExpr.parent; | ||
if ((methodDefinition === null || methodDefinition === void 0 ? void 0 : methodDefinition.type) !== utils_1.AST_NODE_TYPES.MethodDefinition) { | ||
return false; | ||
} | ||
return methodDefinition.static; | ||
} | ||
function isGenericOfClassDecl(variable) { | ||
if (!('isTypeVariable' in variable)) { | ||
// this shouldn't happen... | ||
return false; | ||
} | ||
if (!variable.isTypeVariable) { | ||
return false; | ||
} | ||
if (variable.identifiers.length === 0) { | ||
return false; | ||
} | ||
const typeParameter = variable.identifiers[0].parent; | ||
if ((typeParameter === null || typeParameter === void 0 ? void 0 : typeParameter.type) !== utils_1.AST_NODE_TYPES.TSTypeParameter) { | ||
return false; | ||
} | ||
const typeParameterDecl = typeParameter.parent; | ||
if ((typeParameterDecl === null || typeParameterDecl === void 0 ? void 0 : typeParameterDecl.type) !== utils_1.AST_NODE_TYPES.TSTypeParameterDeclaration) { | ||
return false; | ||
} | ||
const classDecl = typeParameterDecl.parent; | ||
return (classDecl === null || classDecl === void 0 ? void 0 : classDecl.type) === utils_1.AST_NODE_TYPES.ClassDeclaration; | ||
} | ||
function isGenericOfAStaticMethodShadow(variable, shadowed) { | ||
return (isGenericOfStaticMethod(variable) && isGenericOfClassDecl(shadowed)); | ||
} | ||
function isImportDeclaration(definition) { | ||
return definition.type === utils_1.AST_NODE_TYPES.ImportDeclaration; | ||
} | ||
function isExternalModuleDeclarationWithName(scope, name) { | ||
return (scope.type === scope_manager_1.ScopeType.tsModule && | ||
scope.block.type === utils_1.AST_NODE_TYPES.TSModuleDeclaration && | ||
scope.block.id.type === utils_1.AST_NODE_TYPES.Literal && | ||
scope.block.id.value === name); | ||
} | ||
function isExternalDeclarationMerging(scope, variable, shadowed) { | ||
var _a; | ||
const [firstDefinition] = shadowed.defs; | ||
const [secondDefinition] = variable.defs; | ||
return (isTypeImport(firstDefinition) && | ||
isImportDeclaration(firstDefinition.parent) && | ||
isExternalModuleDeclarationWithName(scope, firstDefinition.parent.source.value) && | ||
secondDefinition.node.type === utils_1.AST_NODE_TYPES.TSInterfaceDeclaration && | ||
((_a = secondDefinition.node.parent) === null || _a === void 0 ? void 0 : _a.type) === | ||
utils_1.AST_NODE_TYPES.ExportNamedDeclaration); | ||
} | ||
/** | ||
@@ -140,3 +238,3 @@ * Check if variable name is allowed. | ||
const block = variable.scope.block; | ||
return (block.type === experimental_utils_1.AST_NODE_TYPES.ClassDeclaration && | ||
return (block.type === utils_1.AST_NODE_TYPES.ClassDeclaration && | ||
block.id === variable.identifiers[0]); | ||
@@ -154,6 +252,100 @@ } | ||
const block = variable.scope.block; | ||
return (block.type === experimental_utils_1.AST_NODE_TYPES.TSEnumDeclaration && | ||
return (block.type === utils_1.AST_NODE_TYPES.TSEnumDeclaration && | ||
block.id === variable.identifiers[0]); | ||
} | ||
/** | ||
* Checks whether or not a given location is inside of the range of a given node. | ||
* @param node An node to check. | ||
* @param location A location to check. | ||
* @returns `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]; | ||
} | ||
/** | ||
* Searches from the current node through its ancestry to find a matching node. | ||
* @param node a node to get. | ||
* @param match a callback that checks whether or not the node verifies its condition or not. | ||
* @returns the matching node. | ||
*/ | ||
function findSelfOrAncestor(node, match) { | ||
let currentNode = node; | ||
while (currentNode && !match(currentNode)) { | ||
currentNode = currentNode.parent; | ||
} | ||
return currentNode; | ||
} | ||
/** | ||
* Finds function's outer scope. | ||
* @param scope Function's own scope. | ||
* @returns Function's outer scope. | ||
*/ | ||
function getOuterScope(scope) { | ||
const upper = scope.upper; | ||
if ((upper === null || upper === void 0 ? void 0 : upper.type) === 'function-expression-name') { | ||
return upper.upper; | ||
} | ||
return upper; | ||
} | ||
/** | ||
* Checks if a variable and a shadowedVariable have the same init pattern ancestor. | ||
* @param variable a variable to check. | ||
* @param shadowedVariable a shadowedVariable to check. | ||
* @returns Whether or not the variable and the shadowedVariable have the same init pattern ancestor. | ||
*/ | ||
function isInitPatternNode(variable, shadowedVariable) { | ||
var _a, _b, _c, _d; | ||
const outerDef = shadowedVariable.defs[0]; | ||
if (!outerDef) { | ||
return false; | ||
} | ||
const { variableScope } = variable.scope; | ||
if (!((variableScope.block.type === | ||
utils_1.AST_NODE_TYPES.ArrowFunctionExpression || | ||
variableScope.block.type === utils_1.AST_NODE_TYPES.FunctionExpression) && | ||
getOuterScope(variableScope) === shadowedVariable.scope)) { | ||
return false; | ||
} | ||
const fun = variableScope.block; | ||
const { parent } = fun; | ||
const callExpression = findSelfOrAncestor(parent, node => node.type === utils_1.AST_NODE_TYPES.CallExpression); | ||
if (!callExpression) { | ||
return false; | ||
} | ||
let node = outerDef.name; | ||
const location = callExpression.range[1]; | ||
while (node) { | ||
if (node.type === utils_1.AST_NODE_TYPES.VariableDeclarator) { | ||
if (isInRange(node.init, location)) { | ||
return true; | ||
} | ||
if ((((_b = (_a = node.parent) === null || _a === void 0 ? void 0 : _a.parent) === null || _b === void 0 ? void 0 : _b.type) === utils_1.AST_NODE_TYPES.ForInStatement || | ||
((_d = (_c = node.parent) === null || _c === void 0 ? void 0 : _c.parent) === null || _d === void 0 ? void 0 : _d.type) === utils_1.AST_NODE_TYPES.ForOfStatement) && | ||
isInRange(node.parent.parent.right, location)) { | ||
return true; | ||
} | ||
break; | ||
} | ||
else if (node.type === utils_1.AST_NODE_TYPES.AssignmentPattern) { | ||
if (isInRange(node.right, location)) { | ||
return true; | ||
} | ||
} | ||
else if ([ | ||
utils_1.AST_NODE_TYPES.FunctionDeclaration, | ||
utils_1.AST_NODE_TYPES.ClassDeclaration, | ||
utils_1.AST_NODE_TYPES.FunctionExpression, | ||
utils_1.AST_NODE_TYPES.ClassExpression, | ||
utils_1.AST_NODE_TYPES.ArrowFunctionExpression, | ||
utils_1.AST_NODE_TYPES.CatchClause, | ||
utils_1.AST_NODE_TYPES.ImportDeclaration, | ||
utils_1.AST_NODE_TYPES.ExportNamedDeclaration, | ||
].includes(node.type)) { | ||
break; | ||
} | ||
node = node.parent; | ||
} | ||
return false; | ||
} | ||
/** | ||
* Checks if a variable is inside the initializer of scopeVar. | ||
@@ -179,5 +371,5 @@ * | ||
inner[1] < outer[1] && | ||
((innerDef.type === 'FunctionName' && | ||
innerDef.node.type === experimental_utils_1.AST_NODE_TYPES.FunctionExpression) || | ||
innerDef.node.type === experimental_utils_1.AST_NODE_TYPES.ClassExpression) && | ||
((innerDef.type === scope_manager_1.DefinitionType.FunctionName && | ||
innerDef.node.type === utils_1.AST_NODE_TYPES.FunctionExpression) || | ||
innerDef.node.type === utils_1.AST_NODE_TYPES.ClassExpression) && | ||
outerScope === innerScope.upper); | ||
@@ -210,20 +402,23 @@ } | ||
!outerDef || | ||
outerDef.node.type !== experimental_utils_1.AST_NODE_TYPES.FunctionDeclaration)); | ||
outerDef.node.type !== utils_1.AST_NODE_TYPES.FunctionDeclaration)); | ||
} | ||
/** | ||
* Finds the variable by a given name in a given scope and its upper scopes. | ||
* @param initScope A scope to start find. | ||
* @param name A variable name to find. | ||
* @returns A found variable or `null`. | ||
* Get declared line and column of a variable. | ||
* @param variable The variable to get. | ||
* @returns The declared line and column of the variable. | ||
*/ | ||
function getVariableByName(initScope, name) { | ||
let scope = initScope; | ||
while (scope) { | ||
const variable = scope.set.get(name); | ||
if (variable) { | ||
return variable; | ||
} | ||
scope = scope.upper; | ||
function getDeclaredLocation(variable) { | ||
const identifier = variable.identifiers[0]; | ||
if (identifier) { | ||
return { | ||
global: false, | ||
line: identifier.loc.start.line, | ||
column: identifier.loc.start.column + 1, | ||
}; | ||
} | ||
return null; | ||
else { | ||
return { | ||
global: true, | ||
}; | ||
} | ||
} | ||
@@ -262,3 +457,5 @@ /** | ||
// Gets shadowed variable. | ||
const shadowed = getVariableByName(scope.upper, variable.name); | ||
const shadowed = scope.upper | ||
? utils_1.ASTUtils.findVariable(scope.upper, variable.name) | ||
: null; | ||
if (!shadowed) { | ||
@@ -275,2 +472,11 @@ continue; | ||
} | ||
// ignore static class method generic shadowing class generic | ||
// this is impossible for the scope analyser to understand | ||
// so we have to handle this manually in this rule | ||
if (isGenericOfAStaticMethodShadow(variable, shadowed)) { | ||
continue; | ||
} | ||
if (isExternalDeclarationMerging(scope, variable, shadowed)) { | ||
continue; | ||
} | ||
const isESLintGlobal = 'writeable' in shadowed; | ||
@@ -280,10 +486,21 @@ if ((shadowed.identifiers.length > 0 || | ||
!isOnInitializer(variable, shadowed) && | ||
!(options.ignoreOnInitialization && | ||
isInitPatternNode(variable, shadowed)) && | ||
!(options.hoist !== 'all' && isInTdz(variable, shadowed))) { | ||
context.report({ | ||
node: variable.identifiers[0], | ||
messageId: 'noShadow', | ||
data: { | ||
name: variable.name, | ||
}, | ||
}); | ||
const location = getDeclaredLocation(shadowed); | ||
context.report(Object.assign({ node: variable.identifiers[0] }, (location.global | ||
? { | ||
messageId: 'noShadowGlobal', | ||
data: { | ||
name: variable.name, | ||
}, | ||
} | ||
: { | ||
messageId: 'noShadow', | ||
data: { | ||
name: variable.name, | ||
shadowedLine: location.line, | ||
shadowedColumn: location.column, | ||
}, | ||
}))); | ||
} | ||
@@ -290,0 +507,0 @@ } |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -31,3 +35,2 @@ exports.default = util.createRule({ | ||
description: 'Disallow aliasing `this`', | ||
category: 'Best Practices', | ||
recommended: 'error', | ||
@@ -41,5 +44,7 @@ }, | ||
allowDestructuring: { | ||
description: 'Whether to ignore destructurings, such as `const { props, state } = this`.', | ||
type: 'boolean', | ||
}, | ||
allowedNames: { | ||
description: 'Names to ignore, such as ["self"] for `const self = this;`.', | ||
type: 'array', | ||
@@ -66,8 +71,8 @@ items: { | ||
return { | ||
"VariableDeclarator[init.type='ThisExpression']"(node) { | ||
const { id } = node; | ||
if (allowDestructuring && id.type !== experimental_utils_1.AST_NODE_TYPES.Identifier) { | ||
"VariableDeclarator[init.type='ThisExpression'], AssignmentExpression[right.type='ThisExpression']"(node) { | ||
const id = node.type === utils_1.AST_NODE_TYPES.VariableDeclarator ? node.id : node.left; | ||
if (allowDestructuring && id.type !== utils_1.AST_NODE_TYPES.Identifier) { | ||
return; | ||
} | ||
const hasAllowedName = id.type === experimental_utils_1.AST_NODE_TYPES.Identifier | ||
const hasAllowedName = id.type === utils_1.AST_NODE_TYPES.Identifier | ||
? allowedNames.includes(id.name) | ||
@@ -78,3 +83,3 @@ : false; | ||
node: id, | ||
messageId: id.type === experimental_utils_1.AST_NODE_TYPES.Identifier | ||
messageId: id.type === utils_1.AST_NODE_TYPES.Identifier | ||
? 'thisAssignment' | ||
@@ -81,0 +86,0 @@ : 'thisDestructure', |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,5 +26,5 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const ts = __importStar(require("typescript")); | ||
const util = __importStar(require("../util")); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
exports.default = util.createRule({ | ||
@@ -32,8 +36,20 @@ name: 'no-throw-literal', | ||
description: 'Disallow throwing literals as exceptions', | ||
category: 'Best Practices', | ||
recommended: false, | ||
recommended: 'strict', | ||
extendsBaseRule: true, | ||
requiresTypeChecking: true, | ||
}, | ||
schema: [], | ||
schema: [ | ||
{ | ||
type: 'object', | ||
properties: { | ||
allowThrowingAny: { | ||
type: 'boolean', | ||
}, | ||
allowThrowingUnknown: { | ||
type: 'boolean', | ||
}, | ||
}, | ||
additionalProperties: false, | ||
}, | ||
], | ||
messages: { | ||
@@ -44,4 +60,9 @@ object: 'Expected an error object to be thrown.', | ||
}, | ||
defaultOptions: [], | ||
create(context) { | ||
defaultOptions: [ | ||
{ | ||
allowThrowingAny: true, | ||
allowThrowingUnknown: true, | ||
}, | ||
], | ||
create(context, [options]) { | ||
const parserServices = util.getParserServices(context); | ||
@@ -81,4 +102,4 @@ const program = parserServices.program; | ||
function checkThrowArgument(node) { | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.AwaitExpression || | ||
node.type === experimental_utils_1.AST_NODE_TYPES.YieldExpression) { | ||
if (node.type === utils_1.AST_NODE_TYPES.AwaitExpression || | ||
node.type === utils_1.AST_NODE_TYPES.YieldExpression) { | ||
return; | ||
@@ -92,7 +113,11 @@ } | ||
} | ||
if (util.isTypeAnyType(type) || | ||
util.isTypeUnknownType(type) || | ||
isErrorLike(type)) { | ||
if (options.allowThrowingAny && util.isTypeAnyType(type)) { | ||
return; | ||
} | ||
if (options.allowThrowingUnknown && util.isTypeUnknownType(type)) { | ||
return; | ||
} | ||
if (isErrorLike(type)) { | ||
return; | ||
} | ||
context.report({ node, messageId: 'object' }); | ||
@@ -99,0 +124,0 @@ } |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -37,4 +41,3 @@ const enumValues = [ | ||
docs: { | ||
description: 'Disallow the use of type aliases', | ||
category: 'Stylistic Issues', | ||
description: 'Disallow type aliases', | ||
// too opinionated to be recommended | ||
@@ -52,22 +55,33 @@ recommended: false, | ||
allowAliases: { | ||
description: 'Whether to allow direct one-to-one type aliases.', | ||
enum: enumValues, | ||
}, | ||
allowCallbacks: { | ||
description: 'Whether to allow type aliases for callbacks.', | ||
enum: ['always', 'never'], | ||
}, | ||
allowConditionalTypes: { | ||
description: 'Whether to allow type aliases for conditional types.', | ||
enum: ['always', 'never'], | ||
}, | ||
allowConstructors: { | ||
description: 'Whether to allow type aliases with constructors.', | ||
enum: ['always', 'never'], | ||
}, | ||
allowLiterals: { | ||
description: 'Whether to allow type aliases with object literal types.', | ||
enum: enumValues, | ||
}, | ||
allowMappedTypes: { | ||
description: 'Whether to allow type aliases with mapped types.', | ||
enum: enumValues, | ||
}, | ||
allowTupleTypes: { | ||
description: 'Whether to allow type aliases with tuple types.', | ||
enum: enumValues, | ||
}, | ||
allowGenerics: { | ||
description: 'Whether to allow type aliases with generic types.', | ||
enum: ['always', 'never'], | ||
}, | ||
}, | ||
@@ -87,5 +101,6 @@ additionalProperties: false, | ||
allowTupleTypes: 'never', | ||
allowGenerics: 'never', | ||
}, | ||
], | ||
create(context, [{ allowAliases, allowCallbacks, allowConditionalTypes, allowConstructors, allowLiterals, allowMappedTypes, allowTupleTypes, },]) { | ||
create(context, [{ allowAliases, allowCallbacks, allowConditionalTypes, allowConstructors, allowLiterals, allowMappedTypes, allowTupleTypes, allowGenerics, },]) { | ||
const unions = ['always', 'in-unions', 'in-unions-and-intersections']; | ||
@@ -103,7 +118,9 @@ const intersections = [ | ||
const aliasTypes = new Set([ | ||
experimental_utils_1.AST_NODE_TYPES.TSArrayType, | ||
experimental_utils_1.AST_NODE_TYPES.TSTypeReference, | ||
experimental_utils_1.AST_NODE_TYPES.TSLiteralType, | ||
experimental_utils_1.AST_NODE_TYPES.TSTypeQuery, | ||
experimental_utils_1.AST_NODE_TYPES.TSIndexedAccessType, | ||
utils_1.AST_NODE_TYPES.TSArrayType, | ||
utils_1.AST_NODE_TYPES.TSImportType, | ||
utils_1.AST_NODE_TYPES.TSTypeReference, | ||
utils_1.AST_NODE_TYPES.TSLiteralType, | ||
utils_1.AST_NODE_TYPES.TSTypeQuery, | ||
utils_1.AST_NODE_TYPES.TSIndexedAccessType, | ||
utils_1.AST_NODE_TYPES.TSTemplateLiteralType, | ||
]); | ||
@@ -119,5 +136,5 @@ /** | ||
(!isTopLevel && | ||
((compositionType === experimental_utils_1.AST_NODE_TYPES.TSUnionType && | ||
((compositionType === utils_1.AST_NODE_TYPES.TSUnionType && | ||
unions.includes(allowed)) || | ||
(compositionType === experimental_utils_1.AST_NODE_TYPES.TSIntersectionType && | ||
(compositionType === utils_1.AST_NODE_TYPES.TSIntersectionType && | ||
intersections.includes(allowed))))); | ||
@@ -147,3 +164,3 @@ } | ||
data: { | ||
compositionType: compositionType === experimental_utils_1.AST_NODE_TYPES.TSUnionType | ||
compositionType: compositionType === utils_1.AST_NODE_TYPES.TSUnionType | ||
? 'union' | ||
@@ -156,9 +173,9 @@ : 'intersection', | ||
const isValidTupleType = (type) => { | ||
if (type.node.type === experimental_utils_1.AST_NODE_TYPES.TSTupleType) { | ||
if (type.node.type === utils_1.AST_NODE_TYPES.TSTupleType) { | ||
return true; | ||
} | ||
if (type.node.type === experimental_utils_1.AST_NODE_TYPES.TSTypeOperator) { | ||
if (type.node.type === utils_1.AST_NODE_TYPES.TSTypeOperator) { | ||
if (['keyof', 'readonly'].includes(type.node.operator) && | ||
type.node.typeAnnotation && | ||
type.node.typeAnnotation.type === experimental_utils_1.AST_NODE_TYPES.TSTupleType) { | ||
type.node.typeAnnotation.type === utils_1.AST_NODE_TYPES.TSTupleType) { | ||
return true; | ||
@@ -169,2 +186,6 @@ } | ||
}; | ||
const isValidGeneric = (type) => { | ||
return (type.node.type === utils_1.AST_NODE_TYPES.TSTypeReference && | ||
type.node.typeParameters !== undefined); | ||
}; | ||
const checkAndReport = (optionValue, isTopLevel, type, label) => { | ||
@@ -183,3 +204,3 @@ if (optionValue === 'never' || | ||
function validateTypeAliases(type, isTopLevel = false) { | ||
if (type.node.type === experimental_utils_1.AST_NODE_TYPES.TSFunctionType) { | ||
if (type.node.type === utils_1.AST_NODE_TYPES.TSFunctionType) { | ||
// callback | ||
@@ -190,3 +211,3 @@ if (allowCallbacks === 'never') { | ||
} | ||
else if (type.node.type === experimental_utils_1.AST_NODE_TYPES.TSConditionalType) { | ||
else if (type.node.type === utils_1.AST_NODE_TYPES.TSConditionalType) { | ||
// conditional type | ||
@@ -197,3 +218,3 @@ if (allowConditionalTypes === 'never') { | ||
} | ||
else if (type.node.type === experimental_utils_1.AST_NODE_TYPES.TSConstructorType) { | ||
else if (type.node.type === utils_1.AST_NODE_TYPES.TSConstructorType) { | ||
if (allowConstructors === 'never') { | ||
@@ -203,7 +224,7 @@ reportError(type.node, type.compositionType, isTopLevel, 'Constructors'); | ||
} | ||
else if (type.node.type === experimental_utils_1.AST_NODE_TYPES.TSTypeLiteral) { | ||
else if (type.node.type === utils_1.AST_NODE_TYPES.TSTypeLiteral) { | ||
// literal object type | ||
checkAndReport(allowLiterals, isTopLevel, type, 'Literals'); | ||
} | ||
else if (type.node.type === experimental_utils_1.AST_NODE_TYPES.TSMappedType) { | ||
else if (type.node.type === utils_1.AST_NODE_TYPES.TSMappedType) { | ||
// mapped type | ||
@@ -216,10 +237,14 @@ checkAndReport(allowMappedTypes, isTopLevel, type, 'Mapped types'); | ||
} | ||
else if ( | ||
// eslint-disable-next-line @typescript-eslint/internal/prefer-ast-types-enum | ||
type.node.type.endsWith('Keyword') || | ||
else if (isValidGeneric(type)) { | ||
if (allowGenerics === 'never') { | ||
reportError(type.node, type.compositionType, isTopLevel, 'Generics'); | ||
} | ||
} | ||
else if (type.node.type.endsWith(utils_1.AST_TOKEN_TYPES.Keyword) || | ||
aliasTypes.has(type.node.type) || | ||
(type.node.type === experimental_utils_1.AST_NODE_TYPES.TSTypeOperator && | ||
type.node.operator === 'readonly' && | ||
type.node.typeAnnotation && | ||
aliasTypes.has(type.node.typeAnnotation.type))) { | ||
(type.node.type === utils_1.AST_NODE_TYPES.TSTypeOperator && | ||
(type.node.operator === 'keyof' || | ||
(type.node.operator === 'readonly' && | ||
type.node.typeAnnotation && | ||
aliasTypes.has(type.node.typeAnnotation.type))))) { | ||
// alias / keyword | ||
@@ -237,4 +262,4 @@ checkAndReport(allowAliases, isTopLevel, type, 'Aliases'); | ||
function getTypes(node, compositionType = null) { | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.TSUnionType || | ||
node.type === experimental_utils_1.AST_NODE_TYPES.TSIntersectionType) { | ||
if (node.type === utils_1.AST_NODE_TYPES.TSUnionType || | ||
node.type === utils_1.AST_NODE_TYPES.TSIntersectionType) { | ||
return node.types.reduce((acc, type) => { | ||
@@ -245,5 +270,2 @@ acc.push(...getTypes(type, node.type)); | ||
} | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.TSParenthesizedType) { | ||
return getTypes(node.typeAnnotation, compositionType); | ||
} | ||
return [{ node, compositionType }]; | ||
@@ -250,0 +272,0 @@ } |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const tsutils = __importStar(require("tsutils")); | ||
@@ -31,5 +35,4 @@ const ts = __importStar(require("typescript")); | ||
docs: { | ||
description: 'Flags unnecessary equality comparisons against boolean literals', | ||
category: 'Stylistic Issues', | ||
recommended: false, | ||
description: 'Disallow unnecessary equality comparisons against boolean literals', | ||
recommended: 'strict', | ||
requiresTypeChecking: true, | ||
@@ -50,5 +53,7 @@ }, | ||
allowComparingNullableBooleansToTrue: { | ||
description: 'Whether to allow comparisons between nullable boolean variables and `true`.', | ||
type: 'boolean', | ||
}, | ||
allowComparingNullableBooleansToFalse: { | ||
description: 'Whether to allow comparisons between nullable boolean variables and `false`.', | ||
type: 'boolean', | ||
@@ -71,2 +76,3 @@ }, | ||
const checker = parserServices.program.getTypeChecker(); | ||
const sourceCode = context.getSourceCode(); | ||
function getBooleanComparison(node) { | ||
@@ -116,3 +122,3 @@ const comparison = deconstructComparison(node); | ||
function deconstructComparison(node) { | ||
const comparisonType = util.getEqualsKind(node.operator); | ||
const comparisonType = getEqualsKind(node.operator); | ||
if (!comparisonType) { | ||
@@ -125,3 +131,3 @@ return undefined; | ||
]) { | ||
if (against.type !== experimental_utils_1.AST_NODE_TYPES.Literal || | ||
if (against.type !== utils_1.AST_NODE_TYPES.Literal || | ||
typeof against.value !== 'boolean') { | ||
@@ -134,8 +140,4 @@ continue; | ||
literalBooleanInComparison, | ||
forTruthy: literalBooleanInComparison ? !negated : negated, | ||
expression, | ||
negated, | ||
range: expression.range[0] < against.range[0] | ||
? [expression.range[1], against.range[1]] | ||
: [against.range[1], expression.range[1]], | ||
}; | ||
@@ -146,3 +148,3 @@ } | ||
function nodeIsUnaryNegation(node) { | ||
return (node.type === experimental_utils_1.AST_NODE_TYPES.UnaryExpression && | ||
return (node.type === utils_1.AST_NODE_TYPES.UnaryExpression && | ||
node.prefix && | ||
@@ -169,32 +171,25 @@ node.operator === '!'); | ||
fix: function* (fixer) { | ||
yield fixer.removeRange(comparison.range); | ||
// if the expression `exp` isn't nullable, or we're comparing to `true`, | ||
// we can just replace the entire comparison with `exp` or `!exp` | ||
if (!comparison.expressionIsNullableBoolean || | ||
comparison.literalBooleanInComparison) { | ||
if (!comparison.forTruthy) { | ||
yield fixer.insertTextBefore(node, '!'); | ||
// 1. isUnaryNegation - parent negation | ||
// 2. literalBooleanInComparison - is compared to literal boolean | ||
// 3. negated - is expression negated | ||
const isUnaryNegation = node.parent != null && nodeIsUnaryNegation(node.parent); | ||
const shouldNegate = comparison.negated !== comparison.literalBooleanInComparison; | ||
const mutatedNode = isUnaryNegation ? node.parent : node; | ||
yield fixer.replaceText(mutatedNode, sourceCode.getText(comparison.expression)); | ||
// if `isUnaryNegation === literalBooleanInComparison === !negated` is true - negate the expression | ||
if (shouldNegate === isUnaryNegation) { | ||
yield fixer.insertTextBefore(mutatedNode, '!'); | ||
// if the expression `exp` is not a strong precedence node, wrap it in parentheses | ||
if (!util.isStrongPrecedenceNode(comparison.expression)) { | ||
yield fixer.insertTextBefore(mutatedNode, '('); | ||
yield fixer.insertTextAfter(mutatedNode, ')'); | ||
} | ||
return; | ||
} | ||
// if we're here, then the expression is a nullable boolean and we're | ||
// comparing to a literal `false` | ||
// if we're doing `== false` or `=== false`, then we need to negate the expression | ||
if (!comparison.negated) { | ||
const { parent } = node; | ||
// if the parent is a negation, we can instead just get rid of the parent's negation. | ||
// i.e. instead of resulting in `!(!(exp))`, we can just result in `exp` | ||
if (parent != null && nodeIsUnaryNegation(parent)) { | ||
// remove from the beginning of the parent to the beginning of this node | ||
yield fixer.removeRange([parent.range[0], node.range[0]]); | ||
// remove from the end of the node to the end of the parent | ||
yield fixer.removeRange([node.range[1], parent.range[1]]); | ||
} | ||
else { | ||
yield fixer.insertTextBefore(node, '!'); | ||
} | ||
// if the expression `exp` is nullable, and we're not comparing to `true`, insert `?? true` | ||
if (comparison.expressionIsNullableBoolean && | ||
!comparison.literalBooleanInComparison) { | ||
// provide the default `true` | ||
yield fixer.insertTextBefore(mutatedNode, '('); | ||
yield fixer.insertTextAfter(mutatedNode, ' ?? true)'); | ||
} | ||
// provide the default `true` | ||
yield fixer.insertTextBefore(node, '('); | ||
yield fixer.insertTextAfter(node, ' ?? true)'); | ||
}, | ||
@@ -216,2 +211,28 @@ messageId: comparison.expressionIsNullableBoolean | ||
}); | ||
function getEqualsKind(operator) { | ||
switch (operator) { | ||
case '==': | ||
return { | ||
isPositive: true, | ||
isStrict: false, | ||
}; | ||
case '===': | ||
return { | ||
isPositive: true, | ||
isStrict: true, | ||
}; | ||
case '!=': | ||
return { | ||
isPositive: false, | ||
isStrict: false, | ||
}; | ||
case '!==': | ||
return { | ||
isPositive: false, | ||
isStrict: true, | ||
}; | ||
default: | ||
return undefined; | ||
} | ||
} | ||
//# sourceMappingURL=no-unnecessary-boolean-literal-compare.js.map |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,28 +26,28 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const tsutils_1 = require("tsutils"); | ||
const ts = __importStar(require("typescript")); | ||
const tsutils_1 = require("tsutils"); | ||
const util_1 = require("../util"); | ||
// Truthiness utilities | ||
// #region | ||
const isTruthyLiteral = (type) => tsutils_1.isBooleanLiteralType(type, true) || (tsutils_1.isLiteralType(type) && !!type.value); | ||
const isPossiblyFalsy = (type) => tsutils_1.unionTypeParts(type) | ||
const isTruthyLiteral = (type) => (0, tsutils_1.isBooleanLiteralType)(type, true) || ((0, tsutils_1.isLiteralType)(type) && !!type.value); | ||
const isPossiblyFalsy = (type) => (0, tsutils_1.unionTypeParts)(type) | ||
// PossiblyFalsy flag includes literal values, so exclude ones that | ||
// are definitely truthy | ||
.filter(t => !isTruthyLiteral(t)) | ||
.some(type => util_1.isTypeFlagSet(type, ts.TypeFlags.PossiblyFalsy)); | ||
const isPossiblyTruthy = (type) => tsutils_1.unionTypeParts(type).some(type => !tsutils_1.isFalsyType(type)); | ||
.some(type => (0, util_1.isTypeFlagSet)(type, ts.TypeFlags.PossiblyFalsy)); | ||
const isPossiblyTruthy = (type) => (0, tsutils_1.unionTypeParts)(type).some(type => !(0, tsutils_1.isFalsyType)(type)); | ||
// Nullish utilities | ||
const nullishFlag = ts.TypeFlags.Undefined | ts.TypeFlags.Null; | ||
const isNullishType = (type) => util_1.isTypeFlagSet(type, nullishFlag); | ||
const isPossiblyNullish = (type) => tsutils_1.unionTypeParts(type).some(isNullishType); | ||
const isAlwaysNullish = (type) => tsutils_1.unionTypeParts(type).every(isNullishType); | ||
const isNullishType = (type) => (0, util_1.isTypeFlagSet)(type, nullishFlag); | ||
const isPossiblyNullish = (type) => (0, tsutils_1.unionTypeParts)(type).some(isNullishType); | ||
const isAlwaysNullish = (type) => (0, tsutils_1.unionTypeParts)(type).every(isNullishType); | ||
// isLiteralType only covers numbers and strings, this is a more exhaustive check. | ||
const isLiteral = (type) => tsutils_1.isBooleanLiteralType(type, true) || | ||
tsutils_1.isBooleanLiteralType(type, false) || | ||
const isLiteral = (type) => (0, tsutils_1.isBooleanLiteralType)(type, true) || | ||
(0, tsutils_1.isBooleanLiteralType)(type, false) || | ||
type.flags === ts.TypeFlags.Undefined || | ||
type.flags === ts.TypeFlags.Null || | ||
type.flags === ts.TypeFlags.Void || | ||
tsutils_1.isLiteralType(type); | ||
exports.default = util_1.createRule({ | ||
(0, tsutils_1.isLiteralType)(type); | ||
exports.default = (0, util_1.createRule)({ | ||
name: 'no-unnecessary-condition', | ||
@@ -53,5 +57,4 @@ meta: { | ||
docs: { | ||
description: 'Prevents conditionals where the type is always truthy or always falsy', | ||
category: 'Best Practices', | ||
recommended: false, | ||
description: 'Disallow conditionals where the type is always truthy or always falsy', | ||
recommended: 'strict', | ||
requiresTypeChecking: true, | ||
@@ -64,5 +67,7 @@ }, | ||
allowConstantLoopConditions: { | ||
description: 'Whether to ignore constant loop conditions, such as `while (true)`.', | ||
type: 'boolean', | ||
}, | ||
allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: { | ||
description: 'Whether to not error when running with a tsconfig that has strictNullChecks turned.', | ||
type: 'boolean', | ||
@@ -82,6 +87,6 @@ }, | ||
alwaysNullish: 'Unnecessary conditional, left-hand side of `??` operator is always `null` or `undefined`.', | ||
literalBooleanExpression: 'Unnecessary conditional, both sides of the expression are literal values', | ||
noOverlapBooleanExpression: 'Unnecessary conditional, the types have no overlap', | ||
never: 'Unnecessary conditional, value is `never`', | ||
neverOptionalChain: 'Unnecessary optional chain on a non-nullish value', | ||
literalBooleanExpression: 'Unnecessary conditional, both sides of the expression are literal values.', | ||
noOverlapBooleanExpression: 'Unnecessary conditional, the types have no overlap.', | ||
never: 'Unnecessary conditional, value is `never`.', | ||
neverOptionalChain: 'Unnecessary optional chain on a non-nullish value.', | ||
noStrictNullCheck: 'This rule requires the `strictNullChecks` compiler option to be turned on to function correctly.', | ||
@@ -97,7 +102,7 @@ }, | ||
create(context, [{ allowConstantLoopConditions, allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing, },]) { | ||
const service = util_1.getParserServices(context); | ||
const service = (0, util_1.getParserServices)(context); | ||
const checker = service.program.getTypeChecker(); | ||
const sourceCode = context.getSourceCode(); | ||
const compilerOptions = service.program.getCompilerOptions(); | ||
const isStrictNullChecks = tsutils_1.isStrictCompilerOptionEnabled(compilerOptions, 'strictNullChecks'); | ||
const isStrictNullChecks = (0, tsutils_1.isStrictCompilerOptionEnabled)(compilerOptions, 'strictNullChecks'); | ||
if (!isStrictNullChecks && | ||
@@ -115,3 +120,3 @@ allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing !== true) { | ||
const tsNode = service.esTreeNodeToTSNodeMap.get(node); | ||
return util_1.getConstrainedTypeAtLocation(checker, tsNode); | ||
return (0, util_1.getConstrainedTypeAtLocation)(checker, tsNode); | ||
} | ||
@@ -129,3 +134,3 @@ function nodeIsArrayType(node) { | ||
// Is an index signature | ||
node.type === experimental_utils_1.AST_NODE_TYPES.MemberExpression && | ||
node.type === utils_1.AST_NODE_TYPES.MemberExpression && | ||
node.computed && | ||
@@ -137,3 +142,3 @@ // ...into an array type | ||
// Exception: literal index into a tuple - will have a sound type | ||
node.property.type !== experimental_utils_1.AST_NODE_TYPES.Literal))); | ||
node.property.type !== utils_1.AST_NODE_TYPES.Literal))); | ||
} | ||
@@ -146,3 +151,3 @@ /** | ||
// Check if the node is Unary Negation expression and handle it | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.UnaryExpression && | ||
if (node.type === utils_1.AST_NODE_TYPES.UnaryExpression && | ||
node.operator === '!') { | ||
@@ -163,3 +168,3 @@ return checkNode(node.argument, true); | ||
// In this case it's better to inspect the type of the expression as a whole. | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.LogicalExpression && | ||
if (node.type === utils_1.AST_NODE_TYPES.LogicalExpression && | ||
node.operator !== '??') { | ||
@@ -170,10 +175,10 @@ return checkNode(node.right); | ||
// Conditional is always necessary if it involves: | ||
// `any` or `unknown` or a naked type parameter | ||
if (tsutils_1.unionTypeParts(type).some(part => util_1.isTypeAnyType(part) || | ||
util_1.isTypeUnknownType(part) || | ||
util_1.isTypeFlagSet(part, ts.TypeFlags.TypeParameter))) { | ||
// `any` or `unknown` or a naked type variable | ||
if ((0, tsutils_1.unionTypeParts)(type).some(part => (0, util_1.isTypeAnyType)(part) || | ||
(0, util_1.isTypeUnknownType)(part) || | ||
(0, util_1.isTypeFlagSet)(part, ts.TypeFlags.TypeVariable))) { | ||
return; | ||
} | ||
let messageId = null; | ||
if (util_1.isTypeFlagSet(type, ts.TypeFlags.Never)) { | ||
if ((0, util_1.isTypeFlagSet)(type, ts.TypeFlags.Never)) { | ||
messageId = 'never'; | ||
@@ -192,19 +197,21 @@ } | ||
function checkNodeForNullish(node) { | ||
// Since typescript array index signature types don't represent the | ||
// possibility of out-of-bounds access, if we're indexing into an array | ||
// just skip the check, to avoid false positives | ||
if (isArrayIndexExpression(node)) { | ||
return; | ||
} | ||
const type = getNodeType(node); | ||
// Conditional is always necessary if it involves `any` or `unknown` | ||
if (util_1.isTypeAnyType(type) || util_1.isTypeUnknownType(type)) { | ||
// Conditional is always necessary if it involves `any`, `unknown` or a naked type parameter | ||
if ((0, util_1.isTypeFlagSet)(type, ts.TypeFlags.Any | ts.TypeFlags.Unknown | ts.TypeFlags.TypeParameter)) { | ||
return; | ||
} | ||
let messageId = null; | ||
if (util_1.isTypeFlagSet(type, ts.TypeFlags.Never)) { | ||
if ((0, util_1.isTypeFlagSet)(type, ts.TypeFlags.Never)) { | ||
messageId = 'never'; | ||
} | ||
else if (!isPossiblyNullish(type)) { | ||
messageId = 'neverNullish'; | ||
// Since typescript array index signature types don't represent the | ||
// possibility of out-of-bounds access, if we're indexing into an array | ||
// just skip the check, to avoid false positives | ||
if (!isArrayIndexExpression(node) && | ||
!(node.type === utils_1.AST_NODE_TYPES.ChainExpression && | ||
node.expression.type !== utils_1.AST_NODE_TYPES.TSNonNullExpression && | ||
optionChainContainsOptionArrayIndex(node.expression))) { | ||
messageId = 'neverNullish'; | ||
} | ||
} | ||
@@ -252,2 +259,3 @@ else if (isAlwaysNullish(type)) { | ||
const NULL = ts.TypeFlags.Null; | ||
const VOID = ts.TypeFlags.Void; | ||
const isComparable = (type, flag) => { | ||
@@ -261,10 +269,10 @@ // Allow comparison to `any`, `unknown` or a naked type parameter. | ||
if (node.operator === '==' || node.operator === '!=') { | ||
flag |= NULL | UNDEFINED; | ||
flag |= NULL | UNDEFINED | VOID; | ||
} | ||
return util_1.isTypeFlagSet(type, flag); | ||
return (0, util_1.isTypeFlagSet)(type, flag); | ||
}; | ||
if ((leftType.flags === UNDEFINED && | ||
!isComparable(rightType, UNDEFINED)) || | ||
!isComparable(rightType, UNDEFINED | VOID)) || | ||
(rightType.flags === UNDEFINED && | ||
!isComparable(leftType, UNDEFINED)) || | ||
!isComparable(leftType, UNDEFINED | VOID)) || | ||
(leftType.flags === NULL && !isComparable(rightType, NULL)) || | ||
@@ -293,3 +301,3 @@ (rightType.flags === NULL && !isComparable(leftType, NULL))) { | ||
function checkIfLoopIsNecessaryConditional(node) { | ||
if (node.test === null) { | ||
if (node.test == null) { | ||
// e.g. `for(;;)` | ||
@@ -305,3 +313,3 @@ return; | ||
if (allowConstantLoopConditions && | ||
tsutils_1.isBooleanLiteralType(getNodeType(node.test), true)) { | ||
(0, tsutils_1.isBooleanLiteralType)(getNodeType(node.test), true)) { | ||
return; | ||
@@ -321,4 +329,4 @@ } | ||
// looks like `something.filter` or `something.find` | ||
callee.type === experimental_utils_1.AST_NODE_TYPES.MemberExpression && | ||
callee.property.type === experimental_utils_1.AST_NODE_TYPES.Identifier && | ||
callee.type === utils_1.AST_NODE_TYPES.MemberExpression && | ||
callee.property.type === utils_1.AST_NODE_TYPES.Identifier && | ||
ARRAY_PREDICATE_FUNCTIONS.has(callee.property.name) && | ||
@@ -333,8 +341,8 @@ // and the left-hand side is an array, according to the types | ||
// Inline defined functions | ||
if ((callback.type === experimental_utils_1.AST_NODE_TYPES.ArrowFunctionExpression || | ||
callback.type === experimental_utils_1.AST_NODE_TYPES.FunctionExpression) && | ||
if ((callback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression || | ||
callback.type === utils_1.AST_NODE_TYPES.FunctionExpression) && | ||
callback.body) { | ||
// Two special cases, where we can directly check the node that's returned: | ||
// () => something | ||
if (callback.body.type !== experimental_utils_1.AST_NODE_TYPES.BlockStatement) { | ||
if (callback.body.type !== utils_1.AST_NODE_TYPES.BlockStatement) { | ||
return checkNode(callback.body); | ||
@@ -345,3 +353,3 @@ } | ||
if (callbackBody.length === 1 && | ||
callbackBody[0].type === experimental_utils_1.AST_NODE_TYPES.ReturnStatement && | ||
callbackBody[0].type === utils_1.AST_NODE_TYPES.ReturnStatement && | ||
callbackBody[0].argument) { | ||
@@ -355,3 +363,3 @@ return checkNode(callbackBody[0].argument); | ||
// Otherwise just do type analysis on the function as a whole. | ||
const returnTypes = tsutils_1.getCallSignaturesOfType(getNodeType(callback)).map(sig => sig.getReturnType()); | ||
const returnTypes = (0, tsutils_1.getCallSignaturesOfType)(getNodeType(callback)).map(sig => sig.getReturnType()); | ||
/* istanbul ignore if */ if (returnTypes.length === 0) { | ||
@@ -362,3 +370,3 @@ // Not a callable function | ||
// Predicate is always necessary if it involves `any` or `unknown` | ||
if (returnTypes.some(t => util_1.isTypeAnyType(t) || util_1.isTypeUnknownType(t))) { | ||
if (returnTypes.some(t => (0, util_1.isTypeAnyType)(t) || (0, util_1.isTypeUnknownType)(t))) { | ||
return; | ||
@@ -388,10 +396,10 @@ } | ||
// ``` | ||
function optionChainContainsArrayIndex(node) { | ||
const lhsNode = node.type === experimental_utils_1.AST_NODE_TYPES.CallExpression ? node.callee : node.object; | ||
if (isArrayIndexExpression(lhsNode)) { | ||
function optionChainContainsOptionArrayIndex(node) { | ||
const lhsNode = node.type === utils_1.AST_NODE_TYPES.CallExpression ? node.callee : node.object; | ||
if (node.optional && isArrayIndexExpression(lhsNode)) { | ||
return true; | ||
} | ||
if (lhsNode.type === experimental_utils_1.AST_NODE_TYPES.MemberExpression || | ||
lhsNode.type === experimental_utils_1.AST_NODE_TYPES.CallExpression) { | ||
return optionChainContainsArrayIndex(lhsNode); | ||
if (lhsNode.type === utils_1.AST_NODE_TYPES.MemberExpression || | ||
lhsNode.type === utils_1.AST_NODE_TYPES.CallExpression) { | ||
return optionChainContainsOptionArrayIndex(lhsNode); | ||
} | ||
@@ -405,8 +413,8 @@ return false; | ||
if (propertyType.isNumberLiteral() || propertyType.isStringLiteral()) { | ||
const propType = util_1.getTypeOfPropertyOfName(checker, objType, propertyType.value.toString()); | ||
const propType = (0, util_1.getTypeOfPropertyOfName)(checker, objType, propertyType.value.toString()); | ||
if (propType) { | ||
return util_1.isNullableType(propType, { allowUndefined: true }); | ||
return (0, util_1.isNullableType)(propType, { allowUndefined: true }); | ||
} | ||
} | ||
const typeName = util_1.getTypeName(checker, propertyType); | ||
const typeName = (0, util_1.getTypeName)(checker, propertyType); | ||
return !!((typeName === 'string' && | ||
@@ -428,3 +436,3 @@ checker.getIndexInfoOfType(objType, ts.IndexKind.String)) || | ||
const property = node.property; | ||
if (prevType.isUnion() && util_1.isIdentifier(property)) { | ||
if (prevType.isUnion() && (0, util_1.isIdentifier)(property)) { | ||
const isOwnNullable = prevType.types.some(type => { | ||
@@ -435,6 +443,9 @@ if (node.computed) { | ||
} | ||
const propType = util_1.getTypeOfPropertyOfName(checker, type, property.name); | ||
return propType && util_1.isNullableType(propType, { allowUndefined: true }); | ||
const propType = (0, util_1.getTypeOfPropertyOfName)(checker, type, property.name); | ||
if (propType) { | ||
return (0, util_1.isNullableType)(propType, { allowUndefined: true }); | ||
} | ||
return !!checker.getIndexInfoOfType(type, ts.IndexKind.String); | ||
}); | ||
return (!isOwnNullable && util_1.isNullableType(prevType, { allowUndefined: true })); | ||
return (!isOwnNullable && (0, util_1.isNullableType)(prevType, { allowUndefined: true })); | ||
} | ||
@@ -445,8 +456,9 @@ return false; | ||
const type = getNodeType(node); | ||
const isOwnNullable = node.type === experimental_utils_1.AST_NODE_TYPES.MemberExpression | ||
const isOwnNullable = node.type === utils_1.AST_NODE_TYPES.MemberExpression | ||
? !isNullableOriginFromPrev(node) | ||
: true; | ||
return (util_1.isTypeAnyType(type) || | ||
util_1.isTypeUnknownType(type) || | ||
(util_1.isNullableType(type, { allowUndefined: true }) && isOwnNullable)); | ||
const possiblyVoid = (0, util_1.isTypeFlagSet)(type, ts.TypeFlags.Void); | ||
return ((0, util_1.isTypeFlagSet)(type, ts.TypeFlags.Any | ts.TypeFlags.Unknown) || | ||
(isOwnNullable && | ||
((0, util_1.isNullableType)(type, { allowUndefined: true }) || possiblyVoid))); | ||
} | ||
@@ -462,10 +474,10 @@ function checkOptionalChain(node, beforeOperator, fix) { | ||
// just skip the check, to avoid false positives | ||
if (optionChainContainsArrayIndex(node)) { | ||
if (optionChainContainsOptionArrayIndex(node)) { | ||
return; | ||
} | ||
const nodeToCheck = node.type === experimental_utils_1.AST_NODE_TYPES.CallExpression ? node.callee : node.object; | ||
const nodeToCheck = node.type === utils_1.AST_NODE_TYPES.CallExpression ? node.callee : node.object; | ||
if (isOptionableExpression(nodeToCheck)) { | ||
return; | ||
} | ||
const questionDotOperator = util_1.nullThrows(sourceCode.getTokenAfter(beforeOperator, token => token.type === experimental_utils_1.AST_TOKEN_TYPES.Punctuator && token.value === '?.'), util_1.NullThrowsReasons.MissingToken('operator', node.type)); | ||
const questionDotOperator = (0, util_1.nullThrows)(sourceCode.getTokenAfter(beforeOperator, token => token.type === utils_1.AST_TOKEN_TYPES.Punctuator && token.value === '?.'), util_1.NullThrowsReasons.MissingToken('operator', node.type)); | ||
context.report({ | ||
@@ -486,3 +498,14 @@ node, | ||
} | ||
function checkAssignmentExpression(node) { | ||
// Similar to checkLogicalExpressionForUnnecessaryConditionals, since | ||
// a ||= b is equivalent to a || (a = b) | ||
if (['||=', '&&='].includes(node.operator)) { | ||
checkNode(node.left); | ||
} | ||
else if (node.operator === '??=') { | ||
checkNodeForNullish(node.left); | ||
} | ||
} | ||
return { | ||
AssignmentExpression: checkAssignmentExpression, | ||
BinaryExpression: checkIfBinaryExpressionIsNecessaryConditional, | ||
@@ -489,0 +512,0 @@ CallExpression: checkCallExpression, |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,5 +26,5 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const tsutils = __importStar(require("tsutils")); | ||
const ts = __importStar(require("typescript")); | ||
const tsutils = __importStar(require("tsutils")); | ||
const util = __importStar(require("../util")); | ||
@@ -31,4 +35,3 @@ exports.default = util.createRule({ | ||
docs: { | ||
category: 'Best Practices', | ||
description: 'Warns when a namespace qualifier is unnecessary', | ||
description: 'Disallow unnecessary namespace qualifiers', | ||
recommended: false, | ||
@@ -65,3 +68,3 @@ requiresTypeChecking: true, | ||
const alias = tryGetAliasedSymbol(symbol, checker); | ||
return alias !== null && symbolIsNamespaceInScope(alias); | ||
return alias != null && symbolIsNamespaceInScope(alias); | ||
} | ||
@@ -80,3 +83,3 @@ function getSymbolInScope(node, flags, name) { | ||
const namespaceSymbol = checker.getSymbolAtLocation(tsQualifier); | ||
if (typeof namespaceSymbol === 'undefined' || | ||
if (namespaceSymbol === undefined || | ||
!symbolIsNamespaceInScope(namespaceSymbol)) { | ||
@@ -86,3 +89,3 @@ return false; | ||
const accessedSymbol = checker.getSymbolAtLocation(tsName); | ||
if (typeof accessedSymbol === 'undefined') { | ||
if (accessedSymbol === undefined) { | ||
return false; | ||
@@ -92,4 +95,3 @@ } | ||
const fromScope = getSymbolInScope(tsQualifier, accessedSymbol.flags, sourceCode.getText(name)); | ||
return (typeof fromScope === 'undefined' || | ||
symbolsAreEqual(accessedSymbol, fromScope)); | ||
return (fromScope === undefined || symbolsAreEqual(accessedSymbol, fromScope)); | ||
} | ||
@@ -125,6 +127,6 @@ function visitNamespaceAccess(node, qualifier, name) { | ||
function isPropertyAccessExpression(node) { | ||
return node.type === experimental_utils_1.AST_NODE_TYPES.MemberExpression && !node.computed; | ||
return node.type === utils_1.AST_NODE_TYPES.MemberExpression && !node.computed; | ||
} | ||
function isEntityNameExpression(node) { | ||
return (node.type === experimental_utils_1.AST_NODE_TYPES.Identifier || | ||
return (node.type === utils_1.AST_NODE_TYPES.Identifier || | ||
(isPropertyAccessExpression(node) && | ||
@@ -131,0 +133,0 @@ isEntityNameExpression(node.object))); |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -30,5 +34,4 @@ if (k2 === undefined) k2 = k; | ||
docs: { | ||
description: 'Enforces that type arguments will not be used if not required', | ||
category: 'Best Practices', | ||
recommended: false, | ||
description: 'Disallow type arguments that are equal to the default', | ||
recommended: 'strict', | ||
requiresTypeChecking: true, | ||
@@ -47,3 +50,14 @@ }, | ||
const checker = parserServices.program.getTypeChecker(); | ||
const sourceCode = context.getSourceCode(); | ||
function getTypeForComparison(type) { | ||
if (util.isTypeReferenceType(type)) { | ||
return { | ||
type: type.target, | ||
typeArguments: util.getTypeArguments(type, checker), | ||
}; | ||
} | ||
return { | ||
type, | ||
typeArguments: [], | ||
}; | ||
} | ||
function checkTSArgsAndParameters(esParameters, typeParameters) { | ||
@@ -54,7 +68,26 @@ // Just check the last one. Must specify previous type parameters if the last one is specified. | ||
const param = typeParameters[i]; | ||
// TODO: would like checker.areTypesEquivalent. https://github.com/Microsoft/TypeScript/issues/13502 | ||
if (!(param === null || param === void 0 ? void 0 : param.default) || | ||
param.default.getText() !== sourceCode.getText(arg)) { | ||
if (!(param === null || param === void 0 ? void 0 : param.default)) { | ||
return; | ||
} | ||
// TODO: would like checker.areTypesEquivalent. https://github.com/Microsoft/TypeScript/issues/13502 | ||
const defaultType = checker.getTypeAtLocation(param.default); | ||
const argTsNode = parserServices.esTreeNodeToTSNodeMap.get(arg); | ||
const argType = checker.getTypeAtLocation(argTsNode); | ||
// this check should handle some of the most simple cases of like strings, numbers, etc | ||
if (defaultType !== argType) { | ||
// For more complex types (like aliases to generic object types) - TS won't always create a | ||
// global shared type object for the type - so we need to resort to manually comparing the | ||
// reference type and the passed type arguments. | ||
// Also - in case there are aliases - we need to resolve them before we do checks | ||
const defaultTypeResolved = getTypeForComparison(defaultType); | ||
const argTypeResolved = getTypeForComparison(argType); | ||
if ( | ||
// ensure the resolved type AND all the parameters are the same | ||
defaultTypeResolved.type !== argTypeResolved.type || | ||
defaultTypeResolved.typeArguments.length !== | ||
argTypeResolved.typeArguments.length || | ||
defaultTypeResolved.typeArguments.some((t, i) => t !== argTypeResolved.typeArguments[i])) { | ||
return; | ||
} | ||
} | ||
context.report({ | ||
@@ -101,3 +134,3 @@ node: arg, | ||
} | ||
return util_1.findFirstResult(declarations, decl => tsutils.isClassLikeDeclaration(decl) || | ||
return (0, util_1.findFirstResult)(declarations, decl => ts.isClassLike(decl) || | ||
ts.isTypeAliasDeclaration(decl) || | ||
@@ -104,0 +137,0 @@ ts.isInterfaceDeclaration(decl) |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const tsutils_1 = require("tsutils"); | ||
@@ -31,4 +35,3 @@ const ts = __importStar(require("typescript")); | ||
docs: { | ||
description: 'Warns if a type assertion does not change the type of an expression', | ||
category: 'Best Practices', | ||
description: 'Disallow type assertions that do not change the type of an expression', | ||
recommended: 'error', | ||
@@ -47,2 +50,3 @@ requiresTypeChecking: true, | ||
typesToIgnore: { | ||
description: 'A list of type names to ignore.', | ||
type: 'array', | ||
@@ -102,6 +106,6 @@ items: { | ||
// non-strict mode doesn't care about used before assigned errors | ||
tsutils_1.isStrictCompilerOptionEnabled(compilerOptions, 'strictNullChecks') && | ||
(0, tsutils_1.isStrictCompilerOptionEnabled)(compilerOptions, 'strictNullChecks') && | ||
// ignore class properties as they are compile time guarded | ||
// also ignore function arguments as they can't be used before defined | ||
tsutils_1.isVariableDeclaration(declaration) && | ||
(0, tsutils_1.isVariableDeclaration)(declaration) && | ||
// is it `const x!: number` | ||
@@ -126,4 +130,4 @@ declaration.initializer === undefined && | ||
function isConstAssertion(node) { | ||
return (node.type === experimental_utils_1.AST_NODE_TYPES.TSTypeReference && | ||
node.typeName.type === experimental_utils_1.AST_NODE_TYPES.Identifier && | ||
return (node.type === utils_1.AST_NODE_TYPES.TSTypeReference && | ||
node.typeName.type === utils_1.AST_NODE_TYPES.Identifier && | ||
node.typeName.name === 'const'); | ||
@@ -133,2 +137,23 @@ } | ||
TSNonNullExpression(node) { | ||
var _a; | ||
if (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.AssignmentExpression && | ||
node.parent.operator === '=') { | ||
if (node.parent.left === node) { | ||
context.report({ | ||
node, | ||
messageId: 'contextuallyUnnecessary', | ||
fix(fixer) { | ||
return fixer.removeRange([ | ||
node.expression.range[1], | ||
node.range[1], | ||
]); | ||
}, | ||
}); | ||
} | ||
// for all other = assignments we ignore non-null checks | ||
// this is because non-null assertions can change the type-flow of the code | ||
// so whilst they might be unnecessary for the assignment - they are necessary | ||
// for following code | ||
return; | ||
} | ||
const originalNode = parserServices.esTreeNodeToTSNodeMap.get(node); | ||
@@ -145,4 +170,4 @@ const type = util.getConstrainedTypeAtLocation(checker, originalNode.expression); | ||
return fixer.removeRange([ | ||
originalNode.expression.end, | ||
originalNode.end, | ||
node.expression.range[1], | ||
node.range[1], | ||
]); | ||
@@ -177,4 +202,4 @@ }, | ||
return fixer.removeRange([ | ||
originalNode.expression.end, | ||
originalNode.end, | ||
node.expression.range[1], | ||
node.range[1], | ||
]); | ||
@@ -195,5 +220,5 @@ }, | ||
const castType = checker.getTypeAtLocation(originalNode); | ||
if (tsutils_1.isTypeFlagSet(castType, ts.TypeFlags.Literal) || | ||
(tsutils_1.isObjectType(castType) && | ||
(tsutils_1.isObjectFlagSet(castType, ts.ObjectFlags.Tuple) || | ||
if ((0, tsutils_1.isTypeFlagSet)(castType, ts.TypeFlags.Literal) || | ||
((0, tsutils_1.isObjectType)(castType) && | ||
((0, tsutils_1.isObjectFlagSet)(castType, ts.ObjectFlags.Tuple) || | ||
couldBeTupleType(castType)))) { | ||
@@ -210,11 +235,15 @@ // It's not always safe to remove a cast to a literal type or tuple | ||
fix(fixer) { | ||
return originalNode.kind === ts.SyntaxKind.TypeAssertionExpression | ||
? fixer.removeRange([ | ||
node.range[0], | ||
node.expression.range[0] - 1, | ||
]) | ||
: fixer.removeRange([ | ||
node.expression.range[1] + 1, | ||
node.range[1], | ||
]); | ||
if (originalNode.kind === ts.SyntaxKind.TypeAssertionExpression) { | ||
const closingAngleBracket = sourceCode.getTokenAfter(node.typeAnnotation); | ||
return (closingAngleBracket === null || closingAngleBracket === void 0 ? void 0 : closingAngleBracket.value) === '>' | ||
? fixer.removeRange([ | ||
node.range[0], | ||
closingAngleBracket.range[1], | ||
]) | ||
: null; | ||
} | ||
return fixer.removeRange([ | ||
node.expression.range[1] + 1, | ||
node.range[1], | ||
]); | ||
}, | ||
@@ -221,0 +250,0 @@ }); |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const semver = __importStar(require("semver")); | ||
@@ -38,10 +42,9 @@ const ts = __importStar(require("typescript")); | ||
docs: { | ||
category: 'Best Practices', | ||
description: 'Disallows unnecessary constraints on generic types', | ||
recommended: false, | ||
suggestion: true, | ||
description: 'Disallow unnecessary constraints on generic types', | ||
recommended: 'error', | ||
}, | ||
fixable: 'code', | ||
hasSuggestions: true, | ||
messages: { | ||
unnecessaryConstraint: 'Constraining the generic type `{{name}}` to `{{constraint}}` does nothing and is unnecessary.', | ||
removeUnnecessaryConstraint: 'Remove the unnecessary `{{constraint}}` constraint.', | ||
}, | ||
@@ -61,9 +64,20 @@ schema: [], | ||
? new Map([ | ||
[experimental_utils_1.AST_NODE_TYPES.TSAnyKeyword, 'any'], | ||
[experimental_utils_1.AST_NODE_TYPES.TSUnknownKeyword, 'unknown'], | ||
[utils_1.AST_NODE_TYPES.TSAnyKeyword, 'any'], | ||
[utils_1.AST_NODE_TYPES.TSUnknownKeyword, 'unknown'], | ||
]) | ||
: new Map([[experimental_utils_1.AST_NODE_TYPES.TSUnknownKeyword, 'unknown']]); | ||
: new Map([[utils_1.AST_NODE_TYPES.TSUnknownKeyword, 'unknown']]); | ||
const inJsx = context.getFilename().toLowerCase().endsWith('tsx'); | ||
const source = context.getSourceCode(); | ||
const checkNode = (node, inArrowFunction) => { | ||
const constraint = unnecessaryConstraints.get(node.constraint.type); | ||
function shouldAddTrailingComma() { | ||
if (!inArrowFunction || !inJsx) { | ||
return false; | ||
} | ||
// Only <T>() => {} would need trailing comma | ||
return (node.parent.params.length === | ||
1 && | ||
source.getTokensAfter(node)[0].value !== ',' && | ||
!node.default); | ||
} | ||
if (constraint) { | ||
@@ -75,5 +89,13 @@ context.report({ | ||
}, | ||
fix(fixer) { | ||
return fixer.replaceTextRange([node.name.range[1], node.constraint.range[1]], inArrowFunction && inJsx ? ',' : ''); | ||
}, | ||
suggest: [ | ||
{ | ||
messageId: 'removeUnnecessaryConstraint', | ||
data: { | ||
constraint, | ||
}, | ||
fix(fixer) { | ||
return fixer.replaceTextRange([node.name.range[1], node.constraint.range[1]], shouldAddTrailingComma() ? ',' : ''); | ||
}, | ||
}, | ||
], | ||
messageId: 'unnecessaryConstraint', | ||
@@ -80,0 +102,0 @@ node, |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,4 +26,6 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const tsutils = __importStar(require("tsutils")); | ||
const util = __importStar(require("../util")); | ||
const util_1 = require("../util"); | ||
exports.default = util.createRule({ | ||
@@ -30,4 +36,3 @@ name: 'no-unsafe-assignment', | ||
docs: { | ||
description: 'Disallows assigning any to variables and properties', | ||
category: 'Possible Errors', | ||
description: 'Disallow assigning a value with type `any` to variables and properties', | ||
recommended: 'error', | ||
@@ -37,7 +42,11 @@ requiresTypeChecking: true, | ||
messages: { | ||
anyAssignment: 'Unsafe assignment of an any value.', | ||
unsafeArrayPattern: 'Unsafe array destructuring of an any array value.', | ||
unsafeArrayPatternFromTuple: 'Unsafe array destructuring of a tuple element with an any value.', | ||
anyAssignment: 'Unsafe assignment of an `any` value.', | ||
anyAssignmentThis: [ | ||
'Unsafe assignment of an `any` value. `this` is typed as `any`.', | ||
'You can try to fix this by turning on the `noImplicitThis` compiler option, or adding a `this` parameter to the function.', | ||
].join('\n'), | ||
unsafeArrayPattern: 'Unsafe array destructuring of an `any` array value.', | ||
unsafeArrayPatternFromTuple: 'Unsafe array destructuring of a tuple element with an `any` value.', | ||
unsafeAssignment: 'Unsafe assignment of type {{sender}} to a variable of type {{receiver}}.', | ||
unsafeArraySpread: 'Unsafe spread of an any value in an array.', | ||
unsafeArraySpread: 'Unsafe spread of an `any` value in an array.', | ||
}, | ||
@@ -50,5 +59,7 @@ schema: [], | ||
const checker = program.getTypeChecker(); | ||
const compilerOptions = program.getCompilerOptions(); | ||
const isNoImplicitThis = tsutils.isStrictCompilerOptionEnabled(compilerOptions, 'noImplicitThis'); | ||
// returns true if the assignment reported | ||
function checkArrayDestructureHelper(receiverNode, senderNode) { | ||
if (receiverNode.type !== experimental_utils_1.AST_NODE_TYPES.ArrayPattern) { | ||
if (receiverNode.type !== utils_1.AST_NODE_TYPES.ArrayPattern) { | ||
return false; | ||
@@ -83,3 +94,3 @@ } | ||
} | ||
if (receiverElement.type === experimental_utils_1.AST_NODE_TYPES.RestElement) { | ||
if (receiverElement.type === utils_1.AST_NODE_TYPES.RestElement) { | ||
// don't handle rests as they're not a 1:1 assignment | ||
@@ -101,6 +112,6 @@ continue; | ||
} | ||
else if (receiverElement.type === experimental_utils_1.AST_NODE_TYPES.ArrayPattern) { | ||
else if (receiverElement.type === utils_1.AST_NODE_TYPES.ArrayPattern) { | ||
didReport = checkArrayDestructure(receiverElement, senderType, senderNode); | ||
} | ||
else if (receiverElement.type === experimental_utils_1.AST_NODE_TYPES.ObjectPattern) { | ||
else if (receiverElement.type === utils_1.AST_NODE_TYPES.ObjectPattern) { | ||
didReport = checkObjectDestructure(receiverElement, senderType, senderNode); | ||
@@ -113,3 +124,3 @@ } | ||
function checkObjectDestructureHelper(receiverNode, senderNode) { | ||
if (receiverNode.type !== experimental_utils_1.AST_NODE_TYPES.ObjectPattern) { | ||
if (receiverNode.type !== utils_1.AST_NODE_TYPES.ObjectPattern) { | ||
return false; | ||
@@ -130,5 +141,4 @@ } | ||
let didReport = false; | ||
for (let receiverIndex = 0; receiverIndex < receiverNode.properties.length; receiverIndex += 1) { | ||
const receiverProperty = receiverNode.properties[receiverIndex]; | ||
if (receiverProperty.type === experimental_utils_1.AST_NODE_TYPES.RestElement) { | ||
for (const receiverProperty of receiverNode.properties) { | ||
if (receiverProperty.type === utils_1.AST_NODE_TYPES.RestElement) { | ||
// don't bother checking rest | ||
@@ -140,10 +150,10 @@ continue; | ||
key = | ||
receiverProperty.key.type === experimental_utils_1.AST_NODE_TYPES.Identifier | ||
receiverProperty.key.type === utils_1.AST_NODE_TYPES.Identifier | ||
? receiverProperty.key.name | ||
: String(receiverProperty.key.value); | ||
} | ||
else if (receiverProperty.key.type === experimental_utils_1.AST_NODE_TYPES.Literal) { | ||
else if (receiverProperty.key.type === utils_1.AST_NODE_TYPES.Literal) { | ||
key = String(receiverProperty.key.value); | ||
} | ||
else if (receiverProperty.key.type === experimental_utils_1.AST_NODE_TYPES.TemplateLiteral && | ||
else if (receiverProperty.key.type === utils_1.AST_NODE_TYPES.TemplateLiteral && | ||
receiverProperty.key.quasis.length === 1) { | ||
@@ -168,6 +178,6 @@ key = String(receiverProperty.key.quasis[0].value.cooked); | ||
} | ||
else if (receiverProperty.value.type === experimental_utils_1.AST_NODE_TYPES.ArrayPattern) { | ||
else if (receiverProperty.value.type === utils_1.AST_NODE_TYPES.ArrayPattern) { | ||
didReport = checkArrayDestructure(receiverProperty.value, senderType, senderNode); | ||
} | ||
else if (receiverProperty.value.type === experimental_utils_1.AST_NODE_TYPES.ObjectPattern) { | ||
else if (receiverProperty.value.type === utils_1.AST_NODE_TYPES.ObjectPattern) { | ||
didReport = checkObjectDestructure(receiverProperty.value, senderType, senderNode); | ||
@@ -182,4 +192,5 @@ } | ||
const receiverTsNode = esTreeNodeToTSNodeMap.get(receiverNode); | ||
const receiverType = comparisonType === 2 /* Contextual */ | ||
? (_a = util.getContextualType(checker, receiverTsNode)) !== null && _a !== void 0 ? _a : checker.getTypeAtLocation(receiverTsNode) : checker.getTypeAtLocation(receiverTsNode); | ||
const receiverType = comparisonType === 2 /* ComparisonType.Contextual */ | ||
? (_a = util.getContextualType(checker, receiverTsNode)) !== null && _a !== void 0 ? _a : checker.getTypeAtLocation(receiverTsNode) | ||
: checker.getTypeAtLocation(receiverTsNode); | ||
const senderType = checker.getTypeAtLocation(esTreeNodeToTSNodeMap.get(senderNode)); | ||
@@ -191,12 +202,21 @@ if (util.isTypeAnyType(senderType)) { | ||
} | ||
let messageId = 'anyAssignment'; | ||
if (!isNoImplicitThis) { | ||
// `var foo = this` | ||
const thisExpression = (0, util_1.getThisExpression)(senderNode); | ||
if (thisExpression && | ||
util.isTypeAnyType(util.getConstrainedTypeAtLocation(checker, esTreeNodeToTSNodeMap.get(thisExpression)))) { | ||
messageId = 'anyAssignmentThis'; | ||
} | ||
} | ||
context.report({ | ||
node: reportingNode, | ||
messageId: 'anyAssignment', | ||
messageId, | ||
}); | ||
return true; | ||
} | ||
if (comparisonType === 0 /* None */) { | ||
if (comparisonType === 0 /* ComparisonType.None */) { | ||
return false; | ||
} | ||
const result = util.isUnsafeAssignment(senderType, receiverType, checker); | ||
const result = util.isUnsafeAssignment(senderType, receiverType, checker, senderNode); | ||
if (!result) { | ||
@@ -219,5 +239,5 @@ return false; | ||
? // if there's a type annotation, we can do a comparison | ||
1 /* Basic */ | ||
1 /* ComparisonType.Basic */ | ||
: // no type annotation means the variable's type will just be inferred, thus equal | ||
0 /* None */; | ||
0 /* ComparisonType.None */; | ||
} | ||
@@ -235,7 +255,7 @@ return { | ||
}, | ||
'ClassProperty[value != null]'(node) { | ||
'PropertyDefinition[value != null]'(node) { | ||
checkAssignment(node.key, node.value, node, getComparisonType(node.typeAnnotation)); | ||
}, | ||
'AssignmentExpression[operator = "="], AssignmentPattern'(node) { | ||
let didReport = checkAssignment(node.left, node.right, node, 1 /* Basic */); | ||
let didReport = checkAssignment(node.left, node.right, node, 1 /* ComparisonType.Basic */); | ||
if (!didReport) { | ||
@@ -250,8 +270,8 @@ didReport = checkArrayDestructureHelper(node.left, node.right); | ||
':not(ObjectPattern) > Property'(node) { | ||
if (node.value.type === experimental_utils_1.AST_NODE_TYPES.AssignmentPattern || | ||
node.value.type === experimental_utils_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression) { | ||
if (node.value.type === utils_1.AST_NODE_TYPES.AssignmentPattern || | ||
node.value.type === utils_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression) { | ||
// handled by other selector | ||
return; | ||
} | ||
checkAssignment(node.key, node.value, node, 2 /* Contextual */); | ||
checkAssignment(node.key, node.value, node, 2 /* ComparisonType.Contextual */); | ||
}, | ||
@@ -271,7 +291,7 @@ 'ArrayExpression > SpreadElement'(node) { | ||
const value = util.nullThrows(node.value, util.NullThrowsReasons.MissingToken(node.type, 'value')); | ||
if (value.type !== experimental_utils_1.AST_NODE_TYPES.JSXExpressionContainer || | ||
value.expression.type === experimental_utils_1.AST_NODE_TYPES.JSXEmptyExpression) { | ||
if (value.type !== utils_1.AST_NODE_TYPES.JSXExpressionContainer || | ||
value.expression.type === utils_1.AST_NODE_TYPES.JSXEmptyExpression) { | ||
return; | ||
} | ||
checkAssignment(node.name, value.expression, value.expression, 2 /* Contextual */); | ||
checkAssignment(node.name, value.expression, value.expression, 2 /* ComparisonType.Contextual */); | ||
}, | ||
@@ -278,0 +298,0 @@ }; |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,5 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const tsutils = __importStar(require("tsutils")); | ||
const util = __importStar(require("../util")); | ||
const util_1 = require("../util"); | ||
exports.default = util.createRule({ | ||
@@ -29,4 +35,3 @@ name: 'no-unsafe-call', | ||
docs: { | ||
description: 'Disallows calling an any type value', | ||
category: 'Possible Errors', | ||
description: 'Disallow calling a value with type `any`', | ||
recommended: 'error', | ||
@@ -36,3 +41,7 @@ requiresTypeChecking: true, | ||
messages: { | ||
unsafeCall: 'Unsafe call of an any typed value.', | ||
unsafeCall: 'Unsafe call of an `any` typed value.', | ||
unsafeCallThis: [ | ||
'Unsafe call of an `any` typed value. `this` is typed as `any`.', | ||
'You can try to fix this by turning on the `noImplicitThis` compiler option, or adding a `this` parameter to the function.', | ||
].join('\n'), | ||
unsafeNew: 'Unsafe construction of an any type value.', | ||
@@ -47,2 +56,4 @@ unsafeTemplateTag: 'Unsafe any typed template tag.', | ||
const checker = program.getTypeChecker(); | ||
const compilerOptions = program.getCompilerOptions(); | ||
const isNoImplicitThis = tsutils.isStrictCompilerOptionEnabled(compilerOptions, 'noImplicitThis'); | ||
function checkCall(node, reportingNode, messageId) { | ||
@@ -52,2 +63,10 @@ const tsNode = esTreeNodeToTSNodeMap.get(node); | ||
if (util.isTypeAnyType(type)) { | ||
if (!isNoImplicitThis) { | ||
// `this()` or `this.foo()` or `this.foo[bar]()` | ||
const thisExpression = (0, util_1.getThisExpression)(node); | ||
if (thisExpression && | ||
util.isTypeAnyType(util.getConstrainedTypeAtLocation(checker, esTreeNodeToTSNodeMap.get(thisExpression)))) { | ||
messageId = 'unsafeCallThis'; | ||
} | ||
} | ||
context.report({ | ||
@@ -54,0 +73,0 @@ node: reportingNode, |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,4 +26,6 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const tsutils = __importStar(require("tsutils")); | ||
const util = __importStar(require("../util")); | ||
const util_1 = require("../util"); | ||
exports.default = util.createRule({ | ||
@@ -30,4 +36,3 @@ name: 'no-unsafe-member-access', | ||
docs: { | ||
description: 'Disallows member access on any typed variables', | ||
category: 'Possible Errors', | ||
description: 'Disallow member access on a value with type `any`', | ||
recommended: 'error', | ||
@@ -37,3 +42,7 @@ requiresTypeChecking: true, | ||
messages: { | ||
unsafeMemberExpression: 'Unsafe member access {{property}} on an any value.', | ||
unsafeMemberExpression: 'Unsafe member access {{property}} on an `any` value.', | ||
unsafeThisMemberExpression: [ | ||
'Unsafe member access {{property}} on an `any` value. `this` is typed as `any`.', | ||
'You can try to fix this by turning on the `noImplicitThis` compiler option, or adding a `this` parameter to the function.', | ||
].join('\n'), | ||
unsafeComputedMemberAccess: 'Computed name {{property}} resolves to an any value.', | ||
@@ -47,2 +56,4 @@ }, | ||
const checker = program.getTypeChecker(); | ||
const compilerOptions = program.getCompilerOptions(); | ||
const isNoImplicitThis = tsutils.isStrictCompilerOptionEnabled(compilerOptions, 'noImplicitThis'); | ||
const sourceCode = context.getSourceCode(); | ||
@@ -55,5 +66,5 @@ const stateCache = new Map(); | ||
} | ||
if (node.object.type === experimental_utils_1.AST_NODE_TYPES.MemberExpression) { | ||
if (node.object.type === utils_1.AST_NODE_TYPES.MemberExpression) { | ||
const objectState = checkMemberExpression(node.object); | ||
if (objectState === 1 /* Unsafe */) { | ||
if (objectState === 1 /* State.Unsafe */) { | ||
// if the object is unsafe, we know this will be unsafe as well | ||
@@ -67,9 +78,18 @@ // we don't need to report, as we have already reported on the inner member expr | ||
const type = checker.getTypeAtLocation(tsNode); | ||
const state = util.isTypeAnyType(type) ? 1 /* Unsafe */ : 2 /* Safe */; | ||
const state = util.isTypeAnyType(type) ? 1 /* State.Unsafe */ : 2 /* State.Safe */; | ||
stateCache.set(node, state); | ||
if (state === 1 /* Unsafe */) { | ||
if (state === 1 /* State.Unsafe */) { | ||
const propertyName = sourceCode.getText(node.property); | ||
let messageId = 'unsafeMemberExpression'; | ||
if (!isNoImplicitThis) { | ||
// `this.foo` or `this.foo[bar]` | ||
const thisExpression = (0, util_1.getThisExpression)(node); | ||
if (thisExpression && | ||
util.isTypeAnyType(util.getConstrainedTypeAtLocation(checker, esTreeNodeToTSNodeMap.get(thisExpression)))) { | ||
messageId = 'unsafeThisMemberExpression'; | ||
} | ||
} | ||
context.report({ | ||
node, | ||
messageId: 'unsafeMemberExpression', | ||
messageId, | ||
data: { | ||
@@ -88,7 +108,7 @@ property: node.computed ? `[${propertyName}]` : `.${propertyName}`, | ||
// x[1] | ||
node.type === experimental_utils_1.AST_NODE_TYPES.Literal || | ||
node.type === utils_1.AST_NODE_TYPES.Literal || | ||
// x[1++] x[++x] etc | ||
// FUN FACT - **all** update expressions return type number, regardless of the argument's type, | ||
// because JS engines return NaN if there the argument is not a number. | ||
node.type === experimental_utils_1.AST_NODE_TYPES.UpdateExpression) { | ||
node.type === utils_1.AST_NODE_TYPES.UpdateExpression) { | ||
// perf optimizations - literals can obviously never be `any` | ||
@@ -95,0 +115,0 @@ return; |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,5 +26,6 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const tsutils_1 = require("tsutils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const tsutils = __importStar(require("tsutils")); | ||
const util = __importStar(require("../util")); | ||
const util_1 = require("../util"); | ||
exports.default = util.createRule({ | ||
@@ -31,4 +36,3 @@ name: 'no-unsafe-return', | ||
docs: { | ||
description: 'Disallows returning any from a function', | ||
category: 'Possible Errors', | ||
description: 'Disallow returning a value with type `any` from a function', | ||
recommended: 'error', | ||
@@ -38,4 +42,8 @@ requiresTypeChecking: true, | ||
messages: { | ||
unsafeReturn: 'Unsafe return of an {{type}} typed value', | ||
unsafeReturnAssignment: 'Unsafe return of type {{sender}} from function with return type {{receiver}}.', | ||
unsafeReturn: 'Unsafe return of an `{{type}}` typed value.', | ||
unsafeReturnThis: [ | ||
'Unsafe return of an `{{type}}` typed value. `this` is typed as `any`.', | ||
'You can try to fix this by turning on the `noImplicitThis` compiler option, or adding a `this` parameter to the function.', | ||
].join('\n'), | ||
unsafeReturnAssignment: 'Unsafe return of type `{{sender}}` from function with return type `{{receiver}}`.', | ||
}, | ||
@@ -48,8 +56,10 @@ schema: [], | ||
const checker = program.getTypeChecker(); | ||
const compilerOptions = program.getCompilerOptions(); | ||
const isNoImplicitThis = tsutils.isStrictCompilerOptionEnabled(compilerOptions, 'noImplicitThis'); | ||
function getParentFunctionNode(node) { | ||
let current = node.parent; | ||
while (current) { | ||
if (current.type === experimental_utils_1.AST_NODE_TYPES.ArrowFunctionExpression || | ||
current.type === experimental_utils_1.AST_NODE_TYPES.FunctionDeclaration || | ||
current.type === experimental_utils_1.AST_NODE_TYPES.FunctionExpression) { | ||
if (current.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression || | ||
current.type === utils_1.AST_NODE_TYPES.FunctionDeclaration || | ||
current.type === utils_1.AST_NODE_TYPES.FunctionExpression) { | ||
return current; | ||
@@ -77,3 +87,3 @@ } | ||
// the return type of the arrow function is Set<any> even though the variable is typed as Set<string> | ||
let functionType = tsutils_1.isExpression(functionTSNode) | ||
let functionType = tsutils.isExpression(functionTSNode) | ||
? util.getContextualType(checker, functionTSNode) | ||
@@ -84,3 +94,12 @@ : checker.getTypeAtLocation(functionTSNode); | ||
} | ||
if (anyType !== 2 /* Safe */) { | ||
// If there is an explicit type annotation *and* that type matches the actual | ||
// function return type, we shouldn't complain (it's intentional, even if unsafe) | ||
if (functionTSNode.type) { | ||
for (const signature of functionType.getCallSignatures()) { | ||
if (returnNodeType === signature.getReturnType()) { | ||
return; | ||
} | ||
} | ||
} | ||
if (anyType !== util.AnyType.Safe) { | ||
// Allow cases when the declared return type of the function is either unknown or unknown[] | ||
@@ -90,7 +109,7 @@ // and the function is returning any or any[]. | ||
const functionReturnType = signature.getReturnType(); | ||
if (anyType === 0 /* Any */ && | ||
if (anyType === util.AnyType.Any && | ||
util.isTypeUnknownType(functionReturnType)) { | ||
return; | ||
} | ||
if (anyType === 1 /* AnyArray */ && | ||
if (anyType === util.AnyType.AnyArray && | ||
util.isTypeUnknownArrayType(functionReturnType, checker)) { | ||
@@ -100,8 +119,17 @@ return; | ||
} | ||
let messageId = 'unsafeReturn'; | ||
if (!isNoImplicitThis) { | ||
// `return this` | ||
const thisExpression = (0, util_1.getThisExpression)(returnNode); | ||
if (thisExpression && | ||
util.isTypeAnyType(util.getConstrainedTypeAtLocation(checker, esTreeNodeToTSNodeMap.get(thisExpression)))) { | ||
messageId = 'unsafeReturnThis'; | ||
} | ||
} | ||
// If the function return type was not unknown/unknown[], mark usage as unsafeReturn. | ||
return context.report({ | ||
node: reportingNode, | ||
messageId: 'unsafeReturn', | ||
messageId, | ||
data: { | ||
type: anyType === 0 /* Any */ ? 'any' : 'any[]', | ||
type: anyType === util.AnyType.Any ? 'any' : 'any[]', | ||
}, | ||
@@ -112,9 +140,3 @@ }); | ||
const functionReturnType = signature.getReturnType(); | ||
if (returnNodeType === functionReturnType) { | ||
// don't bother checking if they're the same | ||
// either the function is explicitly declared to return the same type | ||
// or there was no declaration, so the return type is implicit | ||
return; | ||
} | ||
const result = util.isUnsafeAssignment(returnNodeType, functionReturnType, checker); | ||
const result = util.isUnsafeAssignment(returnNodeType, functionReturnType, checker, returnNode); | ||
if (!result) { | ||
@@ -121,0 +143,0 @@ return; |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -21,10 +25,8 @@ if (k2 === undefined) k2 = k; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
var _a; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const no_unused_expressions_1 = __importDefault(require("eslint/lib/rules/no-unused-expressions")); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
const getESLintCoreRule_1 = require("../util/getESLintCoreRule"); | ||
const baseRule = (0, getESLintCoreRule_1.getESLintCoreRule)('no-unused-expressions'); | ||
exports.default = util.createRule({ | ||
@@ -36,8 +38,9 @@ name: 'no-unused-expressions', | ||
description: 'Disallow unused expressions', | ||
category: 'Best Practices', | ||
recommended: false, | ||
extendsBaseRule: true, | ||
}, | ||
schema: no_unused_expressions_1.default.meta.schema, | ||
messages: (_a = no_unused_expressions_1.default.meta.messages) !== null && _a !== void 0 ? _a : { | ||
hasSuggestions: baseRule.meta.hasSuggestions, | ||
schema: baseRule.meta.schema, | ||
// TODO: this rule has only had messages since v7.0 - remove this when we remove support for v6 | ||
messages: (_a = baseRule.meta.messages) !== null && _a !== void 0 ? _a : { | ||
unusedExpression: 'Expected an assignment or function call and instead saw an expression.', | ||
@@ -53,16 +56,15 @@ }, | ||
], | ||
create(context, options) { | ||
const rules = no_unused_expressions_1.default.create(context); | ||
const { allowShortCircuit = false, allowTernary = false } = options[0]; | ||
create(context, [{ allowShortCircuit = false, allowTernary = false }]) { | ||
const rules = baseRule.create(context); | ||
function isValidExpression(node) { | ||
if (allowShortCircuit && node.type === experimental_utils_1.AST_NODE_TYPES.LogicalExpression) { | ||
if (allowShortCircuit && node.type === utils_1.AST_NODE_TYPES.LogicalExpression) { | ||
return isValidExpression(node.right); | ||
} | ||
if (allowTernary && node.type === experimental_utils_1.AST_NODE_TYPES.ConditionalExpression) { | ||
if (allowTernary && node.type === utils_1.AST_NODE_TYPES.ConditionalExpression) { | ||
return (isValidExpression(node.alternate) && | ||
isValidExpression(node.consequent)); | ||
} | ||
return ((node.type === experimental_utils_1.AST_NODE_TYPES.ChainExpression && | ||
node.expression.type === experimental_utils_1.AST_NODE_TYPES.CallExpression) || | ||
node.type === experimental_utils_1.AST_NODE_TYPES.ImportExpression); | ||
return ((node.type === utils_1.AST_NODE_TYPES.ChainExpression && | ||
node.expression.type === utils_1.AST_NODE_TYPES.CallExpression) || | ||
node.type === utils_1.AST_NODE_TYPES.ImportExpression); | ||
} | ||
@@ -69,0 +71,0 @@ return { |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,4 +26,4 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const scope_manager_1 = require("@typescript-eslint/scope-manager"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -32,3 +36,2 @@ exports.default = util.createRule({ | ||
description: 'Disallow unused variables', | ||
category: 'Variables', | ||
recommended: 'warn', | ||
@@ -67,2 +70,5 @@ extendsBaseRule: true, | ||
}, | ||
destructuredArrayIgnorePattern: { | ||
type: 'string', | ||
}, | ||
}, | ||
@@ -79,3 +85,3 @@ additionalProperties: false, | ||
defaultOptions: [{}], | ||
create(context) { | ||
create(context, [firstOption]) { | ||
const filename = context.getFilename(); | ||
@@ -92,3 +98,2 @@ const sourceCode = context.getSourceCode(); | ||
}; | ||
const firstOption = context.options[0]; | ||
if (firstOption) { | ||
@@ -101,4 +106,6 @@ if (typeof firstOption === 'string') { | ||
options.args = (_b = firstOption.args) !== null && _b !== void 0 ? _b : options.args; | ||
options.ignoreRestSiblings = (_c = firstOption.ignoreRestSiblings) !== null && _c !== void 0 ? _c : options.ignoreRestSiblings; | ||
options.caughtErrors = (_d = firstOption.caughtErrors) !== null && _d !== void 0 ? _d : options.caughtErrors; | ||
options.ignoreRestSiblings = | ||
(_c = firstOption.ignoreRestSiblings) !== null && _c !== void 0 ? _c : options.ignoreRestSiblings; | ||
options.caughtErrors = | ||
(_d = firstOption.caughtErrors) !== null && _d !== void 0 ? _d : options.caughtErrors; | ||
if (firstOption.varsIgnorePattern) { | ||
@@ -113,2 +120,5 @@ options.varsIgnorePattern = new RegExp(firstOption.varsIgnorePattern, 'u'); | ||
} | ||
if (firstOption.destructuredArrayIgnorePattern) { | ||
options.destructuredArrayIgnorePattern = new RegExp(firstOption.destructuredArrayIgnorePattern, 'u'); | ||
} | ||
} | ||
@@ -119,4 +129,16 @@ } | ||
function collectUnusedVariables() { | ||
var _a, _b, _c; | ||
var _a, _b, _c, _d, _e; | ||
/** | ||
* Checks whether a node is a sibling of the rest property or not. | ||
* @param {ASTNode} node a node to check | ||
* @returns {boolean} True if the node is a sibling of the rest property, otherwise false. | ||
*/ | ||
function hasRestSibling(node) { | ||
var _a; | ||
return (node.type === utils_1.AST_NODE_TYPES.Property && | ||
((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.ObjectPattern && | ||
node.parent.properties[node.parent.properties.length - 1].type === | ||
utils_1.AST_NODE_TYPES.RestElement); | ||
} | ||
/** | ||
* Determines if a variable has a sibling rest property | ||
@@ -128,10 +150,5 @@ * @param variable eslint-scope variable object. | ||
if (options.ignoreRestSiblings) { | ||
return variable.defs.some(def => { | ||
const propertyNode = def.name.parent; | ||
const patternNode = propertyNode.parent; | ||
return (propertyNode.type === experimental_utils_1.AST_NODE_TYPES.Property && | ||
patternNode.type === experimental_utils_1.AST_NODE_TYPES.ObjectPattern && | ||
patternNode.properties[patternNode.properties.length - 1].type === | ||
experimental_utils_1.AST_NODE_TYPES.RestElement); | ||
}); | ||
const hasRestSiblingDefinition = variable.defs.some(def => hasRestSibling(def.name.parent)); | ||
const hasRestSiblingReference = variable.references.some(ref => hasRestSibling(ref.identifier.parent)); | ||
return hasRestSiblingDefinition || hasRestSiblingReference; | ||
} | ||
@@ -161,3 +178,3 @@ return false; | ||
const def = variable.defs[0]; | ||
if (variable.scope.type === experimental_utils_1.TSESLint.Scope.ScopeType.global && | ||
if (variable.scope.type === utils_1.TSESLint.Scope.ScopeType.global && | ||
options.vars === 'local') { | ||
@@ -167,4 +184,12 @@ // skip variables in the global scope if configured to | ||
} | ||
const refUsedInArrayPatterns = variable.references.some(ref => { var _a; return ((_a = ref.identifier.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.ArrayPattern; }); | ||
// skip elements of array destructuring patterns | ||
if ((((_a = def.name.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.ArrayPattern || | ||
refUsedInArrayPatterns) && | ||
'name' in def.name && | ||
((_b = options.destructuredArrayIgnorePattern) === null || _b === void 0 ? void 0 : _b.test(def.name.name))) { | ||
continue; | ||
} | ||
// skip catch variables | ||
if (def.type === experimental_utils_1.TSESLint.Scope.DefinitionType.CatchClause) { | ||
if (def.type === utils_1.TSESLint.Scope.DefinitionType.CatchClause) { | ||
if (options.caughtErrors === 'none') { | ||
@@ -174,7 +199,8 @@ continue; | ||
// skip ignored parameters | ||
if ('name' in def.name && ((_a = options.caughtErrorsIgnorePattern) === null || _a === void 0 ? void 0 : _a.test(def.name.name))) { | ||
if ('name' in def.name && | ||
((_c = options.caughtErrorsIgnorePattern) === null || _c === void 0 ? void 0 : _c.test(def.name.name))) { | ||
continue; | ||
} | ||
} | ||
if (def.type === experimental_utils_1.TSESLint.Scope.DefinitionType.Parameter) { | ||
if (def.type === utils_1.TSESLint.Scope.DefinitionType.Parameter) { | ||
// if "args" option is "none", skip any parameter | ||
@@ -185,3 +211,4 @@ if (options.args === 'none') { | ||
// skip ignored parameters | ||
if ('name' in def.name && ((_b = options.argsIgnorePattern) === null || _b === void 0 ? void 0 : _b.test(def.name.name))) { | ||
if ('name' in def.name && | ||
((_d = options.argsIgnorePattern) === null || _d === void 0 ? void 0 : _d.test(def.name.name))) { | ||
continue; | ||
@@ -198,3 +225,4 @@ } | ||
// skip ignored variables | ||
if ('name' in def.name && ((_c = options.varsIgnorePattern) === null || _c === void 0 ? void 0 : _c.test(def.name.name))) { | ||
if ('name' in def.name && | ||
((_e = options.varsIgnorePattern) === null || _e === void 0 ? void 0 : _e.test(def.name.name))) { | ||
continue; | ||
@@ -217,3 +245,3 @@ } | ||
// declaration file handling | ||
[ambientDeclarationSelector(experimental_utils_1.AST_NODE_TYPES.Program, true)](node) { | ||
[ambientDeclarationSelector(utils_1.AST_NODE_TYPES.Program, true)](node) { | ||
if (!util.isDefinitionFile(filename)) { | ||
@@ -224,2 +252,16 @@ return; | ||
}, | ||
// module declaration in module declaration should not report unused vars error | ||
// this is workaround as this change should be done in better way | ||
'TSModuleDeclaration > TSModuleDeclaration'(node) { | ||
if (node.id.type === utils_1.AST_NODE_TYPES.Identifier) { | ||
let scope = context.getScope(); | ||
if (scope.upper) { | ||
scope = scope.upper; | ||
} | ||
const superVar = scope.set.get(node.id.name); | ||
if (superVar) { | ||
superVar.eslintUsed = true; | ||
} | ||
} | ||
}, | ||
// children of a namespace that is a child of a declared namespace are auto-exported | ||
@@ -235,3 +277,3 @@ [ambientDeclarationSelector('TSModuleDeclaration[declare = true] > TSModuleBlock TSModuleDeclaration > TSModuleBlock', false)](node) { | ||
// all other statements are not automatically exported in this case | ||
if (moduleDecl.id.type === experimental_utils_1.AST_NODE_TYPES.Literal && | ||
if (moduleDecl.id.type === utils_1.AST_NODE_TYPES.Literal && | ||
checkModuleDeclForExportEquals(moduleDecl)) { | ||
@@ -255,3 +297,3 @@ return; | ||
let pattern; | ||
if (defType === experimental_utils_1.TSESLint.Scope.DefinitionType.CatchClause && | ||
if (defType === utils_1.TSESLint.Scope.DefinitionType.CatchClause && | ||
options.caughtErrorsIgnorePattern) { | ||
@@ -261,3 +303,3 @@ type = 'args'; | ||
} | ||
else if (defType === experimental_utils_1.TSESLint.Scope.DefinitionType.Parameter && | ||
else if (defType === utils_1.TSESLint.Scope.DefinitionType.Parameter && | ||
options.argsIgnorePattern) { | ||
@@ -267,3 +309,3 @@ type = 'args'; | ||
} | ||
else if (defType !== experimental_utils_1.TSESLint.Scope.DefinitionType.Parameter && | ||
else if (defType !== utils_1.TSESLint.Scope.DefinitionType.Parameter && | ||
options.varsIgnorePattern) { | ||
@@ -289,5 +331,12 @@ type = 'vars'; | ||
function getAssignedMessageData(unusedVar) { | ||
const additional = options.varsIgnorePattern | ||
? `. Allowed unused vars must match ${options.varsIgnorePattern.toString()}` | ||
: ''; | ||
var _a; | ||
const def = unusedVar.defs[0]; | ||
let additional = ''; | ||
if (options.destructuredArrayIgnorePattern && | ||
((_a = def === null || def === void 0 ? void 0 : def.name.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.ArrayPattern) { | ||
additional = `. Allowed unused elements of array destructuring patterns must match ${options.destructuredArrayIgnorePattern.toString()}`; | ||
} | ||
else if (options.varsIgnorePattern) { | ||
additional = `. Allowed unused vars must match ${options.varsIgnorePattern.toString()}`; | ||
} | ||
return { | ||
@@ -300,10 +349,10 @@ varName: unusedVar.name, | ||
const unusedVars = collectUnusedVariables(); | ||
for (let i = 0, l = unusedVars.length; i < l; ++i) { | ||
const unusedVar = unusedVars[i]; | ||
for (const unusedVar of unusedVars) { | ||
// Report the first declaration. | ||
if (unusedVar.defs.length > 0) { | ||
const writeReferences = unusedVar.references.filter(ref => ref.isWrite() && | ||
ref.from.variableScope === unusedVar.scope.variableScope); | ||
context.report({ | ||
node: unusedVar.references.length | ||
? unusedVar.references[unusedVar.references.length - 1] | ||
.identifier | ||
node: writeReferences.length | ||
? writeReferences[writeReferences.length - 1].identifier | ||
: unusedVar.identifiers[0], | ||
@@ -331,3 +380,2 @@ messageId: 'unusedVar', | ||
function checkModuleDeclForExportEquals(node) { | ||
var _a, _b; | ||
const cached = MODULE_DECL_CACHE.get(node); | ||
@@ -337,6 +385,8 @@ if (cached != null) { | ||
} | ||
for (const statement of (_b = (_a = node.body) === null || _a === void 0 ? void 0 : _a.body) !== null && _b !== void 0 ? _b : []) { | ||
if (statement.type === experimental_utils_1.AST_NODE_TYPES.TSExportAssignment) { | ||
MODULE_DECL_CACHE.set(node, true); | ||
return true; | ||
if (node.body && node.body.type === utils_1.AST_NODE_TYPES.TSModuleBlock) { | ||
for (const statement of node.body.body) { | ||
if (statement.type === utils_1.AST_NODE_TYPES.TSExportAssignment) { | ||
MODULE_DECL_CACHE.set(node, true); | ||
return true; | ||
} | ||
} | ||
@@ -351,12 +401,12 @@ } | ||
`${parent} > :matches(${[ | ||
experimental_utils_1.AST_NODE_TYPES.TSInterfaceDeclaration, | ||
experimental_utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration, | ||
utils_1.AST_NODE_TYPES.TSInterfaceDeclaration, | ||
utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration, | ||
].join(', ')})`, | ||
// Value things are ambiently exported if they are "declare"d | ||
`${parent} > :matches(${[ | ||
experimental_utils_1.AST_NODE_TYPES.ClassDeclaration, | ||
experimental_utils_1.AST_NODE_TYPES.TSDeclareFunction, | ||
experimental_utils_1.AST_NODE_TYPES.TSEnumDeclaration, | ||
experimental_utils_1.AST_NODE_TYPES.TSModuleDeclaration, | ||
experimental_utils_1.AST_NODE_TYPES.VariableDeclaration, | ||
utils_1.AST_NODE_TYPES.ClassDeclaration, | ||
utils_1.AST_NODE_TYPES.TSDeclareFunction, | ||
utils_1.AST_NODE_TYPES.TSEnumDeclaration, | ||
utils_1.AST_NODE_TYPES.TSModuleDeclaration, | ||
utils_1.AST_NODE_TYPES.VariableDeclaration, | ||
].join(', ')})${childDeclare ? '[declare = true]' : ''}`, | ||
@@ -369,14 +419,14 @@ ].join(', '); | ||
switch (node.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.TSInterfaceDeclaration: | ||
case experimental_utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration: | ||
case experimental_utils_1.AST_NODE_TYPES.ClassDeclaration: | ||
case experimental_utils_1.AST_NODE_TYPES.FunctionDeclaration: | ||
case experimental_utils_1.AST_NODE_TYPES.TSDeclareFunction: | ||
case experimental_utils_1.AST_NODE_TYPES.TSEnumDeclaration: | ||
case experimental_utils_1.AST_NODE_TYPES.TSModuleDeclaration: | ||
if (((_a = node.id) === null || _a === void 0 ? void 0 : _a.type) === experimental_utils_1.AST_NODE_TYPES.Identifier) { | ||
case utils_1.AST_NODE_TYPES.TSInterfaceDeclaration: | ||
case utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration: | ||
case utils_1.AST_NODE_TYPES.ClassDeclaration: | ||
case utils_1.AST_NODE_TYPES.FunctionDeclaration: | ||
case utils_1.AST_NODE_TYPES.TSDeclareFunction: | ||
case utils_1.AST_NODE_TYPES.TSEnumDeclaration: | ||
case utils_1.AST_NODE_TYPES.TSModuleDeclaration: | ||
if (((_a = node.id) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.Identifier) { | ||
identifiers.push(node.id); | ||
} | ||
break; | ||
case experimental_utils_1.AST_NODE_TYPES.VariableDeclaration: | ||
case utils_1.AST_NODE_TYPES.VariableDeclaration: | ||
for (const declaration of node.declarations) { | ||
@@ -391,4 +441,4 @@ visitPattern(declaration, pattern => { | ||
const shouldUseUpperScope = [ | ||
experimental_utils_1.AST_NODE_TYPES.TSModuleDeclaration, | ||
experimental_utils_1.AST_NODE_TYPES.TSDeclareFunction, | ||
utils_1.AST_NODE_TYPES.TSModuleDeclaration, | ||
utils_1.AST_NODE_TYPES.TSDeclareFunction, | ||
].includes(node.type); | ||
@@ -395,0 +445,0 @@ if (scope.variableScope !== scope) { |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,4 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const scope_manager_1 = require("@typescript-eslint/scope-manager"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -36,6 +41,7 @@ const SENTINEL_TYPE = /^(?:(?:Function|Class)(?:Declaration|Expression)|ArrowFunctionExpression|CatchClause|ImportDeclaration|ExportNamedDeclaration)$/; | ||
let ignoreTypeReferences = true; | ||
let allowNamedExports = false; | ||
if (typeof options === 'string') { | ||
functions = options !== 'nofunc'; | ||
} | ||
else if (typeof options === 'object' && options !== null) { | ||
else if (typeof options === 'object' && options != null) { | ||
functions = options.functions !== false; | ||
@@ -47,2 +53,3 @@ classes = options.classes !== false; | ||
ignoreTypeReferences = options.ignoreTypeReferences !== false; | ||
allowNamedExports = options.allowNamedExports !== false; | ||
} | ||
@@ -56,2 +63,3 @@ return { | ||
ignoreTypeReferences, | ||
allowNamedExports, | ||
}; | ||
@@ -63,3 +71,3 @@ } | ||
function isFunction(variable) { | ||
return variable.defs[0].type === 'FunctionName'; | ||
return variable.defs[0].type === scope_manager_1.DefinitionType.FunctionName; | ||
} | ||
@@ -70,3 +78,3 @@ /** | ||
function isTypedef(variable) { | ||
return variable.defs[0].type === 'Type'; | ||
return variable.defs[0].type === scope_manager_1.DefinitionType.Type; | ||
} | ||
@@ -77,3 +85,3 @@ /** | ||
function isOuterEnum(variable, reference) { | ||
return (variable.defs[0].type == 'TSEnumName' && | ||
return (variable.defs[0].type === scope_manager_1.DefinitionType.TSEnumName && | ||
variable.scope.variableScope !== reference.from.variableScope); | ||
@@ -85,3 +93,3 @@ } | ||
function isOuterClass(variable, reference) { | ||
return (variable.defs[0].type === 'ClassName' && | ||
return (variable.defs[0].type === scope_manager_1.DefinitionType.ClassName && | ||
variable.scope.variableScope !== reference.from.variableScope); | ||
@@ -93,6 +101,15 @@ } | ||
function isOuterVariable(variable, reference) { | ||
return (variable.defs[0].type === 'Variable' && | ||
return (variable.defs[0].type === scope_manager_1.DefinitionType.Variable && | ||
variable.scope.variableScope !== reference.from.variableScope); | ||
} | ||
/** | ||
* Checks whether or not a given reference is a export reference. | ||
*/ | ||
function isNamedExports(reference) { | ||
var _a; | ||
const { identifier } = reference; | ||
return (((_a = identifier.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.ExportSpecifier && | ||
identifier.parent.local === identifier); | ||
} | ||
/** | ||
* Recursively checks whether or not a given reference has a type query declaration among it's parents | ||
@@ -102,6 +119,6 @@ */ | ||
switch (node.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.TSTypeQuery: | ||
case utils_1.AST_NODE_TYPES.TSTypeQuery: | ||
return true; | ||
case experimental_utils_1.AST_NODE_TYPES.TSQualifiedName: | ||
case experimental_utils_1.AST_NODE_TYPES.Identifier: | ||
case utils_1.AST_NODE_TYPES.TSQualifiedName: | ||
case utils_1.AST_NODE_TYPES.Identifier: | ||
if (!node.parent) { | ||
@@ -134,3 +151,3 @@ return false; | ||
function isClassRefInClassDecorator(variable, reference) { | ||
if (variable.defs[0].type !== 'ClassName') { | ||
if (variable.defs[0].type !== scope_manager_1.DefinitionType.ClassName) { | ||
return false; | ||
@@ -168,3 +185,3 @@ } | ||
while (node) { | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.VariableDeclarator) { | ||
if (node.type === utils_1.AST_NODE_TYPES.VariableDeclarator) { | ||
if (isInRange(node.init, location)) { | ||
@@ -174,4 +191,4 @@ return true; | ||
if (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.parent) && | ||
(node.parent.parent.type === experimental_utils_1.AST_NODE_TYPES.ForInStatement || | ||
node.parent.parent.type === experimental_utils_1.AST_NODE_TYPES.ForOfStatement) && | ||
(node.parent.parent.type === utils_1.AST_NODE_TYPES.ForInStatement || | ||
node.parent.parent.type === utils_1.AST_NODE_TYPES.ForOfStatement) && | ||
isInRange(node.parent.parent.right, location)) { | ||
@@ -182,3 +199,3 @@ return true; | ||
} | ||
else if (node.type === experimental_utils_1.AST_NODE_TYPES.AssignmentPattern) { | ||
else if (node.type === utils_1.AST_NODE_TYPES.AssignmentPattern) { | ||
if (isInRange(node.right, location)) { | ||
@@ -201,3 +218,2 @@ return true; | ||
description: 'Disallow the use of variables before they are defined', | ||
category: 'Variables', | ||
recommended: false, | ||
@@ -224,2 +240,3 @@ extendsBaseRule: true, | ||
ignoreTypeReferences: { type: 'boolean' }, | ||
allowNamedExports: { type: 'boolean' }, | ||
}, | ||
@@ -240,2 +257,3 @@ additionalProperties: false, | ||
ignoreTypeReferences: true, | ||
allowNamedExports: false, | ||
}, | ||
@@ -271,2 +289,6 @@ ], | ||
} | ||
function isDefinedBeforeUse(variable, reference) { | ||
return (variable.identifiers[0].range[1] <= reference.identifier.range[1] && | ||
!isInInitializer(variable, reference)); | ||
} | ||
/** | ||
@@ -278,2 +300,11 @@ * Finds and validates all variables in a given scope. | ||
const variable = reference.resolved; | ||
function report() { | ||
context.report({ | ||
node: reference.identifier, | ||
messageId: 'noUseBeforeDefine', | ||
data: { | ||
name: reference.identifier.name, | ||
}, | ||
}); | ||
} | ||
// Skips when the reference is: | ||
@@ -285,20 +316,23 @@ // - initializations. | ||
// - allowed by options. | ||
if (reference.init || | ||
!variable || | ||
variable.identifiers.length === 0 || | ||
(variable.identifiers[0].range[1] <= reference.identifier.range[1] && | ||
!isInInitializer(variable, reference)) || | ||
if (reference.init) { | ||
return; | ||
} | ||
if (!options.allowNamedExports && isNamedExports(reference)) { | ||
if (!variable || !isDefinedBeforeUse(variable, reference)) { | ||
report(); | ||
} | ||
return; | ||
} | ||
if (!variable) { | ||
return; | ||
} | ||
if (variable.identifiers.length === 0 || | ||
isDefinedBeforeUse(variable, reference) || | ||
!isForbidden(variable, reference) || | ||
isClassRefInClassDecorator(variable, reference) || | ||
reference.from.type === experimental_utils_1.TSESLint.Scope.ScopeType.functionType) { | ||
reference.from.type === utils_1.TSESLint.Scope.ScopeType.functionType) { | ||
return; | ||
} | ||
// Reports. | ||
context.report({ | ||
node: reference.identifier, | ||
messageId: 'noUseBeforeDefine', | ||
data: { | ||
name: reference.identifier.name, | ||
}, | ||
}); | ||
report(); | ||
}); | ||
@@ -305,0 +339,0 @@ scope.childScopes.forEach(findVariablesInScope); |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -21,10 +25,8 @@ if (k2 === undefined) k2 = k; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
var _a; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const no_useless_constructor_1 = __importDefault(require("eslint/lib/rules/no-useless-constructor")); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
const getESLintCoreRule_1 = require("../util/getESLintCoreRule"); | ||
const baseRule = (0, getESLintCoreRule_1.getESLintCoreRule)('no-useless-constructor'); | ||
/** | ||
@@ -40,3 +42,3 @@ * Check if method with accessibility is not useless | ||
if (node.parent && | ||
node.parent.type === experimental_utils_1.AST_NODE_TYPES.ClassBody && | ||
node.parent.type === utils_1.AST_NODE_TYPES.ClassBody && | ||
node.parent.parent && | ||
@@ -52,7 +54,10 @@ 'superClass' in node.parent.parent && | ||
/** | ||
* Check if method is not unless due to typescript parameter properties | ||
* Check if method is not useless due to typescript parameter properties and decorators | ||
*/ | ||
function checkParams(node) { | ||
return (!node.value.params || | ||
!node.value.params.some(param => param.type === experimental_utils_1.AST_NODE_TYPES.TSParameterProperty)); | ||
return !node.value.params.some(param => { | ||
var _a; | ||
return param.type === utils_1.AST_NODE_TYPES.TSParameterProperty || | ||
((_a = param.decorators) === null || _a === void 0 ? void 0 : _a.length); | ||
}); | ||
} | ||
@@ -65,8 +70,9 @@ exports.default = util.createRule({ | ||
description: 'Disallow unnecessary constructors', | ||
category: 'Best Practices', | ||
recommended: false, | ||
recommended: 'strict', | ||
extendsBaseRule: true, | ||
}, | ||
schema: no_useless_constructor_1.default.meta.schema, | ||
messages: (_a = no_useless_constructor_1.default.meta.messages) !== null && _a !== void 0 ? _a : { | ||
hasSuggestions: baseRule.meta.hasSuggestions, | ||
schema: baseRule.meta.schema, | ||
// TODO: this rule has only had messages since v7.0 - remove this when we remove support for v6 | ||
messages: (_a = baseRule.meta.messages) !== null && _a !== void 0 ? _a : { | ||
noUselessConstructor: 'Useless constructor.', | ||
@@ -77,7 +83,7 @@ }, | ||
create(context) { | ||
const rules = no_useless_constructor_1.default.create(context); | ||
const rules = baseRule.create(context); | ||
return { | ||
MethodDefinition(node) { | ||
if (node.value && | ||
node.value.type === experimental_utils_1.AST_NODE_TYPES.FunctionExpression && | ||
node.value.type === utils_1.AST_NODE_TYPES.FunctionExpression && | ||
node.value.body && | ||
@@ -84,0 +90,0 @@ checkAccessibility(node) && |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -30,4 +34,3 @@ exports.default = util.createRule({ | ||
docs: { | ||
description: 'Disallows the use of require statements except in import statements', | ||
category: 'Best Practices', | ||
description: 'Disallow `require` statements except in import statements', | ||
recommended: 'error', | ||
@@ -43,18 +46,23 @@ }, | ||
return { | ||
CallExpression(node) { | ||
'CallExpression[callee.name="require"]'(node) { | ||
var _a; | ||
const parent = ((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === experimental_utils_1.AST_NODE_TYPES.ChainExpression | ||
const parent = ((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.ChainExpression | ||
? node.parent.parent | ||
: node.parent; | ||
if (node.callee.type === experimental_utils_1.AST_NODE_TYPES.Identifier && | ||
node.callee.name === 'require' && | ||
parent && | ||
(parent.type === experimental_utils_1.AST_NODE_TYPES.VariableDeclarator || | ||
parent.type === experimental_utils_1.AST_NODE_TYPES.CallExpression || | ||
parent.type === experimental_utils_1.AST_NODE_TYPES.TSAsExpression || | ||
parent.type === experimental_utils_1.AST_NODE_TYPES.MemberExpression)) { | ||
context.report({ | ||
node, | ||
messageId: 'noVarReqs', | ||
}); | ||
if (parent && | ||
[ | ||
utils_1.AST_NODE_TYPES.CallExpression, | ||
utils_1.AST_NODE_TYPES.MemberExpression, | ||
utils_1.AST_NODE_TYPES.NewExpression, | ||
utils_1.AST_NODE_TYPES.TSAsExpression, | ||
utils_1.AST_NODE_TYPES.TSTypeAssertion, | ||
utils_1.AST_NODE_TYPES.VariableDeclarator, | ||
].includes(parent.type)) { | ||
const variable = utils_1.ASTUtils.findVariable(context.getScope(), 'require'); | ||
if (!(variable === null || variable === void 0 ? void 0 : variable.identifiers.length)) { | ||
context.report({ | ||
node, | ||
messageId: 'noVarReqs', | ||
}); | ||
} | ||
} | ||
@@ -61,0 +69,0 @@ }, |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const tsutils = __importStar(require("tsutils")); | ||
@@ -31,11 +35,9 @@ const ts = __importStar(require("typescript")); | ||
docs: { | ||
category: 'Best Practices', | ||
description: 'Prefers a non-null assertion over explicit type cast when possible', | ||
recommended: false, | ||
description: 'Enforce non-null assertions over explicit type casts', | ||
recommended: 'strict', | ||
requiresTypeChecking: true, | ||
suggestion: true, | ||
}, | ||
fixable: 'code', | ||
messages: { | ||
preferNonNullAssertion: 'Use a ! assertion to more succintly remove null and undefined from the type.', | ||
preferNonNullAssertion: 'Use a ! assertion to more succinctly remove null and undefined from the type.', | ||
}, | ||
@@ -57,7 +59,27 @@ schema: [], | ||
}; | ||
const couldBeNullish = (type) => { | ||
if (type.flags & ts.TypeFlags.TypeParameter) { | ||
const constraint = type.getConstraint(); | ||
return constraint == null || couldBeNullish(constraint); | ||
} | ||
else if (tsutils.isUnionType(type)) { | ||
for (const part of type.types) { | ||
if (couldBeNullish(part)) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
else { | ||
return ((type.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined)) !== 0); | ||
} | ||
}; | ||
const sameTypeWithoutNullish = (assertedTypes, originalTypes) => { | ||
const nonNullishOriginalTypes = originalTypes.filter(type => type.flags !== ts.TypeFlags.Null && | ||
type.flags !== ts.TypeFlags.Undefined); | ||
const nonNullishOriginalTypes = originalTypes.filter(type => (type.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined)) === 0); | ||
if (nonNullishOriginalTypes.length === originalTypes.length) { | ||
return false; | ||
} | ||
for (const assertedType of assertedTypes) { | ||
if (!nonNullishOriginalTypes.includes(assertedType)) { | ||
if (couldBeNullish(assertedType) || | ||
!nonNullishOriginalTypes.includes(assertedType)) { | ||
return false; | ||
@@ -74,4 +96,4 @@ } | ||
const isConstAssertion = (node) => { | ||
return (node.typeAnnotation.type === experimental_utils_1.AST_NODE_TYPES.TSTypeReference && | ||
node.typeAnnotation.typeName.type === experimental_utils_1.AST_NODE_TYPES.Identifier && | ||
return (node.typeAnnotation.type === utils_1.AST_NODE_TYPES.TSTypeReference && | ||
node.typeAnnotation.typeName.type === utils_1.AST_NODE_TYPES.Identifier && | ||
node.typeAnnotation.typeName.name === 'const'); | ||
@@ -78,0 +100,0 @@ }; |
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const object_curly_spacing_1 = __importDefault(require("eslint/lib/rules/object-curly-spacing")); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util_1 = require("../util"); | ||
exports.default = util_1.createRule({ | ||
const getESLintCoreRule_1 = require("../util/getESLintCoreRule"); | ||
const baseRule = (0, getESLintCoreRule_1.getESLintCoreRule)('object-curly-spacing'); | ||
exports.default = (0, util_1.createRule)({ | ||
name: 'object-curly-spacing', | ||
meta: Object.assign(Object.assign({}, object_curly_spacing_1.default.meta), { docs: { | ||
// eslint-disable-next-line eslint-plugin/prefer-message-ids,eslint-plugin/require-meta-type,eslint-plugin/require-meta-schema,eslint-plugin/require-meta-fixable -- all in base rule - https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/issues/274 | ||
meta: Object.assign(Object.assign({}, baseRule.meta), { docs: { | ||
description: 'Enforce consistent spacing inside braces', | ||
category: 'Stylistic Issues', | ||
recommended: false, | ||
@@ -19,3 +17,5 @@ extendsBaseRule: true, | ||
create(context) { | ||
const spaced = context.options[0] === 'always'; | ||
// eslint-disable-next-line no-restricted-syntax -- Use raw options for extended rules. | ||
const [firstOption, secondOption] = context.options; | ||
const spaced = firstOption === 'always'; | ||
const sourceCode = context.getSourceCode(); | ||
@@ -30,5 +30,3 @@ /** | ||
function isOptionSet(option) { | ||
return context.options[1] | ||
? context.options[1][option] === !spaced | ||
: false; | ||
return secondOption ? secondOption[option] === !spaced : false; | ||
} | ||
@@ -130,8 +128,10 @@ const options = { | ||
function validateBraceSpacing(node, first, second, penultimate, last) { | ||
if (util_1.isTokenOnSameLine(first, second)) { | ||
if ((0, util_1.isTokenOnSameLine)(first, second)) { | ||
const firstSpaced = sourceCode.isSpaceBetween(first, second); | ||
const secondType = sourceCode.getNodeByRangeIndex(second.range[0]) | ||
.type; | ||
const secondType = sourceCode.getNodeByRangeIndex(second.range[0]).type; | ||
const openingCurlyBraceMustBeSpaced = options.arraysInObjectsException && | ||
secondType === experimental_utils_1.AST_NODE_TYPES.TSIndexSignature | ||
[ | ||
utils_1.AST_NODE_TYPES.TSMappedType, | ||
utils_1.AST_NODE_TYPES.TSIndexSignature, | ||
].includes(secondType) | ||
? !options.spaced | ||
@@ -144,17 +144,22 @@ : options.spaced; | ||
firstSpaced && | ||
second.type !== experimental_utils_1.AST_TOKEN_TYPES.Line) { | ||
second.type !== utils_1.AST_TOKEN_TYPES.Line) { | ||
reportNoBeginningSpace(node, first); | ||
} | ||
} | ||
if (util_1.isTokenOnSameLine(penultimate, last)) { | ||
if ((0, util_1.isTokenOnSameLine)(penultimate, last)) { | ||
const shouldCheckPenultimate = (options.arraysInObjectsException && | ||
util_1.isClosingBracketToken(penultimate)) || | ||
(0, util_1.isClosingBracketToken)(penultimate)) || | ||
(options.objectsInObjectsException && | ||
util_1.isClosingBraceToken(penultimate)); | ||
const penultimateType = shouldCheckPenultimate && | ||
sourceCode.getNodeByRangeIndex(penultimate.range[0]).type; | ||
(0, util_1.isClosingBraceToken)(penultimate)); | ||
const penultimateType = shouldCheckPenultimate | ||
? sourceCode.getNodeByRangeIndex(penultimate.range[0]).type | ||
: undefined; | ||
const closingCurlyBraceMustBeSpaced = (options.arraysInObjectsException && | ||
penultimateType === experimental_utils_1.AST_NODE_TYPES.TSTupleType) || | ||
penultimateType === utils_1.AST_NODE_TYPES.TSTupleType) || | ||
(options.objectsInObjectsException && | ||
penultimateType === experimental_utils_1.AST_NODE_TYPES.TSTypeLiteral) | ||
penultimateType !== undefined && | ||
[ | ||
utils_1.AST_NODE_TYPES.TSMappedType, | ||
utils_1.AST_NODE_TYPES.TSTypeLiteral, | ||
].includes(penultimateType)) | ||
? !options.spaced | ||
@@ -189,4 +194,15 @@ : options.spaced; | ||
//-------------------------------------------------------------------------- | ||
const rules = object_curly_spacing_1.default.create(context); | ||
return Object.assign(Object.assign({}, rules), { TSTypeLiteral(node) { | ||
const rules = baseRule.create(context); | ||
return Object.assign(Object.assign({}, rules), { TSMappedType(node) { | ||
const first = sourceCode.getFirstToken(node); | ||
const last = sourceCode.getLastToken(node); | ||
const second = sourceCode.getTokenAfter(first, { | ||
includeComments: true, | ||
}); | ||
const penultimate = sourceCode.getTokenBefore(last, { | ||
includeComments: true, | ||
}); | ||
validateBraceSpacing(node, first, second, penultimate, last); | ||
}, | ||
TSTypeLiteral(node) { | ||
if (node.members.length === 0) { | ||
@@ -193,0 +209,0 @@ return; |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -30,8 +34,7 @@ exports.default = util.createRule({ | ||
docs: { | ||
description: 'Prefer usage of `as const` over literal type', | ||
category: 'Best Practices', | ||
description: 'Enforce the use of `as const` over literal type', | ||
recommended: 'error', | ||
suggestion: true, | ||
}, | ||
fixable: 'code', | ||
hasSuggestions: true, | ||
messages: { | ||
@@ -47,4 +50,4 @@ preferConstAssertion: 'Expected a `const` instead of a literal type assertion.', | ||
function compareTypes(valueNode, typeNode, canFix) { | ||
if (valueNode.type === experimental_utils_1.AST_NODE_TYPES.Literal && | ||
typeNode.type === experimental_utils_1.AST_NODE_TYPES.TSLiteralType && | ||
if (valueNode.type === utils_1.AST_NODE_TYPES.Literal && | ||
typeNode.type === utils_1.AST_NODE_TYPES.TSLiteralType && | ||
'raw' in typeNode.literal && | ||
@@ -83,2 +86,7 @@ valueNode.raw === typeNode.literal.raw) { | ||
}, | ||
PropertyDefinition(node) { | ||
if (node.value && node.typeAnnotation) { | ||
compareTypes(node.value, node.typeAnnotation.typeAnnotation, false); | ||
} | ||
}, | ||
VariableDeclarator(node) { | ||
@@ -85,0 +93,0 @@ if (node.init && node.id.typeAnnotation) { |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -28,9 +32,8 @@ if (k2 === undefined) k2 = k; | ||
docs: { | ||
description: 'Prefer initializing each enums member value', | ||
category: 'Best Practices', | ||
description: 'Require each enum member value to be explicitly initialized', | ||
recommended: false, | ||
suggestion: true, | ||
}, | ||
hasSuggestions: true, | ||
messages: { | ||
defineInitializer: "The value of the member '{{ name }}' should be explicitly defined", | ||
defineInitializer: "The value of the member '{{ name }}' should be explicitly defined.", | ||
defineInitializerSuggestion: 'Can be fixed to {{ name }} = {{ suggested }}', | ||
@@ -37,0 +40,0 @@ }, |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -30,5 +34,4 @@ exports.default = util.createRule({ | ||
docs: { | ||
description: 'Prefer a ‘for-of’ loop over a standard ‘for’ loop if the index is only used to access the array being iterated', | ||
category: 'Stylistic Issues', | ||
recommended: false, | ||
description: 'Enforce the use of `for-of` loop over the standard `for` loop where possible', | ||
recommended: 'strict', | ||
}, | ||
@@ -43,4 +46,3 @@ messages: { | ||
function isSingleVariableDeclaration(node) { | ||
return (node !== null && | ||
node.type === experimental_utils_1.AST_NODE_TYPES.VariableDeclaration && | ||
return ((node === null || node === void 0 ? void 0 : node.type) === utils_1.AST_NODE_TYPES.VariableDeclaration && | ||
node.kind !== 'const' && | ||
@@ -50,16 +52,15 @@ node.declarations.length === 1); | ||
function isLiteral(node, value) { | ||
return node.type === experimental_utils_1.AST_NODE_TYPES.Literal && node.value === value; | ||
return node.type === utils_1.AST_NODE_TYPES.Literal && node.value === value; | ||
} | ||
function isZeroInitialized(node) { | ||
return node.init !== null && isLiteral(node.init, 0); | ||
return node.init != null && isLiteral(node.init, 0); | ||
} | ||
function isMatchingIdentifier(node, name) { | ||
return node.type === experimental_utils_1.AST_NODE_TYPES.Identifier && node.name === name; | ||
return node.type === utils_1.AST_NODE_TYPES.Identifier && node.name === name; | ||
} | ||
function isLessThanLengthExpression(node, name) { | ||
if (node !== null && | ||
node.type === experimental_utils_1.AST_NODE_TYPES.BinaryExpression && | ||
if ((node === null || node === void 0 ? void 0 : node.type) === utils_1.AST_NODE_TYPES.BinaryExpression && | ||
node.operator === '<' && | ||
isMatchingIdentifier(node.left, name) && | ||
node.right.type === experimental_utils_1.AST_NODE_TYPES.MemberExpression && | ||
node.right.type === utils_1.AST_NODE_TYPES.MemberExpression && | ||
isMatchingIdentifier(node.right.property, 'length')) { | ||
@@ -75,6 +76,6 @@ return node.right.object; | ||
switch (node.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.UpdateExpression: | ||
case utils_1.AST_NODE_TYPES.UpdateExpression: | ||
// x++ or ++x | ||
return (node.operator === '++' && isMatchingIdentifier(node.argument, name)); | ||
case experimental_utils_1.AST_NODE_TYPES.AssignmentExpression: | ||
case utils_1.AST_NODE_TYPES.AssignmentExpression: | ||
if (isMatchingIdentifier(node.left, name)) { | ||
@@ -88,3 +89,3 @@ if (node.operator === '+=') { | ||
const expr = node.right; | ||
return (expr.type === experimental_utils_1.AST_NODE_TYPES.BinaryExpression && | ||
return (expr.type === utils_1.AST_NODE_TYPES.BinaryExpression && | ||
expr.operator === '+' && | ||
@@ -110,3 +111,3 @@ ((isMatchingIdentifier(expr.left, name) && | ||
// a[i] = 1, a[i] += 1, etc. | ||
if (parent.type === experimental_utils_1.AST_NODE_TYPES.AssignmentExpression && | ||
if (parent.type === utils_1.AST_NODE_TYPES.AssignmentExpression && | ||
parent.left === node) { | ||
@@ -116,3 +117,3 @@ return true; | ||
// delete a[i] | ||
if (parent.type === experimental_utils_1.AST_NODE_TYPES.UnaryExpression && | ||
if (parent.type === utils_1.AST_NODE_TYPES.UnaryExpression && | ||
parent.operator === 'delete' && | ||
@@ -123,3 +124,3 @@ parent.argument === node) { | ||
// a[i]++, --a[i], etc. | ||
if (parent.type === experimental_utils_1.AST_NODE_TYPES.UpdateExpression && | ||
if (parent.type === utils_1.AST_NODE_TYPES.UpdateExpression && | ||
parent.argument === node) { | ||
@@ -129,13 +130,13 @@ return true; | ||
// [a[i]] = [0] | ||
if (parent.type === experimental_utils_1.AST_NODE_TYPES.ArrayPattern) { | ||
if (parent.type === utils_1.AST_NODE_TYPES.ArrayPattern) { | ||
return true; | ||
} | ||
// [...a[i]] = [0] | ||
if (parent.type === experimental_utils_1.AST_NODE_TYPES.RestElement) { | ||
if (parent.type === utils_1.AST_NODE_TYPES.RestElement) { | ||
return true; | ||
} | ||
// ({ foo: a[i] }) = { foo: 0 } | ||
if (parent.type === experimental_utils_1.AST_NODE_TYPES.Property && | ||
if (parent.type === utils_1.AST_NODE_TYPES.Property && | ||
parent.value === node && | ||
((_a = parent.parent) === null || _a === void 0 ? void 0 : _a.type) === experimental_utils_1.AST_NODE_TYPES.ObjectExpression && | ||
((_a = parent.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.ObjectExpression && | ||
isAssignee(parent.parent)) { | ||
@@ -154,3 +155,4 @@ return true; | ||
(node !== undefined && | ||
node.type === experimental_utils_1.AST_NODE_TYPES.MemberExpression && | ||
node.type === utils_1.AST_NODE_TYPES.MemberExpression && | ||
node.object.type !== utils_1.AST_NODE_TYPES.ThisExpression && | ||
node.property === id && | ||
@@ -169,3 +171,3 @@ sourceCode.getText(node.object) === arrayText && | ||
!isZeroInitialized(declarator) || | ||
declarator.id.type !== experimental_utils_1.AST_NODE_TYPES.Identifier) { | ||
declarator.id.type !== utils_1.AST_NODE_TYPES.Identifier) { | ||
return; | ||
@@ -172,0 +174,0 @@ } |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -23,7 +27,7 @@ if (k2 === undefined) k2 = k; | ||
exports.phrases = void 0; | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
exports.phrases = { | ||
[experimental_utils_1.AST_NODE_TYPES.TSTypeLiteral]: 'Type literal', | ||
[experimental_utils_1.AST_NODE_TYPES.TSInterfaceDeclaration]: 'Interface', | ||
[utils_1.AST_NODE_TYPES.TSTypeLiteral]: 'Type literal', | ||
[utils_1.AST_NODE_TYPES.TSInterfaceDeclaration]: 'Interface', | ||
}; | ||
@@ -34,5 +38,4 @@ exports.default = util.createRule({ | ||
docs: { | ||
description: 'Use function types instead of interfaces with call signatures', | ||
category: 'Best Practices', | ||
recommended: false, | ||
description: 'Enforce using function types instead of interfaces with call signatures', | ||
recommended: 'strict', | ||
}, | ||
@@ -62,3 +65,3 @@ fixable: 'code', | ||
const expr = node.extends[0].expression; | ||
return (expr.type !== experimental_utils_1.AST_NODE_TYPES.Identifier || expr.name !== 'Function'); | ||
return (expr.type !== utils_1.AST_NODE_TYPES.Identifier || expr.name !== 'Function'); | ||
} | ||
@@ -73,5 +76,5 @@ /** | ||
switch (parent.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.TSUnionType: | ||
case experimental_utils_1.AST_NODE_TYPES.TSIntersectionType: | ||
case experimental_utils_1.AST_NODE_TYPES.TSArrayType: | ||
case utils_1.AST_NODE_TYPES.TSUnionType: | ||
case utils_1.AST_NODE_TYPES.TSIntersectionType: | ||
case utils_1.AST_NODE_TYPES.TSArrayType: | ||
return true; | ||
@@ -83,29 +86,2 @@ default: | ||
/** | ||
* @param call The call signature causing the diagnostic | ||
* @param parent The parent of the call | ||
* @returns 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)}`; | ||
const lastChar = suggestion.endsWith(';') ? ';' : ''; | ||
if (lastChar) { | ||
suggestion = suggestion.slice(0, -1); | ||
} | ||
if (shouldWrapSuggestion(parent.parent)) { | ||
suggestion = `(${suggestion})`; | ||
} | ||
if (parent.type === experimental_utils_1.AST_NODE_TYPES.TSInterfaceDeclaration) { | ||
if (typeof parent.typeParameters !== 'undefined') { | ||
return `type ${sourceCode | ||
.getText() | ||
.slice(parent.id.range[0], parent.typeParameters.range[1])} = ${suggestion}${lastChar}`; | ||
} | ||
return `type ${parent.id.name} = ${suggestion}${lastChar}`; | ||
} | ||
return suggestion; | ||
} | ||
/** | ||
* @param member The TypeElement being checked | ||
@@ -116,8 +92,7 @@ * @param node The parent of member being checked | ||
function checkMember(member, node, tsThisTypes = null) { | ||
if ((member.type === experimental_utils_1.AST_NODE_TYPES.TSCallSignatureDeclaration || | ||
member.type === experimental_utils_1.AST_NODE_TYPES.TSConstructSignatureDeclaration) && | ||
typeof member.returnType !== 'undefined') { | ||
if (tsThisTypes !== null && | ||
tsThisTypes.length > 0 && | ||
node.type === experimental_utils_1.AST_NODE_TYPES.TSInterfaceDeclaration) { | ||
if ((member.type === utils_1.AST_NODE_TYPES.TSCallSignatureDeclaration || | ||
member.type === utils_1.AST_NODE_TYPES.TSConstructSignatureDeclaration) && | ||
member.returnType !== undefined) { | ||
if ((tsThisTypes === null || tsThisTypes === void 0 ? void 0 : tsThisTypes.length) && | ||
node.type === utils_1.AST_NODE_TYPES.TSInterfaceDeclaration) { | ||
// the message can be confusing if we don't point directly to the `this` node instead of the whole member | ||
@@ -134,9 +109,65 @@ // and in favour of generating at most one error we'll only report the first occurrence of `this` if there are multiple | ||
} | ||
const suggestion = renderSuggestion(member, node); | ||
const fixStart = node.type === experimental_utils_1.AST_NODE_TYPES.TSTypeLiteral | ||
? node.range[0] | ||
: sourceCode | ||
.getTokens(node) | ||
.filter(token => token.type === experimental_utils_1.AST_TOKEN_TYPES.Keyword && | ||
token.value === 'interface')[0].range[0]; | ||
const fixable = node.parent && | ||
node.parent.type === utils_1.AST_NODE_TYPES.ExportDefaultDeclaration; | ||
const fix = fixable | ||
? null | ||
: (fixer) => { | ||
const fixes = []; | ||
const start = member.range[0]; | ||
const colonPos = member.returnType.range[0] - start; | ||
const text = sourceCode.getText().slice(start, member.range[1]); | ||
const comments = sourceCode | ||
.getCommentsBefore(member) | ||
.concat(sourceCode.getCommentsAfter(member)); | ||
let suggestion = `${text.slice(0, colonPos)} =>${text.slice(colonPos + 1)}`; | ||
const lastChar = suggestion.endsWith(';') ? ';' : ''; | ||
if (lastChar) { | ||
suggestion = suggestion.slice(0, -1); | ||
} | ||
if (shouldWrapSuggestion(node.parent)) { | ||
suggestion = `(${suggestion})`; | ||
} | ||
if (node.type === utils_1.AST_NODE_TYPES.TSInterfaceDeclaration) { | ||
if (node.typeParameters !== undefined) { | ||
suggestion = `type ${sourceCode | ||
.getText() | ||
.slice(node.id.range[0], node.typeParameters.range[1])} = ${suggestion}${lastChar}`; | ||
} | ||
else { | ||
suggestion = `type ${node.id.name} = ${suggestion}${lastChar}`; | ||
} | ||
} | ||
const isParentExported = node.parent && | ||
node.parent.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration; | ||
if (node.type === utils_1.AST_NODE_TYPES.TSInterfaceDeclaration && | ||
isParentExported) { | ||
const commentsText = comments.reduce((text, comment) => { | ||
return (text + | ||
(comment.type === utils_1.AST_TOKEN_TYPES.Line | ||
? `//${comment.value}` | ||
: `/*${comment.value}*/`) + | ||
'\n'); | ||
}, ''); | ||
// comments should move before export and not between export and interface declaration | ||
fixes.push(fixer.insertTextBefore(node.parent, commentsText)); | ||
} | ||
else { | ||
comments.forEach(comment => { | ||
let commentText = comment.type === utils_1.AST_TOKEN_TYPES.Line | ||
? `//${comment.value}` | ||
: `/*${comment.value}*/`; | ||
const isCommentOnTheSameLine = comment.loc.start.line === member.loc.start.line; | ||
if (!isCommentOnTheSameLine) { | ||
commentText += '\n'; | ||
} | ||
else { | ||
commentText += ' '; | ||
} | ||
suggestion = commentText + suggestion; | ||
}); | ||
} | ||
const fixStart = node.range[0]; | ||
fixes.push(fixer.replaceTextRange([fixStart, node.range[1]], suggestion)); | ||
return fixes; | ||
}; | ||
context.report({ | ||
@@ -148,5 +179,3 @@ node: member, | ||
}, | ||
fix(fixer) { | ||
return fixer.replaceTextRange([fixStart, node.range[1]], suggestion); | ||
}, | ||
fix, | ||
}); | ||
@@ -166,3 +195,3 @@ } | ||
// we don't want to incorrectly say "it refers to name" while typescript says it's completely invalid. | ||
if (literalNesting === 0 && tsThisTypes !== null) { | ||
if (literalNesting === 0 && tsThisTypes != null) { | ||
tsThisTypes.push(node); | ||
@@ -169,0 +198,0 @@ } |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,7 +26,7 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const regexpp_1 = require("regexpp"); | ||
const regexpp_1 = require("@eslint-community/regexpp"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const ts = __importStar(require("typescript")); | ||
const util_1 = require("../util"); | ||
exports.default = util_1.createRule({ | ||
exports.default = (0, util_1.createRule)({ | ||
name: 'prefer-includes', | ||
@@ -34,4 +38,3 @@ defaultOptions: [], | ||
description: 'Enforce `includes` method over `indexOf` method', | ||
category: 'Best Practices', | ||
recommended: false, | ||
recommended: 'strict', | ||
requiresTypeChecking: true, | ||
@@ -48,7 +51,7 @@ }, | ||
const globalScope = context.getScope(); | ||
const services = util_1.getParserServices(context); | ||
const services = (0, util_1.getParserServices)(context); | ||
const types = services.program.getTypeChecker(); | ||
function isNumber(node, value) { | ||
const evaluated = util_1.getStaticValue(node, globalScope); | ||
return evaluated !== null && evaluated.value === value; | ||
const evaluated = (0, util_1.getStaticValue)(node, globalScope); | ||
return evaluated != null && evaluated.value === value; | ||
} | ||
@@ -103,7 +106,7 @@ function isPositiveCheck(node) { | ||
function parseRegExp(node) { | ||
const evaluated = util_1.getStaticValue(node, globalScope); | ||
const evaluated = (0, util_1.getStaticValue)(node, globalScope); | ||
if (evaluated == null || !(evaluated.value instanceof RegExp)) { | ||
return null; | ||
} | ||
const { pattern, flags } = regexpp_1.parseRegExpLiteral(evaluated.value); | ||
const { pattern, flags } = (0, regexpp_1.parseRegExpLiteral)(evaluated.value); | ||
if (pattern.alternatives.length !== 1 || | ||
@@ -122,58 +125,74 @@ flags.ignoreCase || | ||
} | ||
return { | ||
[[ | ||
// a.indexOf(b) !== 1 | ||
"BinaryExpression > CallExpression.left > MemberExpression.callee[property.name='indexOf'][computed=false]", | ||
// a?.indexOf(b) !== 1 | ||
"BinaryExpression > ChainExpression.left > CallExpression > MemberExpression.callee[property.name='indexOf'][computed=false]", | ||
].join(', ')](node) { | ||
var _a, _b, _c; | ||
// Check if the comparison is equivalent to `includes()`. | ||
const callNode = node.parent; | ||
const compareNode = (((_a = callNode.parent) === null || _a === void 0 ? void 0 : _a.type) === | ||
experimental_utils_1.AST_NODE_TYPES.ChainExpression | ||
? callNode.parent.parent | ||
: callNode.parent); | ||
const negative = isNegativeCheck(compareNode); | ||
if (!negative && !isPositiveCheck(compareNode)) { | ||
function escapeString(str) { | ||
const EscapeMap = { | ||
'\0': '\\0', | ||
"'": "\\'", | ||
'\\': '\\\\', | ||
'\n': '\\n', | ||
'\r': '\\r', | ||
'\v': '\\v', | ||
'\t': '\\t', | ||
'\f': '\\f', | ||
// "\b" cause unexpected replacements | ||
// '\b': '\\b', | ||
}; | ||
const replaceRegex = new RegExp(Object.values(EscapeMap).join('|'), 'g'); | ||
return str.replace(replaceRegex, char => EscapeMap[char]); | ||
} | ||
function checkArrayIndexOf(node, allowFixing) { | ||
var _a, _b, _c; | ||
// Check if the comparison is equivalent to `includes()`. | ||
const callNode = node.parent; | ||
const compareNode = (((_a = callNode.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.ChainExpression | ||
? callNode.parent.parent | ||
: callNode.parent); | ||
const negative = isNegativeCheck(compareNode); | ||
if (!negative && !isPositiveCheck(compareNode)) { | ||
return; | ||
} | ||
// Get the symbol of `indexOf` method. | ||
const tsNode = services.esTreeNodeToTSNodeMap.get(node.property); | ||
const indexofMethodDeclarations = (_b = types | ||
.getSymbolAtLocation(tsNode)) === null || _b === void 0 ? void 0 : _b.getDeclarations(); | ||
if (indexofMethodDeclarations == null || | ||
indexofMethodDeclarations.length === 0) { | ||
return; | ||
} | ||
// Check if every declaration of `indexOf` method has `includes` method | ||
// and the two methods have the same parameters. | ||
for (const instanceofMethodDecl of indexofMethodDeclarations) { | ||
const typeDecl = instanceofMethodDecl.parent; | ||
const type = types.getTypeAtLocation(typeDecl); | ||
const includesMethodDecl = (_c = type | ||
.getProperty('includes')) === null || _c === void 0 ? void 0 : _c.getDeclarations(); | ||
if (includesMethodDecl == null || | ||
!includesMethodDecl.some(includesMethodDecl => hasSameParameters(includesMethodDecl, instanceofMethodDecl))) { | ||
return; | ||
} | ||
// Get the symbol of `indexOf` method. | ||
const tsNode = services.esTreeNodeToTSNodeMap.get(node.property); | ||
const indexofMethodDeclarations = (_b = types | ||
.getSymbolAtLocation(tsNode)) === null || _b === void 0 ? void 0 : _b.getDeclarations(); | ||
if (indexofMethodDeclarations == null || | ||
indexofMethodDeclarations.length === 0) { | ||
return; | ||
} | ||
// Check if every declaration of `indexOf` method has `includes` method | ||
// and the two methods have the same parameters. | ||
for (const instanceofMethodDecl of indexofMethodDeclarations) { | ||
const typeDecl = instanceofMethodDecl.parent; | ||
const type = types.getTypeAtLocation(typeDecl); | ||
const includesMethodDecl = (_c = type | ||
.getProperty('includes')) === null || _c === void 0 ? void 0 : _c.getDeclarations(); | ||
if (includesMethodDecl == null || | ||
!includesMethodDecl.some(includesMethodDecl => hasSameParameters(includesMethodDecl, instanceofMethodDecl))) { | ||
return; | ||
} | ||
// Report it. | ||
context.report(Object.assign({ node: compareNode, messageId: 'preferIncludes' }, (allowFixing && { | ||
*fix(fixer) { | ||
if (negative) { | ||
yield fixer.insertTextBefore(callNode, '!'); | ||
} | ||
} | ||
// Report it. | ||
context.report({ | ||
node: compareNode, | ||
messageId: 'preferIncludes', | ||
*fix(fixer) { | ||
if (negative) { | ||
yield fixer.insertTextBefore(callNode, '!'); | ||
} | ||
yield fixer.replaceText(node.property, 'includes'); | ||
yield fixer.removeRange([callNode.range[1], compareNode.range[1]]); | ||
}, | ||
}); | ||
yield fixer.replaceText(node.property, 'includes'); | ||
yield fixer.removeRange([callNode.range[1], compareNode.range[1]]); | ||
}, | ||
}))); | ||
} | ||
return { | ||
// a.indexOf(b) !== 1 | ||
"BinaryExpression > CallExpression.left > MemberExpression.callee[property.name='indexOf'][computed=false]"(node) { | ||
checkArrayIndexOf(node, /* allowFixing */ true); | ||
}, | ||
// a?.indexOf(b) !== 1 | ||
"BinaryExpression > ChainExpression.left > CallExpression > MemberExpression.callee[property.name='indexOf'][computed=false]"(node) { | ||
checkArrayIndexOf(node, /* allowFixing */ false); | ||
}, | ||
// /bar/.test(foo) | ||
'CallExpression > MemberExpression.callee[property.name="test"][computed=false]'(node) { | ||
'CallExpression[arguments.length=1] > MemberExpression.callee[property.name="test"][computed=false]'(node) { | ||
var _a; | ||
const callNode = node.parent; | ||
const text = callNode.arguments.length === 1 ? parseRegExp(node.object) : null; | ||
const text = parseRegExp(node.object); | ||
if (text == null) { | ||
@@ -185,3 +204,3 @@ return; | ||
const tsNode = services.esTreeNodeToTSNodeMap.get(argument); | ||
const type = util_1.getConstrainedTypeAtLocation(types, tsNode); | ||
const type = (0, util_1.getConstrainedTypeAtLocation)(types, tsNode); | ||
const includesMethodDecl = (_a = type | ||
@@ -197,8 +216,9 @@ .getProperty('includes')) === null || _a === void 0 ? void 0 : _a.getDeclarations(); | ||
const argNode = callNode.arguments[0]; | ||
const needsParen = argNode.type !== experimental_utils_1.AST_NODE_TYPES.Literal && | ||
argNode.type !== experimental_utils_1.AST_NODE_TYPES.TemplateLiteral && | ||
argNode.type !== experimental_utils_1.AST_NODE_TYPES.Identifier && | ||
argNode.type !== experimental_utils_1.AST_NODE_TYPES.MemberExpression && | ||
argNode.type !== experimental_utils_1.AST_NODE_TYPES.CallExpression; | ||
const needsParen = argNode.type !== utils_1.AST_NODE_TYPES.Literal && | ||
argNode.type !== utils_1.AST_NODE_TYPES.TemplateLiteral && | ||
argNode.type !== utils_1.AST_NODE_TYPES.Identifier && | ||
argNode.type !== utils_1.AST_NODE_TYPES.MemberExpression && | ||
argNode.type !== utils_1.AST_NODE_TYPES.CallExpression; | ||
yield fixer.removeRange([callNode.range[0], argNode.range[0]]); | ||
yield fixer.removeRange([argNode.range[1], callNode.range[1]]); | ||
if (needsParen) { | ||
@@ -208,3 +228,3 @@ yield fixer.insertTextBefore(argNode, '('); | ||
} | ||
yield fixer.insertTextAfter(argNode, `${node.optional ? '?.' : '.'}includes(${JSON.stringify(text)}`); | ||
yield fixer.insertTextAfter(argNode, `${node.optional ? '?.' : '.'}includes('${escapeString(text)}')`); | ||
}, | ||
@@ -211,0 +231,0 @@ }); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util_1 = require("../util"); | ||
exports.default = util_1.createRule({ | ||
exports.default = (0, util_1.createRule)({ | ||
name: 'prefer-literal-enum-member', | ||
@@ -10,5 +10,4 @@ meta: { | ||
docs: { | ||
description: 'Require that all enum members be literal values to prevent unintended enum member name shadow issues', | ||
category: 'Best Practices', | ||
recommended: false, | ||
description: 'Require all enum members to be literal values', | ||
recommended: 'strict', | ||
requiresTypeChecking: false, | ||
@@ -19,6 +18,20 @@ }, | ||
}, | ||
schema: [], | ||
schema: [ | ||
{ | ||
type: 'object', | ||
properties: { | ||
allowBitwiseExpressions: { | ||
type: 'boolean', | ||
}, | ||
}, | ||
additionalProperties: false, | ||
}, | ||
], | ||
}, | ||
defaultOptions: [], | ||
create(context) { | ||
defaultOptions: [ | ||
{ | ||
allowBitwiseExpressions: false, | ||
}, | ||
], | ||
create(context, [{ allowBitwiseExpressions }]) { | ||
return { | ||
@@ -31,7 +44,7 @@ TSEnumMember(node) { | ||
// any old literal | ||
if (node.initializer.type === experimental_utils_1.AST_NODE_TYPES.Literal) { | ||
if (node.initializer.type === utils_1.AST_NODE_TYPES.Literal) { | ||
return; | ||
} | ||
// TemplateLiteral without expressions | ||
if (node.initializer.type === experimental_utils_1.AST_NODE_TYPES.TemplateLiteral && | ||
if (node.initializer.type === utils_1.AST_NODE_TYPES.TemplateLiteral && | ||
node.initializer.expressions.length === 0) { | ||
@@ -41,7 +54,15 @@ return; | ||
// -1 and +1 | ||
if (node.initializer.type === experimental_utils_1.AST_NODE_TYPES.UnaryExpression && | ||
['+', '-'].includes(node.initializer.operator) && | ||
node.initializer.argument.type === experimental_utils_1.AST_NODE_TYPES.Literal) { | ||
if (node.initializer.type === utils_1.AST_NODE_TYPES.UnaryExpression && | ||
node.initializer.argument.type === utils_1.AST_NODE_TYPES.Literal && | ||
(['+', '-'].includes(node.initializer.operator) || | ||
(allowBitwiseExpressions && node.initializer.operator === '~'))) { | ||
return; | ||
} | ||
if (allowBitwiseExpressions && | ||
node.initializer.type === utils_1.AST_NODE_TYPES.BinaryExpression && | ||
['|', '&', '^', '<<', '>>', '>>>'].includes(node.initializer.operator) && | ||
node.initializer.left.type === utils_1.AST_NODE_TYPES.Literal && | ||
node.initializer.right.type === utils_1.AST_NODE_TYPES.Literal) { | ||
return; | ||
} | ||
context.report({ | ||
@@ -48,0 +69,0 @@ node: node.id, |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -30,4 +34,3 @@ exports.default = util.createRule({ | ||
docs: { | ||
description: 'Require the use of the `namespace` keyword instead of the `module` keyword to declare custom TypeScript modules', | ||
category: 'Best Practices', | ||
description: 'Require using `namespace` keyword over `module` keyword to declare custom TypeScript modules', | ||
recommended: 'error', | ||
@@ -47,3 +50,3 @@ }, | ||
// Do nothing if the name is a string. | ||
if (!node.id || node.id.type === experimental_utils_1.AST_NODE_TYPES.Literal) { | ||
if (!node.id || node.id.type === utils_1.AST_NODE_TYPES.Literal) { | ||
return; | ||
@@ -54,3 +57,3 @@ } | ||
if (moduleType && | ||
moduleType.type === experimental_utils_1.AST_TOKEN_TYPES.Identifier && | ||
moduleType.type === utils_1.AST_TOKEN_TYPES.Identifier && | ||
moduleType.value === 'module') { | ||
@@ -57,0 +60,0 @@ context.report({ |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,5 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const tsutils = __importStar(require("tsutils")); | ||
const ts = __importStar(require("typescript")); | ||
const util = __importStar(require("../util")); | ||
@@ -30,11 +36,12 @@ exports.default = util.createRule({ | ||
docs: { | ||
description: 'Enforce the usage of the nullish coalescing operator instead of logical chaining', | ||
category: 'Best Practices', | ||
recommended: false, | ||
suggestion: true, | ||
description: 'Enforce using the nullish coalescing operator instead of logical chaining', | ||
recommended: 'strict', | ||
requiresTypeChecking: true, | ||
}, | ||
hasSuggestions: true, | ||
messages: { | ||
preferNullish: 'Prefer using nullish coalescing operator (`??`) instead of a logical or (`||`), as it is a safer operator.', | ||
preferNullishOverOr: 'Prefer using nullish coalescing operator (`??`) instead of a logical or (`||`), as it is a safer operator.', | ||
preferNullishOverTernary: 'Prefer using nullish coalescing operator (`??`) instead of a ternary expression, as it is simpler to read.', | ||
suggestNullish: 'Fix to nullish coalescing operator (`??`).', | ||
noStrictNullCheck: 'This rule requires the `strictNullChecks` compiler option to be turned on to function correctly.', | ||
}, | ||
@@ -45,2 +52,5 @@ schema: [ | ||
properties: { | ||
allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: { | ||
type: 'boolean', | ||
}, | ||
ignoreConditionalTests: { | ||
@@ -52,3 +62,12 @@ type: 'boolean', | ||
}, | ||
forceSuggestionFixer: { | ||
ignorePrimitives: { | ||
type: 'object', | ||
properties: { | ||
bigint: { type: 'boolean' }, | ||
boolean: { type: 'boolean' }, | ||
number: { type: 'boolean' }, | ||
string: { type: 'boolean' }, | ||
}, | ||
}, | ||
ignoreTernaryTests: { | ||
type: 'boolean', | ||
@@ -63,11 +82,155 @@ }, | ||
{ | ||
allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: false, | ||
ignoreConditionalTests: true, | ||
ignoreTernaryTests: true, | ||
ignoreMixedLogicalExpressions: true, | ||
ignorePrimitives: { | ||
bigint: false, | ||
boolean: false, | ||
number: false, | ||
string: false, | ||
}, | ||
}, | ||
], | ||
create(context, [{ ignoreConditionalTests, ignoreMixedLogicalExpressions }]) { | ||
create(context, [{ allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing, ignoreConditionalTests, ignoreMixedLogicalExpressions, ignorePrimitives, ignoreTernaryTests, },]) { | ||
const parserServices = util.getParserServices(context); | ||
const compilerOptions = parserServices.program.getCompilerOptions(); | ||
const sourceCode = context.getSourceCode(); | ||
const checker = parserServices.program.getTypeChecker(); | ||
const isStrictNullChecks = tsutils.isStrictCompilerOptionEnabled(compilerOptions, 'strictNullChecks'); | ||
if (!isStrictNullChecks && | ||
allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing !== true) { | ||
context.report({ | ||
loc: { | ||
start: { line: 0, column: 0 }, | ||
end: { line: 0, column: 0 }, | ||
}, | ||
messageId: 'noStrictNullCheck', | ||
}); | ||
} | ||
return { | ||
ConditionalExpression(node) { | ||
if (ignoreTernaryTests) { | ||
return; | ||
} | ||
let operator; | ||
let nodesInsideTestExpression = []; | ||
if (node.test.type === utils_1.AST_NODE_TYPES.BinaryExpression) { | ||
nodesInsideTestExpression = [node.test.left, node.test.right]; | ||
if (node.test.operator === '==' || | ||
node.test.operator === '!=' || | ||
node.test.operator === '===' || | ||
node.test.operator === '!==') { | ||
operator = node.test.operator; | ||
} | ||
} | ||
else if (node.test.type === utils_1.AST_NODE_TYPES.LogicalExpression && | ||
node.test.left.type === utils_1.AST_NODE_TYPES.BinaryExpression && | ||
node.test.right.type === utils_1.AST_NODE_TYPES.BinaryExpression) { | ||
nodesInsideTestExpression = [ | ||
node.test.left.left, | ||
node.test.left.right, | ||
node.test.right.left, | ||
node.test.right.right, | ||
]; | ||
if (node.test.operator === '||') { | ||
if (node.test.left.operator === '===' && | ||
node.test.right.operator === '===') { | ||
operator = '==='; | ||
} | ||
else if (((node.test.left.operator === '===' || | ||
node.test.right.operator === '===') && | ||
(node.test.left.operator === '==' || | ||
node.test.right.operator === '==')) || | ||
(node.test.left.operator === '==' && | ||
node.test.right.operator === '==')) { | ||
operator = '=='; | ||
} | ||
} | ||
else if (node.test.operator === '&&') { | ||
if (node.test.left.operator === '!==' && | ||
node.test.right.operator === '!==') { | ||
operator = '!=='; | ||
} | ||
else if (((node.test.left.operator === '!==' || | ||
node.test.right.operator === '!==') && | ||
(node.test.left.operator === '!=' || | ||
node.test.right.operator === '!=')) || | ||
(node.test.left.operator === '!=' && | ||
node.test.right.operator === '!=')) { | ||
operator = '!='; | ||
} | ||
} | ||
} | ||
if (!operator) { | ||
return; | ||
} | ||
let identifier; | ||
let hasUndefinedCheck = false; | ||
let hasNullCheck = false; | ||
// we check that the test only contains null, undefined and the identifier | ||
for (const testNode of nodesInsideTestExpression) { | ||
if (util.isNullLiteral(testNode)) { | ||
hasNullCheck = true; | ||
} | ||
else if (util.isUndefinedIdentifier(testNode)) { | ||
hasUndefinedCheck = true; | ||
} | ||
else if ((operator === '!==' || operator === '!=') && | ||
util.isNodeEqual(testNode, node.consequent)) { | ||
identifier = testNode; | ||
} | ||
else if ((operator === '===' || operator === '==') && | ||
util.isNodeEqual(testNode, node.alternate)) { | ||
identifier = testNode; | ||
} | ||
else { | ||
return; | ||
} | ||
} | ||
if (!identifier) { | ||
return; | ||
} | ||
const isFixable = (() => { | ||
// it is fixable if we check for both null and undefined, or not if neither | ||
if (hasUndefinedCheck === hasNullCheck) { | ||
return hasUndefinedCheck; | ||
} | ||
// it is fixable if we loosely check for either null or undefined | ||
if (operator === '==' || operator === '!=') { | ||
return true; | ||
} | ||
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(identifier); | ||
const type = checker.getTypeAtLocation(tsNode); | ||
const flags = util.getTypeFlags(type); | ||
if (flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) { | ||
return false; | ||
} | ||
const hasNullType = (flags & ts.TypeFlags.Null) !== 0; | ||
// it is fixable if we check for undefined and the type is not nullable | ||
if (hasUndefinedCheck && !hasNullType) { | ||
return true; | ||
} | ||
const hasUndefinedType = (flags & ts.TypeFlags.Undefined) !== 0; | ||
// it is fixable if we check for null and the type can't be undefined | ||
return hasNullCheck && !hasUndefinedType; | ||
})(); | ||
if (isFixable) { | ||
context.report({ | ||
node, | ||
messageId: 'preferNullishOverTernary', | ||
suggest: [ | ||
{ | ||
messageId: 'suggestNullish', | ||
fix(fixer) { | ||
const [left, right] = operator === '===' || operator === '==' | ||
? [node.alternate, node.consequent] | ||
: [node.consequent, node.alternate]; | ||
return fixer.replaceText(node, `${sourceCode.text.slice(left.range[0], left.range[1])} ?? ${sourceCode.text.slice(right.range[0], right.range[1])}`); | ||
}, | ||
}, | ||
], | ||
}); | ||
} | ||
}, | ||
'LogicalExpression[operator = "||"]'(node) { | ||
@@ -87,3 +250,14 @@ const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node); | ||
} | ||
const barBarOperator = util.nullThrows(sourceCode.getTokenAfter(node.left, token => token.type === experimental_utils_1.AST_TOKEN_TYPES.Punctuator && | ||
const ignorableFlags = [ | ||
ignorePrimitives.bigint && ts.TypeFlags.BigInt, | ||
ignorePrimitives.boolean && ts.TypeFlags.BooleanLiteral, | ||
ignorePrimitives.number && ts.TypeFlags.Number, | ||
ignorePrimitives.string && ts.TypeFlags.String, | ||
] | ||
.filter((flag) => flag !== undefined) | ||
.reduce((previous, flag) => previous | flag, 0); | ||
if (type.types.some(t => tsutils.isTypeFlagSet(t, ignorableFlags))) { | ||
return; | ||
} | ||
const barBarOperator = util.nullThrows(sourceCode.getTokenAfter(node.left, token => token.type === utils_1.AST_TOKEN_TYPES.Punctuator && | ||
token.value === node.operator), util.NullThrowsReasons.MissingToken('operator', node.type)); | ||
@@ -93,3 +267,3 @@ function* fix(fixer) { | ||
// '&&' and '??' operations cannot be mixed without parentheses (e.g. a && b ?? c) | ||
if (node.left.type === experimental_utils_1.AST_NODE_TYPES.LogicalExpression && | ||
if (node.left.type === utils_1.AST_NODE_TYPES.LogicalExpression && | ||
!util.isLogicalOrOperator(node.left.left)) { | ||
@@ -107,3 +281,3 @@ yield fixer.insertTextBefore(node.left.right, '('); | ||
node: barBarOperator, | ||
messageId: 'preferNullish', | ||
messageId: 'preferNullishOverOr', | ||
suggest: [ | ||
@@ -125,7 +299,7 @@ { | ||
parents.add(current); | ||
if ((current.type === experimental_utils_1.AST_NODE_TYPES.ConditionalExpression || | ||
current.type === experimental_utils_1.AST_NODE_TYPES.DoWhileStatement || | ||
current.type === experimental_utils_1.AST_NODE_TYPES.IfStatement || | ||
current.type === experimental_utils_1.AST_NODE_TYPES.ForStatement || | ||
current.type === experimental_utils_1.AST_NODE_TYPES.WhileStatement) && | ||
if ((current.type === utils_1.AST_NODE_TYPES.ConditionalExpression || | ||
current.type === utils_1.AST_NODE_TYPES.DoWhileStatement || | ||
current.type === utils_1.AST_NODE_TYPES.IfStatement || | ||
current.type === utils_1.AST_NODE_TYPES.ForStatement || | ||
current.type === utils_1.AST_NODE_TYPES.WhileStatement) && | ||
parents.has(current.test)) { | ||
@@ -135,4 +309,4 @@ return true; | ||
if ([ | ||
experimental_utils_1.AST_NODE_TYPES.ArrowFunctionExpression, | ||
experimental_utils_1.AST_NODE_TYPES.FunctionExpression, | ||
utils_1.AST_NODE_TYPES.ArrowFunctionExpression, | ||
utils_1.AST_NODE_TYPES.FunctionExpression, | ||
].includes(current.type)) { | ||
@@ -158,3 +332,3 @@ /** | ||
seen.add(current); | ||
if (current && current.type === experimental_utils_1.AST_NODE_TYPES.LogicalExpression) { | ||
if (current && current.type === utils_1.AST_NODE_TYPES.LogicalExpression) { | ||
if (current.operator === '&&') { | ||
@@ -161,0 +335,0 @@ return true; |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,7 +26,8 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const tsutils_1 = require("tsutils"); | ||
const ts = __importStar(require("typescript")); | ||
const util = __importStar(require("../util")); | ||
/* | ||
The AST is always constructed such the first element is always the deepest element. | ||
I.e. for this code: `foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz` | ||
@@ -46,7 +51,6 @@ The AST will look like this: | ||
docs: { | ||
description: 'Prefer using concise optional chain expressions instead of chained logical ands', | ||
category: 'Best Practices', | ||
recommended: false, | ||
suggestion: true, | ||
description: 'Enforce using concise optional chain expressions instead of chained logical ands, negated logical ors, or empty objects', | ||
recommended: 'strict', | ||
}, | ||
hasSuggestions: true, | ||
messages: { | ||
@@ -61,7 +65,110 @@ preferOptionalChain: "Prefer using an optional chain expression instead, as it's more concise and easier to read.", | ||
const sourceCode = context.getSourceCode(); | ||
const parserServices = util.getParserServices(context, true); | ||
return { | ||
'LogicalExpression[operator="||"], LogicalExpression[operator="??"]'(node) { | ||
const leftNode = node.left; | ||
const rightNode = node.right; | ||
const parentNode = node.parent; | ||
const isRightNodeAnEmptyObjectLiteral = rightNode.type === utils_1.AST_NODE_TYPES.ObjectExpression && | ||
rightNode.properties.length === 0; | ||
if (!isRightNodeAnEmptyObjectLiteral || | ||
!parentNode || | ||
parentNode.type !== utils_1.AST_NODE_TYPES.MemberExpression || | ||
parentNode.optional) { | ||
return; | ||
} | ||
function isLeftSideLowerPrecedence() { | ||
const logicalTsNode = parserServices.esTreeNodeToTSNodeMap.get(node); | ||
const leftTsNode = parserServices.esTreeNodeToTSNodeMap.get(leftNode); | ||
const operator = (0, tsutils_1.isBinaryExpression)(logicalTsNode) | ||
? logicalTsNode.operatorToken.kind | ||
: ts.SyntaxKind.Unknown; | ||
const leftPrecedence = util.getOperatorPrecedence(leftTsNode.kind, operator); | ||
return leftPrecedence < util.OperatorPrecedence.LeftHandSide; | ||
} | ||
context.report({ | ||
node: parentNode, | ||
messageId: 'optionalChainSuggest', | ||
suggest: [ | ||
{ | ||
messageId: 'optionalChainSuggest', | ||
fix: (fixer) => { | ||
const leftNodeText = sourceCode.getText(leftNode); | ||
// Any node that is made of an operator with higher or equal precedence, | ||
const maybeWrappedLeftNode = isLeftSideLowerPrecedence() | ||
? `(${leftNodeText})` | ||
: leftNodeText; | ||
const propertyToBeOptionalText = sourceCode.getText(parentNode.property); | ||
const maybeWrappedProperty = parentNode.computed | ||
? `[${propertyToBeOptionalText}]` | ||
: propertyToBeOptionalText; | ||
return fixer.replaceTextRange(parentNode.range, `${maybeWrappedLeftNode}?.${maybeWrappedProperty}`); | ||
}, | ||
}, | ||
], | ||
}); | ||
}, | ||
[[ | ||
'LogicalExpression[operator="||"] > UnaryExpression[operator="!"] > Identifier', | ||
'LogicalExpression[operator="||"] > UnaryExpression[operator="!"] > MemberExpression', | ||
'LogicalExpression[operator="||"] > UnaryExpression[operator="!"] > ChainExpression > MemberExpression', | ||
'LogicalExpression[operator="||"] > UnaryExpression[operator="!"] > MetaProperty', | ||
].join(',')](initialIdentifierOrNotEqualsExpr) { | ||
// selector guarantees this cast | ||
const initialExpression = (initialIdentifierOrNotEqualsExpr.parent.type === | ||
utils_1.AST_NODE_TYPES.ChainExpression | ||
? initialIdentifierOrNotEqualsExpr.parent.parent | ||
: initialIdentifierOrNotEqualsExpr.parent).parent; | ||
if (initialExpression.left.type !== utils_1.AST_NODE_TYPES.UnaryExpression || | ||
initialExpression.left.argument !== initialIdentifierOrNotEqualsExpr) { | ||
// the node(identifier or member expression) is not the deepest left node | ||
return; | ||
} | ||
// walk up the tree to figure out how many logical expressions we can include | ||
let previous = initialExpression; | ||
let current = initialExpression; | ||
let previousLeftText = getText(initialIdentifierOrNotEqualsExpr); | ||
let optionallyChainedCode = previousLeftText; | ||
let expressionCount = 1; | ||
while (current.type === utils_1.AST_NODE_TYPES.LogicalExpression) { | ||
if (current.right.type !== utils_1.AST_NODE_TYPES.UnaryExpression || | ||
!isValidChainTarget(current.right.argument, | ||
// only allow unary '!' with identifiers for the first chain - !foo || !foo() | ||
expressionCount === 1)) { | ||
break; | ||
} | ||
const { rightText, shouldBreak } = breakIfInvalid({ | ||
rightNode: current.right.argument, | ||
previousLeftText, | ||
}); | ||
if (shouldBreak) { | ||
break; | ||
} | ||
let invalidOptionallyChainedPrivateProperty; | ||
({ | ||
invalidOptionallyChainedPrivateProperty, | ||
expressionCount, | ||
previousLeftText, | ||
optionallyChainedCode, | ||
previous, | ||
current, | ||
} = normalizeRepeatingPatterns(rightText, expressionCount, previousLeftText, optionallyChainedCode, previous, current)); | ||
if (invalidOptionallyChainedPrivateProperty) { | ||
return; | ||
} | ||
} | ||
reportIfMoreThanOne({ | ||
expressionCount, | ||
previous, | ||
optionallyChainedCode, | ||
sourceCode, | ||
context, | ||
shouldHandleChainedAnds: false, | ||
}); | ||
}, | ||
[[ | ||
'LogicalExpression[operator="&&"] > Identifier', | ||
'LogicalExpression[operator="&&"] > MemberExpression', | ||
'LogicalExpression[operator="&&"] > ChainExpression > MemberExpression', | ||
'LogicalExpression[operator="&&"] > MetaProperty', | ||
'LogicalExpression[operator="&&"] > BinaryExpression[operator="!=="]', | ||
@@ -72,3 +179,4 @@ 'LogicalExpression[operator="&&"] > BinaryExpression[operator="!="]', | ||
// selector guarantees this cast | ||
const initialExpression = (((_a = initialIdentifierOrNotEqualsExpr.parent) === null || _a === void 0 ? void 0 : _a.type) === experimental_utils_1.AST_NODE_TYPES.ChainExpression | ||
const initialExpression = (((_a = initialIdentifierOrNotEqualsExpr.parent) === null || _a === void 0 ? void 0 : _a.type) === | ||
utils_1.AST_NODE_TYPES.ChainExpression | ||
? initialIdentifierOrNotEqualsExpr.parent.parent | ||
@@ -89,3 +197,3 @@ : initialIdentifierOrNotEqualsExpr.parent); | ||
let expressionCount = 1; | ||
while (current.type === experimental_utils_1.AST_NODE_TYPES.LogicalExpression) { | ||
while (current.type === utils_1.AST_NODE_TYPES.LogicalExpression) { | ||
if (!isValidChainTarget(current.right, | ||
@@ -96,81 +204,48 @@ // only allow identifiers for the first chain - foo && foo() | ||
} | ||
const leftText = previousLeftText; | ||
const rightText = getText(current.right); | ||
// can't just use startsWith because of cases like foo && fooBar.baz; | ||
const matchRegex = new RegExp(`^${ | ||
// escape regex characters | ||
leftText.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[^a-zA-Z0-9_$]`); | ||
if (!matchRegex.test(rightText) && | ||
// handle redundant cases like foo.bar && foo.bar | ||
leftText !== rightText) { | ||
const { rightText, shouldBreak } = breakIfInvalid({ | ||
rightNode: current.right, | ||
previousLeftText, | ||
}); | ||
if (shouldBreak) { | ||
break; | ||
} | ||
// omit weird doubled up expression that make no sense like foo.bar && foo.bar | ||
if (rightText !== leftText) { | ||
expressionCount += 1; | ||
previousLeftText = rightText; | ||
/* | ||
Diff the left and right text to construct the fix string | ||
There are the following cases: | ||
1) | ||
rightText === 'foo.bar.baz.buzz' | ||
leftText === 'foo.bar.baz' | ||
diff === '.buzz' | ||
2) | ||
rightText === 'foo.bar.baz.buzz()' | ||
leftText === 'foo.bar.baz' | ||
diff === '.buzz()' | ||
3) | ||
rightText === 'foo.bar.baz.buzz()' | ||
leftText === 'foo.bar.baz.buzz' | ||
diff === '()' | ||
4) | ||
rightText === 'foo.bar.baz[buzz]' | ||
leftText === 'foo.bar.baz' | ||
diff === '[buzz]' | ||
5) | ||
rightText === 'foo.bar.baz?.buzz' | ||
leftText === 'foo.bar.baz' | ||
diff === '?.buzz' | ||
*/ | ||
const diff = rightText.replace(leftText, ''); | ||
if (diff.startsWith('?')) { | ||
// item was "pre optional chained" | ||
optionallyChainedCode += diff; | ||
} | ||
else { | ||
const needsDot = diff.startsWith('(') || diff.startsWith('['); | ||
optionallyChainedCode += `?${needsDot ? '.' : ''}${diff}`; | ||
} | ||
let invalidOptionallyChainedPrivateProperty; | ||
({ | ||
invalidOptionallyChainedPrivateProperty, | ||
expressionCount, | ||
previousLeftText, | ||
optionallyChainedCode, | ||
previous, | ||
current, | ||
} = normalizeRepeatingPatterns(rightText, expressionCount, previousLeftText, optionallyChainedCode, previous, current)); | ||
if (invalidOptionallyChainedPrivateProperty) { | ||
return; | ||
} | ||
previous = current; | ||
current = util.nullThrows(current.parent, util.NullThrowsReasons.MissingParent); | ||
} | ||
if (expressionCount > 1) { | ||
if (previous.right.type === experimental_utils_1.AST_NODE_TYPES.BinaryExpression) { | ||
// case like foo && foo.bar !== someValue | ||
optionallyChainedCode += ` ${previous.right.operator} ${sourceCode.getText(previous.right.right)}`; | ||
} | ||
context.report({ | ||
node: previous, | ||
messageId: 'preferOptionalChain', | ||
suggest: [ | ||
{ | ||
messageId: 'optionalChainSuggest', | ||
fix: (fixer) => [ | ||
fixer.replaceText(previous, optionallyChainedCode), | ||
], | ||
}, | ||
], | ||
}); | ||
} | ||
reportIfMoreThanOne({ | ||
expressionCount, | ||
previous, | ||
optionallyChainedCode, | ||
sourceCode, | ||
context, | ||
shouldHandleChainedAnds: true, | ||
}); | ||
}, | ||
}; | ||
function breakIfInvalid({ previousLeftText, rightNode, }) { | ||
let shouldBreak = false; | ||
const rightText = getText(rightNode); | ||
// can't just use startsWith because of cases like foo && fooBar.baz; | ||
const matchRegex = new RegExp(`^${ | ||
// escape regex characters | ||
previousLeftText.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[^a-zA-Z0-9_$]`); | ||
if (!matchRegex.test(rightText) && | ||
// handle redundant cases like foo.bar && foo.bar | ||
previousLeftText !== rightText) { | ||
shouldBreak = true; | ||
} | ||
return { shouldBreak, leftText: previousLeftText, rightText }; | ||
} | ||
function getText(node) { | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.BinaryExpression) { | ||
if (node.type === utils_1.AST_NODE_TYPES.BinaryExpression) { | ||
return getText( | ||
@@ -180,3 +255,3 @@ // isValidChainTarget ensures this is type safe | ||
} | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.CallExpression) { | ||
if (node.type === utils_1.AST_NODE_TYPES.CallExpression) { | ||
const calleeText = getText( | ||
@@ -193,10 +268,14 @@ // isValidChainTarget ensures this is type safe | ||
} | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.Identifier) { | ||
if (node.type === utils_1.AST_NODE_TYPES.Identifier || | ||
node.type === utils_1.AST_NODE_TYPES.PrivateIdentifier) { | ||
return node.name; | ||
} | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.ThisExpression) { | ||
if (node.type === utils_1.AST_NODE_TYPES.MetaProperty) { | ||
return `${node.meta.name}.${node.property.name}`; | ||
} | ||
if (node.type === utils_1.AST_NODE_TYPES.ThisExpression) { | ||
return 'this'; | ||
} | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.ChainExpression) { | ||
/* istanbul ignore if */ if (node.expression.type === experimental_utils_1.AST_NODE_TYPES.TSNonNullExpression) { | ||
if (node.type === utils_1.AST_NODE_TYPES.ChainExpression) { | ||
/* istanbul ignore if */ if (node.expression.type === utils_1.AST_NODE_TYPES.TSNonNullExpression) { | ||
// this shouldn't happen | ||
@@ -207,2 +286,6 @@ return ''; | ||
} | ||
if (node.object.type === utils_1.AST_NODE_TYPES.TSNonNullExpression) { | ||
// Not supported mixing with TSNonNullExpression | ||
return ''; | ||
} | ||
return getMemberExpressionText(node); | ||
@@ -217,10 +300,9 @@ } | ||
switch (node.object.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.CallExpression: | ||
case experimental_utils_1.AST_NODE_TYPES.Identifier: | ||
objectText = getText(node.object); | ||
break; | ||
case experimental_utils_1.AST_NODE_TYPES.MemberExpression: | ||
case utils_1.AST_NODE_TYPES.MemberExpression: | ||
objectText = getMemberExpressionText(node.object); | ||
break; | ||
case experimental_utils_1.AST_NODE_TYPES.ThisExpression: | ||
case utils_1.AST_NODE_TYPES.CallExpression: | ||
case utils_1.AST_NODE_TYPES.Identifier: | ||
case utils_1.AST_NODE_TYPES.MetaProperty: | ||
case utils_1.AST_NODE_TYPES.ThisExpression: | ||
objectText = getText(node.object); | ||
@@ -230,3 +312,3 @@ break; | ||
default: | ||
throw new Error(`Unexpected member object type: ${node.object.type}`); | ||
return ''; | ||
} | ||
@@ -237,10 +319,11 @@ let propertyText; | ||
switch (node.property.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.Identifier: | ||
case utils_1.AST_NODE_TYPES.Identifier: | ||
propertyText = getText(node.property); | ||
break; | ||
case experimental_utils_1.AST_NODE_TYPES.Literal: | ||
case experimental_utils_1.AST_NODE_TYPES.TemplateLiteral: | ||
case utils_1.AST_NODE_TYPES.Literal: | ||
case utils_1.AST_NODE_TYPES.TemplateLiteral: | ||
case utils_1.AST_NODE_TYPES.BinaryExpression: | ||
propertyText = sourceCode.getText(node.property); | ||
break; | ||
case experimental_utils_1.AST_NODE_TYPES.MemberExpression: | ||
case utils_1.AST_NODE_TYPES.MemberExpression: | ||
propertyText = getMemberExpressionText(node.property); | ||
@@ -250,3 +333,3 @@ break; | ||
default: | ||
throw new Error(`Unexpected member property type: ${node.object.type}`); | ||
return ''; | ||
} | ||
@@ -258,8 +341,10 @@ return `${objectText}${node.optional ? '?.' : ''}[${propertyText}]`; | ||
switch (node.property.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.Identifier: | ||
case utils_1.AST_NODE_TYPES.Identifier: | ||
propertyText = getText(node.property); | ||
break; | ||
/* istanbul ignore next */ | ||
case utils_1.AST_NODE_TYPES.PrivateIdentifier: | ||
propertyText = '#' + getText(node.property); | ||
break; | ||
default: | ||
throw new Error(`Unexpected member property type: ${node.object.type}`); | ||
propertyText = sourceCode.getText(node.property); | ||
} | ||
@@ -272,21 +357,114 @@ return `${objectText}${node.optional ? '?.' : '.'}${propertyText}`; | ||
const ALLOWED_MEMBER_OBJECT_TYPES = new Set([ | ||
experimental_utils_1.AST_NODE_TYPES.CallExpression, | ||
experimental_utils_1.AST_NODE_TYPES.Identifier, | ||
experimental_utils_1.AST_NODE_TYPES.MemberExpression, | ||
experimental_utils_1.AST_NODE_TYPES.ThisExpression, | ||
utils_1.AST_NODE_TYPES.CallExpression, | ||
utils_1.AST_NODE_TYPES.Identifier, | ||
utils_1.AST_NODE_TYPES.MemberExpression, | ||
utils_1.AST_NODE_TYPES.ThisExpression, | ||
utils_1.AST_NODE_TYPES.MetaProperty, | ||
]); | ||
const ALLOWED_COMPUTED_PROP_TYPES = new Set([ | ||
experimental_utils_1.AST_NODE_TYPES.Identifier, | ||
experimental_utils_1.AST_NODE_TYPES.Literal, | ||
experimental_utils_1.AST_NODE_TYPES.MemberExpression, | ||
experimental_utils_1.AST_NODE_TYPES.TemplateLiteral, | ||
utils_1.AST_NODE_TYPES.Identifier, | ||
utils_1.AST_NODE_TYPES.Literal, | ||
utils_1.AST_NODE_TYPES.MemberExpression, | ||
utils_1.AST_NODE_TYPES.TemplateLiteral, | ||
]); | ||
const ALLOWED_NON_COMPUTED_PROP_TYPES = new Set([ | ||
experimental_utils_1.AST_NODE_TYPES.Identifier, | ||
utils_1.AST_NODE_TYPES.Identifier, | ||
utils_1.AST_NODE_TYPES.PrivateIdentifier, | ||
]); | ||
function reportIfMoreThanOne({ expressionCount, previous, optionallyChainedCode, sourceCode, context, shouldHandleChainedAnds, }) { | ||
if (expressionCount > 1) { | ||
if (shouldHandleChainedAnds && | ||
previous.right.type === utils_1.AST_NODE_TYPES.BinaryExpression) { | ||
let operator = previous.right.operator; | ||
if (previous.right.operator === '!==' && | ||
// TODO(#4820): Use the type checker to know whether this is `null` | ||
previous.right.right.type === utils_1.AST_NODE_TYPES.Literal && | ||
previous.right.right.raw === 'null') { | ||
// case like foo !== null && foo.bar !== null | ||
operator = '!='; | ||
} | ||
// case like foo && foo.bar !== someValue | ||
optionallyChainedCode += ` ${operator} ${sourceCode.getText(previous.right.right)}`; | ||
} | ||
context.report({ | ||
node: previous, | ||
messageId: 'preferOptionalChain', | ||
suggest: [ | ||
{ | ||
messageId: 'optionalChainSuggest', | ||
fix: (fixer) => [ | ||
fixer.replaceText(previous, `${shouldHandleChainedAnds ? '' : '!'}${optionallyChainedCode}`), | ||
], | ||
}, | ||
], | ||
}); | ||
} | ||
} | ||
function normalizeRepeatingPatterns(rightText, expressionCount, previousLeftText, optionallyChainedCode, previous, current) { | ||
const leftText = previousLeftText; | ||
let invalidOptionallyChainedPrivateProperty = false; | ||
// omit weird doubled up expression that make no sense like foo.bar && foo.bar | ||
if (rightText !== previousLeftText) { | ||
expressionCount += 1; | ||
previousLeftText = rightText; | ||
/* | ||
Diff the left and right text to construct the fix string | ||
There are the following cases: | ||
1) | ||
rightText === 'foo.bar.baz.buzz' | ||
leftText === 'foo.bar.baz' | ||
diff === '.buzz' | ||
2) | ||
rightText === 'foo.bar.baz.buzz()' | ||
leftText === 'foo.bar.baz' | ||
diff === '.buzz()' | ||
3) | ||
rightText === 'foo.bar.baz.buzz()' | ||
leftText === 'foo.bar.baz.buzz' | ||
diff === '()' | ||
4) | ||
rightText === 'foo.bar.baz[buzz]' | ||
leftText === 'foo.bar.baz' | ||
diff === '[buzz]' | ||
5) | ||
rightText === 'foo.bar.baz?.buzz' | ||
leftText === 'foo.bar.baz' | ||
diff === '?.buzz' | ||
*/ | ||
const diff = rightText.replace(leftText, ''); | ||
if (diff.startsWith('.#')) { | ||
// Do not handle direct optional chaining on private properties because of a typescript bug (https://github.com/microsoft/TypeScript/issues/42734) | ||
// We still allow in computed properties | ||
invalidOptionallyChainedPrivateProperty = true; | ||
} | ||
if (diff.startsWith('?')) { | ||
// item was "pre optional chained" | ||
optionallyChainedCode += diff; | ||
} | ||
else { | ||
const needsDot = diff.startsWith('(') || diff.startsWith('['); | ||
optionallyChainedCode += `?${needsDot ? '.' : ''}${diff}`; | ||
} | ||
} | ||
previous = current; | ||
current = util.nullThrows(current.parent, util.NullThrowsReasons.MissingParent); | ||
return { | ||
invalidOptionallyChainedPrivateProperty, | ||
expressionCount, | ||
previousLeftText, | ||
optionallyChainedCode, | ||
previous, | ||
current, | ||
}; | ||
} | ||
function isValidChainTarget(node, allowIdentifier) { | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.ChainExpression) { | ||
if (node.type === utils_1.AST_NODE_TYPES.ChainExpression) { | ||
return isValidChainTarget(node.expression, allowIdentifier); | ||
} | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.MemberExpression) { | ||
if (node.type === utils_1.AST_NODE_TYPES.MemberExpression) { | ||
const isObjectValid = ALLOWED_MEMBER_OBJECT_TYPES.has(node.object.type) && | ||
@@ -298,3 +476,3 @@ // make sure to validate the expression is of our expected structure | ||
// make sure to validate the member expression is of our expected structure | ||
(node.property.type === experimental_utils_1.AST_NODE_TYPES.MemberExpression | ||
(node.property.type === utils_1.AST_NODE_TYPES.MemberExpression | ||
? isValidChainTarget(node.property, allowIdentifier) | ||
@@ -305,8 +483,9 @@ : true) | ||
} | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.CallExpression) { | ||
if (node.type === utils_1.AST_NODE_TYPES.CallExpression) { | ||
return isValidChainTarget(node.callee, allowIdentifier); | ||
} | ||
if (allowIdentifier && | ||
(node.type === experimental_utils_1.AST_NODE_TYPES.Identifier || | ||
node.type === experimental_utils_1.AST_NODE_TYPES.ThisExpression)) { | ||
(node.type === utils_1.AST_NODE_TYPES.Identifier || | ||
node.type === utils_1.AST_NODE_TYPES.ThisExpression || | ||
node.type === utils_1.AST_NODE_TYPES.MetaProperty)) { | ||
return true; | ||
@@ -321,16 +500,7 @@ } | ||
*/ | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.BinaryExpression && | ||
return (node.type === utils_1.AST_NODE_TYPES.BinaryExpression && | ||
['!==', '!='].includes(node.operator) && | ||
isValidChainTarget(node.left, allowIdentifier)) { | ||
if (node.right.type === experimental_utils_1.AST_NODE_TYPES.Identifier && | ||
node.right.name === 'undefined') { | ||
return true; | ||
} | ||
if (node.right.type === experimental_utils_1.AST_NODE_TYPES.Literal && | ||
node.right.value === null) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
isValidChainTarget(node.left, allowIdentifier) && | ||
(util.isUndefinedIdentifier(node.right) || util.isNullLiteral(node.right))); | ||
} | ||
//# sourceMappingURL=prefer-optional-chain.js.map |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -30,4 +34,3 @@ exports.default = util.createRule({ | ||
docs: { | ||
description: 'Requires that function parameters are typed as readonly to prevent accidental mutation of inputs', | ||
category: 'Possible Errors', | ||
description: 'Require function parameters to be typed as `readonly` to prevent accidental mutation of inputs', | ||
recommended: false, | ||
@@ -40,10 +43,7 @@ requiresTypeChecking: true, | ||
additionalProperties: false, | ||
properties: { | ||
checkParameterProperties: { | ||
properties: Object.assign({ checkParameterProperties: { | ||
type: 'boolean', | ||
}, | ||
ignoreInferredTypes: { | ||
}, ignoreInferredTypes: { | ||
type: 'boolean', | ||
}, | ||
}, | ||
} }, util.readonlynessOptionsSchema.properties), | ||
}, | ||
@@ -56,9 +56,5 @@ ], | ||
defaultOptions: [ | ||
{ | ||
checkParameterProperties: true, | ||
ignoreInferredTypes: false, | ||
}, | ||
Object.assign({ checkParameterProperties: true, ignoreInferredTypes: false }, util.readonlynessOptionsDefaults), | ||
], | ||
create(context, options) { | ||
const [{ checkParameterProperties, ignoreInferredTypes }] = options; | ||
create(context, [{ checkParameterProperties, ignoreInferredTypes, treatMethodsAsReadonly }]) { | ||
const { esTreeNodeToTSNodeMap, program } = util.getParserServices(context); | ||
@@ -68,18 +64,18 @@ const checker = program.getTypeChecker(); | ||
[[ | ||
experimental_utils_1.AST_NODE_TYPES.ArrowFunctionExpression, | ||
experimental_utils_1.AST_NODE_TYPES.FunctionDeclaration, | ||
experimental_utils_1.AST_NODE_TYPES.FunctionExpression, | ||
experimental_utils_1.AST_NODE_TYPES.TSCallSignatureDeclaration, | ||
experimental_utils_1.AST_NODE_TYPES.TSConstructSignatureDeclaration, | ||
experimental_utils_1.AST_NODE_TYPES.TSDeclareFunction, | ||
experimental_utils_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression, | ||
experimental_utils_1.AST_NODE_TYPES.TSFunctionType, | ||
experimental_utils_1.AST_NODE_TYPES.TSMethodSignature, | ||
utils_1.AST_NODE_TYPES.ArrowFunctionExpression, | ||
utils_1.AST_NODE_TYPES.FunctionDeclaration, | ||
utils_1.AST_NODE_TYPES.FunctionExpression, | ||
utils_1.AST_NODE_TYPES.TSCallSignatureDeclaration, | ||
utils_1.AST_NODE_TYPES.TSConstructSignatureDeclaration, | ||
utils_1.AST_NODE_TYPES.TSDeclareFunction, | ||
utils_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression, | ||
utils_1.AST_NODE_TYPES.TSFunctionType, | ||
utils_1.AST_NODE_TYPES.TSMethodSignature, | ||
].join(', ')](node) { | ||
for (const param of node.params) { | ||
if (!checkParameterProperties && | ||
param.type === experimental_utils_1.AST_NODE_TYPES.TSParameterProperty) { | ||
param.type === utils_1.AST_NODE_TYPES.TSParameterProperty) { | ||
continue; | ||
} | ||
const actualParam = param.type === experimental_utils_1.AST_NODE_TYPES.TSParameterProperty | ||
const actualParam = param.type === utils_1.AST_NODE_TYPES.TSParameterProperty | ||
? param.parameter | ||
@@ -92,3 +88,5 @@ : param; | ||
const type = checker.getTypeAtLocation(tsNode); | ||
const isReadOnly = util.isTypeReadonly(checker, type); | ||
const isReadOnly = util.isTypeReadonly(checker, type, { | ||
treatMethodsAsReadonly: treatMethodsAsReadonly, | ||
}); | ||
if (!isReadOnly) { | ||
@@ -95,0 +93,0 @@ context.report({ |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,2 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const tsutils = __importStar(require("tsutils")); | ||
@@ -27,8 +32,7 @@ const ts = __importStar(require("typescript")); | ||
const util_1 = require("../util"); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const functionScopeBoundaries = [ | ||
experimental_utils_1.AST_NODE_TYPES.ArrowFunctionExpression, | ||
experimental_utils_1.AST_NODE_TYPES.FunctionDeclaration, | ||
experimental_utils_1.AST_NODE_TYPES.FunctionExpression, | ||
experimental_utils_1.AST_NODE_TYPES.MethodDefinition, | ||
utils_1.AST_NODE_TYPES.ArrowFunctionExpression, | ||
utils_1.AST_NODE_TYPES.FunctionDeclaration, | ||
utils_1.AST_NODE_TYPES.FunctionExpression, | ||
utils_1.AST_NODE_TYPES.MethodDefinition, | ||
].join(', '); | ||
@@ -39,4 +43,3 @@ exports.default = util.createRule({ | ||
docs: { | ||
description: "Requires that private members are marked as `readonly` if they're never modified outside of the constructor", | ||
category: 'Best Practices', | ||
description: "Require private members to be marked as `readonly` if they're never modified outside of the constructor", | ||
recommended: false, | ||
@@ -104,3 +107,4 @@ requiresTypeChecking: true, | ||
} | ||
else if (ts.isBinaryExpression(parent)) { | ||
else if (ts.isBinaryExpression(parent) && | ||
!ts.isPropertyAccessExpression(current)) { | ||
return (parent.left === current && | ||
@@ -115,6 +119,2 @@ parent.operatorToken.kind === ts.SyntaxKind.EqualsToken); | ||
} | ||
function isConstructor(node) { | ||
return (node.type === experimental_utils_1.AST_NODE_TYPES.MethodDefinition && | ||
node.kind === 'constructor'); | ||
} | ||
function isFunctionScopeBoundaryInStack(node) { | ||
@@ -168,3 +168,3 @@ if (classScopeStack.length === 0) { | ||
[functionScopeBoundaries](node) { | ||
if (isConstructor(node)) { | ||
if (utils_1.ASTUtils.isConstructor(node)) { | ||
classScopeStack[classScopeStack.length - 1].enterConstructor(parserServices.esTreeNodeToTSNodeMap.get(node)); | ||
@@ -177,3 +177,3 @@ } | ||
[`${functionScopeBoundaries}:exit`](node) { | ||
if (isConstructor(node)) { | ||
if (utils_1.ASTUtils.isConstructor(node)) { | ||
classScopeStack[classScopeStack.length - 1].exitConstructor(); | ||
@@ -199,4 +199,9 @@ } | ||
this.constructorScopeDepth = OUTSIDE_CONSTRUCTOR; | ||
this.checker = checker; | ||
this.classType = checker.getTypeAtLocation(classNode); | ||
const classType = checker.getTypeAtLocation(classNode); | ||
if (tsutils.isIntersectionType(classType)) { | ||
this.classType = classType.types[0]; | ||
} | ||
else { | ||
this.classType = classType; | ||
} | ||
for (const member of classNode.members) { | ||
@@ -226,3 +231,3 @@ if (ts.isPropertyDeclaration(member)) { | ||
if (!modifierType.getSymbol() || | ||
!util_1.typeIsOrHasBaseType(modifierType, this.classType)) { | ||
!(0, util_1.typeIsOrHasBaseType)(modifierType, this.classType)) { | ||
return; | ||
@@ -229,0 +234,0 @@ } |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -29,3 +33,3 @@ const getMemberExpressionName = (member) => { | ||
} | ||
if (member.property.type === experimental_utils_1.AST_NODE_TYPES.Literal && | ||
if (member.property.type === utils_1.AST_NODE_TYPES.Literal && | ||
typeof member.property.value === 'string') { | ||
@@ -41,5 +45,4 @@ return member.property.value; | ||
docs: { | ||
category: 'Best Practices', | ||
recommended: false, | ||
description: 'Prefer using type parameter when calling `Array#reduce` instead of casting', | ||
description: 'Enforce using type parameter when calling `Array#reduce` instead of casting', | ||
recommended: 'strict', | ||
requiresTypeChecking: true, | ||
@@ -75,15 +78,20 @@ }, | ||
node: secondArg, | ||
fix: fixer => [ | ||
fixer.removeRange([ | ||
secondArg.range[0], | ||
secondArg.expression.range[0], | ||
]), | ||
fixer.removeRange([ | ||
secondArg.expression.range[1], | ||
secondArg.range[1], | ||
]), | ||
fixer.insertTextAfter(callee, `<${context | ||
.getSourceCode() | ||
.getText(secondArg.typeAnnotation)}>`), | ||
], | ||
fix: fixer => { | ||
const fixes = [ | ||
fixer.removeRange([ | ||
secondArg.range[0], | ||
secondArg.expression.range[0], | ||
]), | ||
fixer.removeRange([ | ||
secondArg.expression.range[1], | ||
secondArg.range[1], | ||
]), | ||
]; | ||
if (!callee.parent.typeParameters) { | ||
fixes.push(fixer.insertTextAfter(callee, `<${context | ||
.getSourceCode() | ||
.getText(secondArg.typeAnnotation)}>`)); | ||
} | ||
return fixes; | ||
}, | ||
}); | ||
@@ -90,0 +98,0 @@ return; |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
}) : function(o, v) { | ||
o["default"] = v; | ||
}); | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const tsutils = __importStar(require("tsutils")); | ||
const util_1 = require("../util"); | ||
exports.default = util_1.createRule({ | ||
var ArgumentType; | ||
(function (ArgumentType) { | ||
ArgumentType[ArgumentType["Other"] = 0] = "Other"; | ||
ArgumentType[ArgumentType["String"] = 1] = "String"; | ||
ArgumentType[ArgumentType["RegExp"] = 2] = "RegExp"; | ||
ArgumentType[ArgumentType["Both"] = 3] = "Both"; | ||
})(ArgumentType || (ArgumentType = {})); | ||
exports.default = (0, util_1.createRule)({ | ||
name: 'prefer-regexp-exec', | ||
@@ -9,6 +41,6 @@ defaultOptions: [], | ||
type: 'suggestion', | ||
fixable: 'code', | ||
docs: { | ||
description: 'Enforce that `RegExp#exec` is used instead of `String#match` if no global flag is provided', | ||
category: 'Best Practices', | ||
recommended: 'error', | ||
description: 'Enforce `RegExp#exec` over `String#match` if no global flag is provided', | ||
recommended: false, | ||
requiresTypeChecking: true, | ||
@@ -23,30 +55,104 @@ }, | ||
const globalScope = context.getScope(); | ||
const service = util_1.getParserServices(context); | ||
const typeChecker = service.program.getTypeChecker(); | ||
const parserServices = (0, util_1.getParserServices)(context); | ||
const typeChecker = parserServices.program.getTypeChecker(); | ||
const sourceCode = context.getSourceCode(); | ||
/** | ||
* Check if a given node is a string. | ||
* @param node The node to check. | ||
* Check if a given node type is a string. | ||
* @param node The node type to check. | ||
*/ | ||
function isStringType(node) { | ||
const objectType = typeChecker.getTypeAtLocation(service.esTreeNodeToTSNodeMap.get(node)); | ||
return util_1.getTypeName(typeChecker, objectType) === 'string'; | ||
function isStringType(type) { | ||
return (0, util_1.getTypeName)(typeChecker, type) === 'string'; | ||
} | ||
/** | ||
* Check if a given node type is a RegExp. | ||
* @param node The node type to check. | ||
*/ | ||
function isRegExpType(type) { | ||
return (0, util_1.getTypeName)(typeChecker, type) === 'RegExp'; | ||
} | ||
function collectArgumentTypes(types) { | ||
let result = ArgumentType.Other; | ||
for (const type of types) { | ||
if (isRegExpType(type)) { | ||
result |= ArgumentType.RegExp; | ||
} | ||
else if (isStringType(type)) { | ||
result |= ArgumentType.String; | ||
} | ||
} | ||
return result; | ||
} | ||
function isLikelyToContainGlobalFlag(node) { | ||
if (node.type === utils_1.AST_NODE_TYPES.CallExpression || | ||
node.type === utils_1.AST_NODE_TYPES.NewExpression) { | ||
const [, flags] = node.arguments; | ||
return (flags && | ||
flags.type === utils_1.AST_NODE_TYPES.Literal && | ||
typeof flags.value === 'string' && | ||
flags.value.includes('g')); | ||
} | ||
return node.type === utils_1.AST_NODE_TYPES.Identifier; | ||
} | ||
return { | ||
"CallExpression[arguments.length=1] > MemberExpression.callee[property.name='match'][computed=false]"(node) { | ||
const callNode = node.parent; | ||
const arg = callNode.arguments[0]; | ||
const evaluated = util_1.getStaticValue(arg, globalScope); | ||
"CallExpression[arguments.length=1] > MemberExpression.callee[property.name='match'][computed=false]"(memberNode) { | ||
const objectNode = memberNode.object; | ||
const callNode = memberNode.parent; | ||
const [argumentNode] = callNode.arguments; | ||
const argumentValue = (0, util_1.getStaticValue)(argumentNode, globalScope); | ||
if (!isStringType(typeChecker.getTypeAtLocation(parserServices.esTreeNodeToTSNodeMap.get(objectNode)))) { | ||
return; | ||
} | ||
// Don't report regular expressions with global flag. | ||
if (evaluated && | ||
evaluated.value instanceof RegExp && | ||
evaluated.value.flags.includes('g')) { | ||
if ((!argumentValue && isLikelyToContainGlobalFlag(argumentNode)) || | ||
(argumentValue && | ||
argumentValue.value instanceof RegExp && | ||
argumentValue.value.flags.includes('g'))) { | ||
return; | ||
} | ||
if (isStringType(node.object)) { | ||
context.report({ | ||
node: callNode, | ||
if (argumentNode.type === utils_1.AST_NODE_TYPES.Literal && | ||
typeof argumentNode.value === 'string') { | ||
let regExp; | ||
try { | ||
regExp = RegExp(argumentNode.value); | ||
} | ||
catch (_a) { | ||
return; | ||
} | ||
return context.report({ | ||
node: memberNode.property, | ||
messageId: 'regExpExecOverStringMatch', | ||
fix: (0, util_1.getWrappingFixer)({ | ||
sourceCode, | ||
node: callNode, | ||
innerNode: [objectNode], | ||
wrap: objectCode => `${regExp.toString()}.exec(${objectCode})`, | ||
}), | ||
}); | ||
return; | ||
} | ||
const argumentType = typeChecker.getTypeAtLocation(parserServices.esTreeNodeToTSNodeMap.get(argumentNode)); | ||
const argumentTypes = collectArgumentTypes(tsutils.unionTypeParts(argumentType)); | ||
switch (argumentTypes) { | ||
case ArgumentType.RegExp: | ||
return context.report({ | ||
node: memberNode.property, | ||
messageId: 'regExpExecOverStringMatch', | ||
fix: (0, util_1.getWrappingFixer)({ | ||
sourceCode, | ||
node: callNode, | ||
innerNode: [objectNode, argumentNode], | ||
wrap: (objectCode, argumentCode) => `${argumentCode}.exec(${objectCode})`, | ||
}), | ||
}); | ||
case ArgumentType.String: | ||
return context.report({ | ||
node: memberNode.property, | ||
messageId: 'regExpExecOverStringMatch', | ||
fix: (0, util_1.getWrappingFixer)({ | ||
sourceCode, | ||
node: callNode, | ||
innerNode: [objectNode, argumentNode], | ||
wrap: (objectCode, argumentCode) => `RegExp(${argumentCode}).exec(${objectCode})`, | ||
}), | ||
}); | ||
} | ||
}, | ||
@@ -53,0 +159,0 @@ }; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const regexpp_1 = require("regexpp"); | ||
const regexpp_1 = require("@eslint-community/regexpp"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util_1 = require("../util"); | ||
const EQ_OPERATORS = /^[=!]=/; | ||
const regexpp = new regexpp_1.RegExpParser(); | ||
exports.default = util_1.createRule({ | ||
exports.default = (0, util_1.createRule)({ | ||
name: 'prefer-string-starts-ends-with', | ||
@@ -14,5 +14,4 @@ defaultOptions: [], | ||
docs: { | ||
description: 'Enforce the use of `String#startsWith` and `String#endsWith` instead of other equivalent methods of checking substrings', | ||
category: 'Best Practices', | ||
recommended: false, | ||
description: 'Enforce using `String#startsWith` and `String#endsWith` over other equivalent methods of checking substrings', | ||
recommended: 'strict', | ||
requiresTypeChecking: true, | ||
@@ -30,3 +29,3 @@ }, | ||
const sourceCode = context.getSourceCode(); | ||
const service = util_1.getParserServices(context); | ||
const service = (0, util_1.getParserServices)(context); | ||
const typeChecker = service.program.getTypeChecker(); | ||
@@ -39,3 +38,3 @@ /** | ||
const objectType = typeChecker.getTypeAtLocation(service.esTreeNodeToTSNodeMap.get(node)); | ||
return util_1.getTypeName(typeChecker, objectType) === 'string'; | ||
return (0, util_1.getTypeName)(typeChecker, objectType) === 'string'; | ||
} | ||
@@ -47,4 +46,4 @@ /** | ||
function isNull(node) { | ||
const evaluated = util_1.getStaticValue(node, globalScope); | ||
return evaluated != null && evaluated.value === null; | ||
const evaluated = (0, util_1.getStaticValue)(node, globalScope); | ||
return evaluated != null && evaluated.value == null; | ||
} | ||
@@ -57,3 +56,3 @@ /** | ||
function isNumber(node, value) { | ||
const evaluated = util_1.getStaticValue(node, globalScope); | ||
const evaluated = (0, util_1.getStaticValue)(node, globalScope); | ||
return evaluated != null && evaluated.value === value; | ||
@@ -67,3 +66,3 @@ } | ||
function isCharacter(node) { | ||
const evaluated = util_1.getStaticValue(node, globalScope); | ||
const evaluated = (0, util_1.getStaticValue)(node, globalScope); | ||
return (evaluated != null && | ||
@@ -79,3 +78,3 @@ typeof evaluated.value === 'string' && | ||
function isEqualityComparison(node) { | ||
return (node.type === experimental_utils_1.AST_NODE_TYPES.BinaryExpression && | ||
return (node.type === utils_1.AST_NODE_TYPES.BinaryExpression && | ||
EQ_OPERATORS.test(node.operator)); | ||
@@ -115,8 +114,8 @@ } | ||
function isLengthExpression(node, expectedObjectNode) { | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.MemberExpression) { | ||
return (util_1.getPropertyName(node, globalScope) === 'length' && | ||
if (node.type === utils_1.AST_NODE_TYPES.MemberExpression) { | ||
return ((0, util_1.getPropertyName)(node, globalScope) === 'length' && | ||
isSameTokens(node.object, expectedObjectNode)); | ||
} | ||
const evaluatedLength = util_1.getStaticValue(node, globalScope); | ||
const evaluatedString = util_1.getStaticValue(expectedObjectNode, globalScope); | ||
const evaluatedLength = (0, util_1.getStaticValue)(node, globalScope); | ||
const evaluatedString = (0, util_1.getStaticValue)(expectedObjectNode, globalScope); | ||
return (evaluatedLength != null && | ||
@@ -137,5 +136,5 @@ evaluatedString != null && | ||
function isNegativeIndexExpression(node, expectedIndexedNode) { | ||
return ((node.type === experimental_utils_1.AST_NODE_TYPES.UnaryExpression && | ||
return ((node.type === utils_1.AST_NODE_TYPES.UnaryExpression && | ||
node.operator === '-') || | ||
(node.type === experimental_utils_1.AST_NODE_TYPES.BinaryExpression && | ||
(node.type === utils_1.AST_NODE_TYPES.BinaryExpression && | ||
node.operator === '-' && | ||
@@ -153,3 +152,3 @@ isLengthExpression(node.left, expectedIndexedNode))); | ||
function isLastIndexExpression(node, expectedObjectNode) { | ||
return (node.type === experimental_utils_1.AST_NODE_TYPES.BinaryExpression && | ||
return (node.type === utils_1.AST_NODE_TYPES.BinaryExpression && | ||
node.operator === '-' && | ||
@@ -204,3 +203,3 @@ isLengthExpression(node.left, expectedObjectNode) && | ||
function parseRegExp(node) { | ||
const evaluated = util_1.getStaticValue(node, globalScope); | ||
const evaluated = (0, util_1.getStaticValue)(node, globalScope); | ||
if (evaluated == null || !(evaluated.value instanceof RegExp)) { | ||
@@ -224,7 +223,7 @@ return null; | ||
function getLeftNode(node) { | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.ChainExpression) { | ||
if (node.type === utils_1.AST_NODE_TYPES.ChainExpression) { | ||
return getLeftNode(node.expression); | ||
} | ||
let leftNode; | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.CallExpression) { | ||
if (node.type === utils_1.AST_NODE_TYPES.CallExpression) { | ||
leftNode = node.callee; | ||
@@ -235,3 +234,3 @@ } | ||
} | ||
if (leftNode.type !== experimental_utils_1.AST_NODE_TYPES.MemberExpression) { | ||
if (leftNode.type !== utils_1.AST_NODE_TYPES.MemberExpression) { | ||
throw new Error(`Expected a MemberExpression, got ${leftNode.type}`); | ||
@@ -276,3 +275,3 @@ } | ||
var _a; | ||
return util_1.nullThrows(((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === experimental_utils_1.AST_NODE_TYPES.ChainExpression | ||
return (0, util_1.nullThrows)(((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.ChainExpression | ||
? node.parent.parent | ||
@@ -294,3 +293,3 @@ : node.parent, util_1.NullThrowsReasons.MissingParent); | ||
let indexNode = null; | ||
if ((parentNode === null || parentNode === void 0 ? void 0 : parentNode.type) === experimental_utils_1.AST_NODE_TYPES.CallExpression) { | ||
if ((parentNode === null || parentNode === void 0 ? void 0 : parentNode.type) === utils_1.AST_NODE_TYPES.CallExpression) { | ||
if (parentNode.arguments.length === 1) { | ||
@@ -358,3 +357,3 @@ indexNode = parentNode.arguments[0]; | ||
!isEqualityComparison(parentNode) || | ||
parentNode.right.type !== experimental_utils_1.AST_NODE_TYPES.BinaryExpression || | ||
parentNode.right.type !== utils_1.AST_NODE_TYPES.BinaryExpression || | ||
parentNode.right.operator !== '-' || | ||
@@ -443,3 +442,3 @@ !isLengthExpression(parentNode.right.left, node.object) || | ||
if (eqNode.operator.length === 2 && | ||
(eqNode.right.type !== experimental_utils_1.AST_NODE_TYPES.Literal || | ||
(eqNode.right.type !== utils_1.AST_NODE_TYPES.Literal || | ||
typeof eqNode.right.value !== 'string')) { | ||
@@ -458,3 +457,3 @@ return null; | ||
const posNode = callNode.arguments[0]; | ||
const posNodeIsAbsolutelyValid = (posNode.type === experimental_utils_1.AST_NODE_TYPES.BinaryExpression && | ||
const posNodeIsAbsolutelyValid = (posNode.type === utils_1.AST_NODE_TYPES.BinaryExpression && | ||
posNode.operator === '-' && | ||
@@ -464,3 +463,3 @@ isLengthExpression(posNode.left, node.object) && | ||
(negativeIndexSupported && | ||
posNode.type === experimental_utils_1.AST_NODE_TYPES.UnaryExpression && | ||
posNode.type === utils_1.AST_NODE_TYPES.UnaryExpression && | ||
posNode.operator === '-' && | ||
@@ -492,7 +491,7 @@ isLengthExpression(posNode.argument, eqNode.right)); | ||
const argNode = callNode.arguments[0]; | ||
const needsParen = argNode.type !== experimental_utils_1.AST_NODE_TYPES.Literal && | ||
argNode.type !== experimental_utils_1.AST_NODE_TYPES.TemplateLiteral && | ||
argNode.type !== experimental_utils_1.AST_NODE_TYPES.Identifier && | ||
argNode.type !== experimental_utils_1.AST_NODE_TYPES.MemberExpression && | ||
argNode.type !== experimental_utils_1.AST_NODE_TYPES.CallExpression; | ||
const needsParen = argNode.type !== utils_1.AST_NODE_TYPES.Literal && | ||
argNode.type !== utils_1.AST_NODE_TYPES.TemplateLiteral && | ||
argNode.type !== utils_1.AST_NODE_TYPES.Identifier && | ||
argNode.type !== utils_1.AST_NODE_TYPES.MemberExpression && | ||
argNode.type !== utils_1.AST_NODE_TYPES.CallExpression; | ||
yield fixer.removeRange([callNode.range[0], argNode.range[0]]); | ||
@@ -499,0 +498,0 @@ if (needsParen) { |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,4 +26,4 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
exports.default = util.createRule({ | ||
@@ -30,5 +34,4 @@ name: 'prefer-ts-expect-error', | ||
docs: { | ||
description: 'Recommends using `@ts-expect-error` over `@ts-ignore`', | ||
category: 'Best Practices', | ||
recommended: false, | ||
description: 'Enforce using `@ts-expect-error` over `@ts-ignore`', | ||
recommended: 'strict', | ||
}, | ||
@@ -47,3 +50,3 @@ fixable: 'code', | ||
function isLineComment(comment) { | ||
return comment.type === experimental_utils_1.AST_TOKEN_TYPES.Line; | ||
return comment.type === utils_1.AST_TOKEN_TYPES.Line; | ||
} | ||
@@ -50,0 +53,0 @@ function getLastCommentLine(comment) { |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const ts = __importStar(require("typescript")); | ||
@@ -32,4 +36,3 @@ const util = __importStar(require("../util")); | ||
docs: { | ||
description: 'Requires any function or method that returns a Promise to be marked async', | ||
category: 'Best Practices', | ||
description: 'Require any function or method that returns a Promise to be marked async', | ||
recommended: false, | ||
@@ -46,5 +49,7 @@ requiresTypeChecking: true, | ||
allowAny: { | ||
description: 'Whether to consider `any` and `unknown` to be Promises.', | ||
type: 'boolean', | ||
}, | ||
allowedPromiseNames: { | ||
description: 'Any extra names of classes or interfaces to be considered Promises.', | ||
type: 'array', | ||
@@ -89,2 +94,3 @@ items: { | ||
const checker = parserServices.program.getTypeChecker(); | ||
const sourceCode = context.getSourceCode(); | ||
function validateNode(node) { | ||
@@ -100,7 +106,9 @@ var _a; | ||
const returnType = checker.getReturnTypeOfSignature(signatures[0]); | ||
if (!util.containsAllTypesByName(returnType, allowAny, allAllowedPromiseNames)) { | ||
if (!util.containsAllTypesByName(returnType, allowAny, allAllowedPromiseNames, | ||
// If no return type is explicitly set, we check if any parts of the return type match a Promise (instead of requiring all to match). | ||
node.returnType == null)) { | ||
// Return type is not a promise | ||
return; | ||
} | ||
if (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === experimental_utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition) { | ||
if (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition) { | ||
// Abstract method can't be async | ||
@@ -110,4 +118,4 @@ return; | ||
if (node.parent && | ||
(node.parent.type === experimental_utils_1.AST_NODE_TYPES.Property || | ||
node.parent.type === experimental_utils_1.AST_NODE_TYPES.MethodDefinition) && | ||
(node.parent.type === utils_1.AST_NODE_TYPES.Property || | ||
node.parent.type === utils_1.AST_NODE_TYPES.MethodDefinition) && | ||
(node.parent.kind === 'get' || node.parent.kind === 'set')) { | ||
@@ -122,2 +130,3 @@ // Getters and setters can't be async | ||
node, | ||
loc: util.getFunctionHeadLoc(node, sourceCode), | ||
}); | ||
@@ -128,8 +137,30 @@ } | ||
node, | ||
loc: util.getFunctionHeadLoc(node, sourceCode), | ||
fix: fixer => { | ||
if (node.parent && | ||
(node.parent.type === experimental_utils_1.AST_NODE_TYPES.MethodDefinition || | ||
(node.parent.type === experimental_utils_1.AST_NODE_TYPES.Property && | ||
(node.parent.type === utils_1.AST_NODE_TYPES.MethodDefinition || | ||
(node.parent.type === utils_1.AST_NODE_TYPES.Property && | ||
node.parent.method))) { | ||
return fixer.insertTextBefore(node.parent.key, 'async '); | ||
// this function is a class method or object function property shorthand | ||
const method = node.parent; | ||
// the token to put `async` before | ||
let keyToken = sourceCode.getFirstToken(method); | ||
// if there are decorators then skip past them | ||
if (method.type === utils_1.AST_NODE_TYPES.MethodDefinition && | ||
method.decorators) { | ||
const lastDecorator = method.decorators[method.decorators.length - 1]; | ||
keyToken = sourceCode.getTokenAfter(lastDecorator); | ||
} | ||
// if current token is a keyword like `static` or `public` then skip it | ||
while (keyToken.type === utils_1.AST_TOKEN_TYPES.Keyword && | ||
keyToken.range[0] < method.key.range[0]) { | ||
keyToken = sourceCode.getTokenAfter(keyToken); | ||
} | ||
// check if there is a space between key and previous token | ||
const insertSpace = !sourceCode.isSpaceBetween(sourceCode.getTokenBefore(keyToken), keyToken); | ||
let code = 'async '; | ||
if (insertSpace) { | ||
code = ` ${code}`; | ||
} | ||
return fixer.insertTextBefore(keyToken, code); | ||
} | ||
@@ -140,16 +171,13 @@ return fixer.insertTextBefore(node, 'async '); | ||
} | ||
return { | ||
return Object.assign(Object.assign(Object.assign({}, (checkArrowFunctions && { | ||
'ArrowFunctionExpression[async = false]'(node) { | ||
if (checkArrowFunctions) { | ||
validateNode(node); | ||
} | ||
validateNode(node); | ||
}, | ||
})), (checkFunctionDeclarations && { | ||
'FunctionDeclaration[async = false]'(node) { | ||
if (checkFunctionDeclarations) { | ||
validateNode(node); | ||
} | ||
validateNode(node); | ||
}, | ||
'FunctionExpression[async = false]'(node) { | ||
})), { 'FunctionExpression[async = false]'(node) { | ||
if (node.parent && | ||
node.parent.type === experimental_utils_1.AST_NODE_TYPES.MethodDefinition && | ||
node.parent.type === utils_1.AST_NODE_TYPES.MethodDefinition && | ||
node.parent.kind === 'method') { | ||
@@ -164,6 +192,5 @@ if (checkMethodDeclarations) { | ||
} | ||
}, | ||
}; | ||
} }); | ||
}, | ||
}); | ||
//# sourceMappingURL=promise-function-async.js.map |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -21,10 +25,8 @@ if (k2 === undefined) k2 = k; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
var _a; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const quotes_1 = __importDefault(require("eslint/lib/rules/quotes")); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
const getESLintCoreRule_1 = require("../util/getESLintCoreRule"); | ||
const baseRule = (0, getESLintCoreRule_1.getESLintCoreRule)('quotes'); | ||
exports.default = util.createRule({ | ||
@@ -36,3 +38,2 @@ name: 'quotes', | ||
description: 'Enforce the consistent use of either backticks, double, or single quotes', | ||
category: 'Stylistic Issues', | ||
recommended: false, | ||
@@ -42,6 +43,8 @@ extendsBaseRule: true, | ||
fixable: 'code', | ||
messages: (_a = quotes_1.default.meta.messages) !== null && _a !== void 0 ? _a : { | ||
hasSuggestions: baseRule.meta.hasSuggestions, | ||
// TODO: this rule has only had messages since v7.0 - remove this when we remove support for v6 | ||
messages: (_a = baseRule.meta.messages) !== null && _a !== void 0 ? _a : { | ||
wrongQuotes: 'Strings must use {{description}}.', | ||
}, | ||
schema: quotes_1.default.meta.schema, | ||
schema: baseRule.meta.schema, | ||
}, | ||
@@ -56,17 +59,17 @@ defaultOptions: [ | ||
create(context, [option]) { | ||
const rules = quotes_1.default.create(context); | ||
const rules = baseRule.create(context); | ||
function isAllowedAsNonBacktick(node) { | ||
const parent = node.parent; | ||
switch (parent === null || parent === void 0 ? void 0 : parent.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition: | ||
case experimental_utils_1.AST_NODE_TYPES.TSMethodSignature: | ||
case experimental_utils_1.AST_NODE_TYPES.TSPropertySignature: | ||
case experimental_utils_1.AST_NODE_TYPES.TSModuleDeclaration: | ||
case experimental_utils_1.AST_NODE_TYPES.TSLiteralType: | ||
case experimental_utils_1.AST_NODE_TYPES.TSExternalModuleReference: | ||
case utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition: | ||
case utils_1.AST_NODE_TYPES.TSMethodSignature: | ||
case utils_1.AST_NODE_TYPES.TSPropertySignature: | ||
case utils_1.AST_NODE_TYPES.TSModuleDeclaration: | ||
case utils_1.AST_NODE_TYPES.TSLiteralType: | ||
case utils_1.AST_NODE_TYPES.TSExternalModuleReference: | ||
return true; | ||
case experimental_utils_1.AST_NODE_TYPES.TSEnumMember: | ||
case utils_1.AST_NODE_TYPES.TSEnumMember: | ||
return node === parent.id; | ||
case experimental_utils_1.AST_NODE_TYPES.TSAbstractClassProperty: | ||
case experimental_utils_1.AST_NODE_TYPES.ClassProperty: | ||
case utils_1.AST_NODE_TYPES.TSAbstractPropertyDefinition: | ||
case utils_1.AST_NODE_TYPES.PropertyDefinition: | ||
return node === parent.key; | ||
@@ -73,0 +76,0 @@ default: |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -33,4 +37,3 @@ if (k2 === undefined) k2 = k; | ||
docs: { | ||
description: 'Requires `Array#sort` calls to always provide a `compareFunction`', | ||
category: 'Best Practices', | ||
description: 'Require `Array#sort` calls to always provide a `compareFunction`', | ||
recommended: false, | ||
@@ -47,2 +50,3 @@ requiresTypeChecking: true, | ||
ignoreStringArrays: { | ||
description: 'Whether to ignore arrays in which all elements are strings.', | ||
type: 'boolean', | ||
@@ -64,3 +68,3 @@ }, | ||
if (checker.isArrayType(type) || checker.isTupleType(type)) { | ||
const typeArgs = checker.getTypeArguments(type); | ||
const typeArgs = util.getTypeArguments(type, checker); | ||
return typeArgs.every(arg => util.getTypeName(checker, arg) === 'string'); | ||
@@ -67,0 +71,0 @@ } |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const tsutils = __importStar(require("tsutils")); | ||
@@ -32,3 +36,2 @@ const util = __importStar(require("../util")); | ||
description: 'Disallow async functions which have no `await` expression', | ||
category: 'Best Practices', | ||
recommended: 'error', | ||
@@ -107,16 +110,19 @@ requiresTypeChecking: true, | ||
var _a; | ||
if (!scopeInfo || !scopeInfo.isGen || !node.argument) { | ||
if (!(scopeInfo === null || scopeInfo === void 0 ? void 0 : scopeInfo.isGen) || !node.argument) { | ||
return; | ||
} | ||
if (((_a = node === null || node === void 0 ? void 0 : node.argument) === null || _a === void 0 ? void 0 : _a.type) === experimental_utils_1.AST_NODE_TYPES.Literal) { | ||
if (((_a = node === null || node === void 0 ? void 0 : node.argument) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.Literal) { | ||
// making this `false` as for literals we don't need to check the definition | ||
// eg : async function* run() { yield* 1 } | ||
scopeInfo.isAsyncYield = false; | ||
scopeInfo.isAsyncYield || (scopeInfo.isAsyncYield = false); | ||
} | ||
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node === null || node === void 0 ? void 0 : node.argument); | ||
const type = checker.getTypeAtLocation(tsNode); | ||
const symbol = type.getSymbol(); | ||
// async function* test1() {yield* asyncGenerator() } | ||
if ((symbol === null || symbol === void 0 ? void 0 : symbol.getName()) === 'AsyncGenerator') { | ||
scopeInfo.isAsyncYield = true; | ||
const typesToCheck = expandUnionOrIntersectionType(type); | ||
for (const type of typesToCheck) { | ||
const asyncIterator = tsutils.getWellKnownSymbolPropertyOfType(type, 'asyncIterator', checker); | ||
if (asyncIterator !== undefined) { | ||
scopeInfo.isAsyncYield = true; | ||
break; | ||
} | ||
} | ||
@@ -157,3 +163,3 @@ } | ||
var _a; | ||
return (((_a = node.body) === null || _a === void 0 ? void 0 : _a.type) === experimental_utils_1.AST_NODE_TYPES.BlockStatement && | ||
return (((_a = node.body) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.BlockStatement && | ||
node.body.body.length === 0); | ||
@@ -178,3 +184,3 @@ } | ||
let end = null; | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.ArrowFunctionExpression) { | ||
if (node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) { | ||
const arrowToken = util.nullThrows(sourceCode.getTokenBefore(node.body, util.isArrowToken), util.NullThrowsReasons.MissingToken('=>', node.type)); | ||
@@ -184,4 +190,4 @@ start = arrowToken.loc.start; | ||
} | ||
else if (parent.type === experimental_utils_1.AST_NODE_TYPES.Property || | ||
parent.type === experimental_utils_1.AST_NODE_TYPES.MethodDefinition) { | ||
else if (parent.type === utils_1.AST_NODE_TYPES.Property || | ||
parent.type === utils_1.AST_NODE_TYPES.MethodDefinition) { | ||
start = parent.loc.start; | ||
@@ -199,2 +205,8 @@ end = getOpeningParenOfParams(node, sourceCode).loc.start; | ||
} | ||
function expandUnionOrIntersectionType(type) { | ||
if (type.isUnionOrIntersection()) { | ||
return type.types.flatMap(expandUnionOrIntersectionType); | ||
} | ||
return [type]; | ||
} | ||
//# sourceMappingURL=require-await.js.map |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,2 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const tsutils = __importStar(require("tsutils")); | ||
const ts = __importStar(require("typescript")); | ||
@@ -30,4 +35,3 @@ const util = __importStar(require("../util")); | ||
docs: { | ||
description: 'When adding two variables, operands must both be of type number or of type string', | ||
category: 'Best Practices', | ||
description: 'Require both operands of addition to be the same type and be `bigint`, `number`, or `string`', | ||
recommended: 'error', | ||
@@ -37,5 +41,5 @@ requiresTypeChecking: true, | ||
messages: { | ||
notNumbers: "Operands of '+' operation must either be both strings or both numbers.", | ||
notStrings: "Operands of '+' operation must either be both strings or both numbers. Consider using a template literal.", | ||
notBigInts: "Operands of '+' operation must be both bigints.", | ||
bigintAndNumber: "Numeric '+' operations must either be both bigints or both numbers. Got `{{left}}` + `{{right}}`.", | ||
invalid: "Invalid operand for a '+' operation. Operands must each be a number or {{stringLike}}. Got `{{type}}`.", | ||
mismatched: "Operands of '+' operations must be a number or {{stringLike}}. Got `{{left}}` + `{{right}}`.", | ||
}, | ||
@@ -47,3 +51,24 @@ schema: [ | ||
properties: { | ||
allowAny: { | ||
description: 'Whether to allow `any` typed values.', | ||
type: 'boolean', | ||
}, | ||
allowBoolean: { | ||
description: 'Whether to allow `boolean` typed values.', | ||
type: 'boolean', | ||
}, | ||
allowNullish: { | ||
description: 'Whether to allow potentially `null` or `undefined` typed values.', | ||
type: 'boolean', | ||
}, | ||
allowNumberAndString: { | ||
description: 'Whether to allow `bigint`/`number` typed values and `string` typed values to be added together.', | ||
type: 'boolean', | ||
}, | ||
allowRegExp: { | ||
description: 'Whether to allow `regexp` typed values.', | ||
type: 'boolean', | ||
}, | ||
checkCompoundAssignments: { | ||
description: 'Whether to check compound assignments such as `+=`.', | ||
type: 'boolean', | ||
@@ -60,66 +85,103 @@ }, | ||
], | ||
create(context, [{ checkCompoundAssignments }]) { | ||
create(context, [{ checkCompoundAssignments, allowAny, allowBoolean, allowNullish, allowNumberAndString, allowRegExp, },]) { | ||
const service = util.getParserServices(context); | ||
const typeChecker = service.program.getTypeChecker(); | ||
/** | ||
* Helper function to get base type of node | ||
*/ | ||
function getBaseTypeOfLiteralType(type) { | ||
if (type.isNumberLiteral()) { | ||
return 'number'; | ||
} | ||
if (type.isStringLiteral()) { | ||
return 'string'; | ||
} | ||
// is BigIntLiteral | ||
if (type.flags & ts.TypeFlags.BigIntLiteral) { | ||
return 'bigint'; | ||
} | ||
if (type.isUnion()) { | ||
const types = type.types.map(getBaseTypeOfLiteralType); | ||
return types.every(value => value === types[0]) ? types[0] : 'invalid'; | ||
} | ||
if (type.isIntersection()) { | ||
const types = type.types.map(getBaseTypeOfLiteralType); | ||
return types.some(value => value === 'string') ? 'string' : 'invalid'; | ||
} | ||
const stringType = typeChecker.typeToString(type); | ||
if (stringType === 'number' || | ||
stringType === 'string' || | ||
stringType === 'bigint') { | ||
return stringType; | ||
} | ||
return 'invalid'; | ||
const stringLikes = [ | ||
allowAny && '`any`', | ||
allowBoolean && '`boolean`', | ||
allowNullish && '`null`', | ||
allowRegExp && '`RegExp`', | ||
allowNullish && '`undefined`', | ||
].filter((value) => typeof value === 'string'); | ||
const stringLike = stringLikes.length | ||
? stringLikes.length === 1 | ||
? `string, allowing a string + ${stringLikes[0]}` | ||
: `string, allowing a string + any of: ${stringLikes.join(', ')}` | ||
: 'string'; | ||
function getTypeConstrained(node) { | ||
return typeChecker.getBaseTypeOfLiteralType(util.getConstrainedTypeAtLocation(typeChecker, service.esTreeNodeToTSNodeMap.get(node))); | ||
} | ||
/** | ||
* Helper function to get base type of node | ||
* @param node the node to be evaluated. | ||
*/ | ||
function getNodeType(node) { | ||
const tsNode = service.esTreeNodeToTSNodeMap.get(node); | ||
const type = util.getConstrainedTypeAtLocation(typeChecker, tsNode); | ||
return getBaseTypeOfLiteralType(type); | ||
} | ||
function checkPlusOperands(node) { | ||
const leftType = getNodeType(node.left); | ||
const rightType = getNodeType(node.right); | ||
if (leftType === 'invalid' || | ||
rightType === 'invalid' || | ||
leftType !== rightType) { | ||
if (leftType === 'string' || rightType === 'string') { | ||
const leftType = getTypeConstrained(node.left); | ||
const rightType = getTypeConstrained(node.right); | ||
if (leftType === rightType && | ||
tsutils.isTypeFlagSet(leftType, ts.TypeFlags.BigIntLike | | ||
ts.TypeFlags.NumberLike | | ||
ts.TypeFlags.StringLike)) { | ||
return; | ||
} | ||
let hadIndividualComplaint = false; | ||
for (const [baseNode, baseType, otherType] of [ | ||
[node.left, leftType, rightType], | ||
[node.right, rightType, leftType], | ||
]) { | ||
if (isTypeFlagSetInUnion(baseType, ts.TypeFlags.ESSymbolLike | | ||
ts.TypeFlags.Never | | ||
ts.TypeFlags.Unknown) || | ||
(!allowAny && isTypeFlagSetInUnion(baseType, ts.TypeFlags.Any)) || | ||
(!allowBoolean && | ||
isTypeFlagSetInUnion(baseType, ts.TypeFlags.BooleanLike)) || | ||
(!allowNullish && | ||
util.isTypeFlagSet(baseType, ts.TypeFlags.Null | ts.TypeFlags.Undefined))) { | ||
context.report({ | ||
node, | ||
messageId: 'notStrings', | ||
data: { | ||
stringLike, | ||
type: typeChecker.typeToString(baseType), | ||
}, | ||
messageId: 'invalid', | ||
node: baseNode, | ||
}); | ||
hadIndividualComplaint = true; | ||
continue; | ||
} | ||
else if (leftType === 'bigint' || rightType === 'bigint') { | ||
context.report({ | ||
// RegExps also contain ts.TypeFlags.Any & ts.TypeFlags.Object | ||
for (const subBaseType of tsutils.unionTypeParts(baseType)) { | ||
const typeName = util.getTypeName(typeChecker, subBaseType); | ||
if (typeName === 'RegExp' | ||
? !allowRegExp || | ||
tsutils.isTypeFlagSet(otherType, ts.TypeFlags.NumberLike) | ||
: (!allowAny && util.isTypeAnyType(subBaseType)) || | ||
isDeeplyObjectType(subBaseType)) { | ||
context.report({ | ||
data: { | ||
stringLike, | ||
type: typeChecker.typeToString(subBaseType), | ||
}, | ||
messageId: 'invalid', | ||
node: baseNode, | ||
}); | ||
hadIndividualComplaint = true; | ||
continue; | ||
} | ||
} | ||
} | ||
if (hadIndividualComplaint) { | ||
return; | ||
} | ||
for (const [baseType, otherType] of [ | ||
[leftType, rightType], | ||
[rightType, leftType], | ||
]) { | ||
if (!allowNumberAndString && | ||
isTypeFlagSetInUnion(baseType, ts.TypeFlags.StringLike) && | ||
isTypeFlagSetInUnion(otherType, ts.TypeFlags.NumberLike)) { | ||
return context.report({ | ||
data: { | ||
stringLike, | ||
left: typeChecker.typeToString(leftType), | ||
right: typeChecker.typeToString(rightType), | ||
}, | ||
messageId: 'mismatched', | ||
node, | ||
messageId: 'notBigInts', | ||
}); | ||
} | ||
else { | ||
context.report({ | ||
if (isTypeFlagSetInUnion(baseType, ts.TypeFlags.NumberLike) && | ||
isTypeFlagSetInUnion(otherType, ts.TypeFlags.BigIntLike)) { | ||
return context.report({ | ||
data: { | ||
left: typeChecker.typeToString(leftType), | ||
right: typeChecker.typeToString(rightType), | ||
}, | ||
messageId: 'bigintAndNumber', | ||
node, | ||
messageId: 'notNumbers', | ||
}); | ||
@@ -129,12 +191,19 @@ } | ||
} | ||
return { | ||
"BinaryExpression[operator='+']": checkPlusOperands, | ||
return Object.assign({ "BinaryExpression[operator='+']": checkPlusOperands }, (checkCompoundAssignments && { | ||
"AssignmentExpression[operator='+=']"(node) { | ||
if (checkCompoundAssignments) { | ||
checkPlusOperands(node); | ||
} | ||
checkPlusOperands(node); | ||
}, | ||
}; | ||
})); | ||
}, | ||
}); | ||
function isDeeplyObjectType(type) { | ||
return type.isIntersection() | ||
? tsutils.intersectionTypeParts(type).every(tsutils.isObjectType) | ||
: tsutils.unionTypeParts(type).every(tsutils.isObjectType); | ||
} | ||
function isTypeFlagSetInUnion(type, flag) { | ||
return tsutils | ||
.unionTypeParts(type) | ||
.some(subType => tsutils.isTypeFlagSet(subType, flag)); | ||
} | ||
//# sourceMappingURL=restrict-plus-operands.js.map |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const ts = __importStar(require("typescript")); | ||
@@ -31,4 +35,3 @@ const util = __importStar(require("../util")); | ||
docs: { | ||
description: 'Enforce template literal expressions to be of string type', | ||
category: 'Best Practices', | ||
description: 'Enforce template literal expressions to be of `string` type', | ||
recommended: 'error', | ||
@@ -44,6 +47,26 @@ requiresTypeChecking: true, | ||
properties: { | ||
allowNumber: { type: 'boolean' }, | ||
allowBoolean: { type: 'boolean' }, | ||
allowAny: { type: 'boolean' }, | ||
allowNullish: { type: 'boolean' }, | ||
allowAny: { | ||
description: 'Whether to allow `any` typed values in template expressions.', | ||
type: 'boolean', | ||
}, | ||
allowBoolean: { | ||
description: 'Whether to allow `boolean` typed values in template expressions.', | ||
type: 'boolean', | ||
}, | ||
allowNullish: { | ||
description: 'Whether to allow `nullish` typed values in template expressions.', | ||
type: 'boolean', | ||
}, | ||
allowNumber: { | ||
description: 'Whether to allow `number` typed values in template expressions.', | ||
type: 'boolean', | ||
}, | ||
allowRegExp: { | ||
description: 'Whether to allow `regexp` typed values in template expressions.', | ||
type: 'boolean', | ||
}, | ||
allowNever: { | ||
description: 'Whether to allow `never` typed values in template expressions.', | ||
type: 'boolean', | ||
}, | ||
}, | ||
@@ -76,2 +99,6 @@ }, | ||
} | ||
if (options.allowRegExp && | ||
util.getTypeName(typeChecker, type) === 'RegExp') { | ||
return true; | ||
} | ||
if (options.allowNullish && | ||
@@ -81,2 +108,5 @@ util.isTypeFlagSet(type, ts.TypeFlags.Null | ts.TypeFlags.Undefined)) { | ||
} | ||
if (options.allowNever && util.isTypeNeverType(type)) { | ||
return true; | ||
} | ||
return false; | ||
@@ -87,3 +117,3 @@ } | ||
// don't check tagged template literals | ||
if (node.parent.type === experimental_utils_1.AST_NODE_TYPES.TaggedTemplateExpression) { | ||
if (node.parent.type === utils_1.AST_NODE_TYPES.TaggedTemplateExpression) { | ||
return; | ||
@@ -90,0 +120,0 @@ } |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,6 +26,8 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const tsutils = __importStar(require("tsutils")); | ||
const tsutils_1 = require("tsutils"); | ||
const ts = __importStar(require("typescript")); | ||
const util = __importStar(require("../util")); | ||
const getOperatorPrecedence_1 = require("../util/getOperatorPrecedence"); | ||
exports.default = util.createRule({ | ||
@@ -31,4 +37,3 @@ name: 'return-await', | ||
docs: { | ||
description: 'Enforces consistent returning of awaited values', | ||
category: 'Best Practices', | ||
description: 'Enforce consistent returning of awaited values', | ||
recommended: false, | ||
@@ -39,2 +44,3 @@ requiresTypeChecking: true, | ||
fixable: 'code', | ||
hasSuggestions: true, | ||
type: 'problem', | ||
@@ -57,12 +63,16 @@ messages: { | ||
const sourceCode = context.getSourceCode(); | ||
let scopeInfo = null; | ||
const scopeInfoStack = []; | ||
function enterFunction(node) { | ||
scopeInfo = { | ||
scopeInfoStack.push({ | ||
hasAsync: node.async, | ||
}; | ||
owningFunc: node, | ||
}); | ||
} | ||
function exitFunction() { | ||
scopeInfoStack.pop(); | ||
} | ||
function inTry(node) { | ||
let ancestor = node.parent; | ||
while (ancestor && !ts.isFunctionLike(ancestor)) { | ||
if (tsutils.isTryStatement(ancestor)) { | ||
if (ts.isTryStatement(ancestor)) { | ||
return true; | ||
@@ -77,3 +87,3 @@ } | ||
while (ancestor && !ts.isFunctionLike(ancestor)) { | ||
if (tsutils.isCatchClause(ancestor)) { | ||
if (ts.isCatchClause(ancestor)) { | ||
return true; | ||
@@ -88,4 +98,4 @@ } | ||
while (ancestor && !ts.isFunctionLike(ancestor)) { | ||
if (tsutils.isTryStatement(ancestor.parent) && | ||
tsutils.isBlock(ancestor) && | ||
if (ts.isTryStatement(ancestor.parent) && | ||
ts.isBlock(ancestor) && | ||
ancestor.parent.end === ancestor.end) { | ||
@@ -101,3 +111,3 @@ return true; | ||
while (ancestor && !ts.isFunctionLike(ancestor)) { | ||
if (tsutils.isTryStatement(ancestor)) { | ||
if (ts.isTryStatement(ancestor)) { | ||
return !!ancestor.finallyBlock; | ||
@@ -131,8 +141,24 @@ } | ||
} | ||
function insertAwait(fixer, node) { | ||
return fixer.insertTextBefore(node, 'await '); | ||
function insertAwait(fixer, node, isHighPrecendence) { | ||
if (isHighPrecendence) { | ||
return fixer.insertTextBefore(node, 'await '); | ||
} | ||
else { | ||
return [ | ||
fixer.insertTextBefore(node, 'await ('), | ||
fixer.insertTextAfter(node, ')'), | ||
]; | ||
} | ||
} | ||
function isHigherPrecedenceThanAwait(node) { | ||
const operator = (0, tsutils_1.isBinaryExpression)(node) | ||
? node.operatorToken.kind | ||
: ts.SyntaxKind.Unknown; | ||
const nodePrecedence = (0, getOperatorPrecedence_1.getOperatorPrecedence)(node.kind, operator); | ||
const awaitPrecedence = (0, getOperatorPrecedence_1.getOperatorPrecedence)(ts.SyntaxKind.AwaitExpression, ts.SyntaxKind.Unknown); | ||
return nodePrecedence > awaitPrecedence; | ||
} | ||
function test(node, expression) { | ||
let child; | ||
const isAwait = tsutils.isAwaitExpression(expression); | ||
const isAwait = ts.isAwaitExpression(expression); | ||
if (isAwait) { | ||
@@ -170,3 +196,3 @@ child = expression.getChildAt(1); | ||
node, | ||
fix: fixer => insertAwait(fixer, node), | ||
fix: fixer => insertAwait(fixer, node, isHigherPrecedenceThanAwait(expression)), | ||
}); | ||
@@ -205,3 +231,3 @@ } | ||
node, | ||
fix: fixer => insertAwait(fixer, node), | ||
fix: fixer => insertAwait(fixer, node, isHigherPrecedenceThanAwait(expression)), | ||
}); | ||
@@ -213,3 +239,3 @@ } | ||
function findPossiblyReturnedNodes(node) { | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.ConditionalExpression) { | ||
if (node.type === utils_1.AST_NODE_TYPES.ConditionalExpression) { | ||
return [ | ||
@@ -226,4 +252,8 @@ ...findPossiblyReturnedNodes(node.alternate), | ||
ArrowFunctionExpression: enterFunction, | ||
'FunctionDeclaration:exit': exitFunction, | ||
'FunctionExpression:exit': exitFunction, | ||
'ArrowFunctionExpression:exit': exitFunction, | ||
// executes after less specific handler, so exitFunction is called | ||
'ArrowFunctionExpression[async = true]:exit'(node) { | ||
if (node.body.type !== experimental_utils_1.AST_NODE_TYPES.BlockStatement) { | ||
if (node.body.type !== utils_1.AST_NODE_TYPES.BlockStatement) { | ||
findPossiblyReturnedNodes(node.body).forEach(node => { | ||
@@ -236,3 +266,4 @@ const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node); | ||
ReturnStatement(node) { | ||
if (!scopeInfo || !scopeInfo.hasAsync || !node.argument) { | ||
const scopeInfo = scopeInfoStack[scopeInfoStack.length - 1]; | ||
if (!(scopeInfo === null || scopeInfo === void 0 ? void 0 : scopeInfo.hasAsync) || !node.argument) { | ||
return; | ||
@@ -239,0 +270,0 @@ } |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -21,10 +25,8 @@ if (k2 === undefined) k2 = k; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
var _a; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const semi_1 = __importDefault(require("eslint/lib/rules/semi")); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
const getESLintCoreRule_1 = require("../util/getESLintCoreRule"); | ||
const baseRule = (0, getESLintCoreRule_1.getESLintCoreRule)('semi'); | ||
exports.default = util.createRule({ | ||
@@ -36,3 +38,2 @@ name: 'semi', | ||
description: 'Require or disallow semicolons instead of ASI', | ||
category: 'Stylistic Issues', | ||
// too opinionated to be recommended | ||
@@ -43,4 +44,6 @@ recommended: false, | ||
fixable: 'code', | ||
schema: semi_1.default.meta.schema, | ||
messages: (_a = semi_1.default.meta.messages) !== null && _a !== void 0 ? _a : { | ||
hasSuggestions: baseRule.meta.hasSuggestions, | ||
schema: baseRule.meta.schema, | ||
// TODO: this rule has only had messages since v7.0 - remove this when we remove support for v6 | ||
messages: (_a = baseRule.meta.messages) !== null && _a !== void 0 ? _a : { | ||
missingSemi: 'Missing semicolon.', | ||
@@ -58,3 +61,3 @@ extraSemi: 'Extra semicolon.', | ||
create(context) { | ||
const rules = semi_1.default.create(context); | ||
const rules = baseRule.create(context); | ||
const checkForSemicolon = rules.ExpressionStatement; | ||
@@ -70,9 +73,9 @@ /* | ||
const nodesToCheck = [ | ||
experimental_utils_1.AST_NODE_TYPES.ClassProperty, | ||
experimental_utils_1.AST_NODE_TYPES.TSAbstractClassProperty, | ||
experimental_utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition, | ||
experimental_utils_1.AST_NODE_TYPES.TSDeclareFunction, | ||
experimental_utils_1.AST_NODE_TYPES.TSExportAssignment, | ||
experimental_utils_1.AST_NODE_TYPES.TSImportEqualsDeclaration, | ||
experimental_utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration, | ||
utils_1.AST_NODE_TYPES.PropertyDefinition, | ||
utils_1.AST_NODE_TYPES.TSAbstractPropertyDefinition, | ||
utils_1.AST_NODE_TYPES.TSDeclareFunction, | ||
utils_1.AST_NODE_TYPES.TSExportAssignment, | ||
utils_1.AST_NODE_TYPES.TSImportEqualsDeclaration, | ||
utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration, | ||
utils_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression, | ||
].reduce((acc, node) => { | ||
@@ -83,3 +86,3 @@ acc[node] = checkForSemicolon; | ||
return Object.assign(Object.assign(Object.assign({}, rules), nodesToCheck), { ExportDefaultDeclaration(node) { | ||
if (node.declaration.type !== experimental_utils_1.AST_NODE_TYPES.TSInterfaceDeclaration) { | ||
if (node.declaration.type !== utils_1.AST_NODE_TYPES.TSInterfaceDeclaration) { | ||
rules.ExportDefaultDeclaration(node); | ||
@@ -86,0 +89,0 @@ } |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -43,52 +47,60 @@ const util_1 = require("../util"); | ||
switch (node.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.TSParenthesizedType: | ||
return getGroup(node.typeAnnotation); | ||
case experimental_utils_1.AST_NODE_TYPES.TSConditionalType: | ||
case utils_1.AST_NODE_TYPES.TSConditionalType: | ||
return Group.conditional; | ||
case experimental_utils_1.AST_NODE_TYPES.TSConstructorType: | ||
case experimental_utils_1.AST_NODE_TYPES.TSFunctionType: | ||
case utils_1.AST_NODE_TYPES.TSConstructorType: | ||
case utils_1.AST_NODE_TYPES.TSFunctionType: | ||
return Group.function; | ||
case experimental_utils_1.AST_NODE_TYPES.TSImportType: | ||
case utils_1.AST_NODE_TYPES.TSImportType: | ||
return Group.import; | ||
case experimental_utils_1.AST_NODE_TYPES.TSIntersectionType: | ||
case utils_1.AST_NODE_TYPES.TSIntersectionType: | ||
return Group.intersection; | ||
case experimental_utils_1.AST_NODE_TYPES.TSAnyKeyword: | ||
case experimental_utils_1.AST_NODE_TYPES.TSBigIntKeyword: | ||
case experimental_utils_1.AST_NODE_TYPES.TSBooleanKeyword: | ||
case experimental_utils_1.AST_NODE_TYPES.TSNeverKeyword: | ||
case experimental_utils_1.AST_NODE_TYPES.TSNumberKeyword: | ||
case experimental_utils_1.AST_NODE_TYPES.TSObjectKeyword: | ||
case experimental_utils_1.AST_NODE_TYPES.TSStringKeyword: | ||
case experimental_utils_1.AST_NODE_TYPES.TSSymbolKeyword: | ||
case experimental_utils_1.AST_NODE_TYPES.TSThisType: | ||
case experimental_utils_1.AST_NODE_TYPES.TSUnknownKeyword: | ||
case utils_1.AST_NODE_TYPES.TSAnyKeyword: | ||
case utils_1.AST_NODE_TYPES.TSBigIntKeyword: | ||
case utils_1.AST_NODE_TYPES.TSBooleanKeyword: | ||
case utils_1.AST_NODE_TYPES.TSNeverKeyword: | ||
case utils_1.AST_NODE_TYPES.TSNumberKeyword: | ||
case utils_1.AST_NODE_TYPES.TSObjectKeyword: | ||
case utils_1.AST_NODE_TYPES.TSStringKeyword: | ||
case utils_1.AST_NODE_TYPES.TSSymbolKeyword: | ||
case utils_1.AST_NODE_TYPES.TSThisType: | ||
case utils_1.AST_NODE_TYPES.TSUnknownKeyword: | ||
case utils_1.AST_NODE_TYPES.TSIntrinsicKeyword: | ||
return Group.keyword; | ||
case experimental_utils_1.AST_NODE_TYPES.TSNullKeyword: | ||
case experimental_utils_1.AST_NODE_TYPES.TSUndefinedKeyword: | ||
case experimental_utils_1.AST_NODE_TYPES.TSVoidKeyword: | ||
case utils_1.AST_NODE_TYPES.TSNullKeyword: | ||
case utils_1.AST_NODE_TYPES.TSUndefinedKeyword: | ||
case utils_1.AST_NODE_TYPES.TSVoidKeyword: | ||
return Group.nullish; | ||
case experimental_utils_1.AST_NODE_TYPES.TSLiteralType: | ||
case experimental_utils_1.AST_NODE_TYPES.TSTemplateLiteralType: | ||
case utils_1.AST_NODE_TYPES.TSLiteralType: | ||
case utils_1.AST_NODE_TYPES.TSTemplateLiteralType: | ||
return Group.literal; | ||
case experimental_utils_1.AST_NODE_TYPES.TSArrayType: | ||
case experimental_utils_1.AST_NODE_TYPES.TSIndexedAccessType: | ||
case experimental_utils_1.AST_NODE_TYPES.TSInferType: | ||
case experimental_utils_1.AST_NODE_TYPES.TSTypeReference: | ||
case utils_1.AST_NODE_TYPES.TSArrayType: | ||
case utils_1.AST_NODE_TYPES.TSIndexedAccessType: | ||
case utils_1.AST_NODE_TYPES.TSInferType: | ||
case utils_1.AST_NODE_TYPES.TSTypeReference: | ||
case utils_1.AST_NODE_TYPES.TSQualifiedName: | ||
return Group.named; | ||
case experimental_utils_1.AST_NODE_TYPES.TSMappedType: | ||
case experimental_utils_1.AST_NODE_TYPES.TSTypeLiteral: | ||
case utils_1.AST_NODE_TYPES.TSMappedType: | ||
case utils_1.AST_NODE_TYPES.TSTypeLiteral: | ||
return Group.object; | ||
case experimental_utils_1.AST_NODE_TYPES.TSTypeOperator: | ||
case experimental_utils_1.AST_NODE_TYPES.TSTypeQuery: | ||
case utils_1.AST_NODE_TYPES.TSTypeOperator: | ||
case utils_1.AST_NODE_TYPES.TSTypeQuery: | ||
return Group.operator; | ||
case experimental_utils_1.AST_NODE_TYPES.TSTupleType: | ||
case utils_1.AST_NODE_TYPES.TSTupleType: | ||
return Group.tuple; | ||
case experimental_utils_1.AST_NODE_TYPES.TSUnionType: | ||
case utils_1.AST_NODE_TYPES.TSUnionType: | ||
return Group.union; | ||
// These types should never occur as part of a union/intersection | ||
case experimental_utils_1.AST_NODE_TYPES.TSInterfaceHeritage: | ||
case experimental_utils_1.AST_NODE_TYPES.TSNamedTupleMember: | ||
case experimental_utils_1.AST_NODE_TYPES.TSOptionalType: | ||
case experimental_utils_1.AST_NODE_TYPES.TSRestType: | ||
case experimental_utils_1.AST_NODE_TYPES.TSTypePredicate: | ||
case utils_1.AST_NODE_TYPES.TSAbstractKeyword: | ||
case utils_1.AST_NODE_TYPES.TSAsyncKeyword: | ||
case utils_1.AST_NODE_TYPES.TSDeclareKeyword: | ||
case utils_1.AST_NODE_TYPES.TSExportKeyword: | ||
case utils_1.AST_NODE_TYPES.TSNamedTupleMember: | ||
case utils_1.AST_NODE_TYPES.TSOptionalType: | ||
case utils_1.AST_NODE_TYPES.TSPrivateKeyword: | ||
case utils_1.AST_NODE_TYPES.TSProtectedKeyword: | ||
case utils_1.AST_NODE_TYPES.TSPublicKeyword: | ||
case utils_1.AST_NODE_TYPES.TSReadonlyKeyword: | ||
case utils_1.AST_NODE_TYPES.TSRestType: | ||
case utils_1.AST_NODE_TYPES.TSStaticKeyword: | ||
case utils_1.AST_NODE_TYPES.TSTypePredicate: | ||
/* istanbul ignore next */ | ||
@@ -101,9 +113,10 @@ throw new Error(`Unexpected Type ${node.type}`); | ||
meta: { | ||
deprecated: true, | ||
type: 'suggestion', | ||
docs: { | ||
description: 'Enforces that members of a type union/intersection are sorted alphabetically', | ||
category: 'Stylistic Issues', | ||
description: 'Enforce members of a type union/intersection to be sorted alphabetically', | ||
recommended: false, | ||
}, | ||
fixable: 'code', | ||
hasSuggestions: true, | ||
messages: { | ||
@@ -114,2 +127,3 @@ notSorted: '{{type}} type members must be sorted.', | ||
}, | ||
replacedBy: ['@typescript-eslint/sort-type-constituents'], | ||
schema: [ | ||
@@ -120,12 +134,15 @@ { | ||
checkIntersections: { | ||
description: 'Whether to check intersection types.', | ||
type: 'boolean', | ||
}, | ||
checkUnions: { | ||
description: 'Whether to check union types.', | ||
type: 'boolean', | ||
}, | ||
groupOrder: { | ||
description: 'Ordering of the groups.', | ||
type: 'array', | ||
items: { | ||
type: 'string', | ||
enum: util_1.getEnumNames(Group), | ||
enum: (0, util_1.getEnumNames)(Group), | ||
}, | ||
@@ -191,7 +208,7 @@ }, | ||
name: '', | ||
type: node.type === experimental_utils_1.AST_NODE_TYPES.TSIntersectionType | ||
type: node.type === utils_1.AST_NODE_TYPES.TSIntersectionType | ||
? 'Intersection' | ||
: 'Union', | ||
}; | ||
if (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === experimental_utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration) { | ||
if (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration) { | ||
messageId = 'notSortedNamed'; | ||
@@ -202,4 +219,8 @@ data.name = node.parent.id.name; | ||
const sorted = expectedOrder | ||
.map(t => t.text) | ||
.join(node.type === experimental_utils_1.AST_NODE_TYPES.TSIntersectionType ? ' & ' : ' | '); | ||
.map(t => (0, util_1.typeNodeRequiresParentheses)(t.node, t.text) || | ||
(node.type === utils_1.AST_NODE_TYPES.TSIntersectionType && | ||
t.node.type === utils_1.AST_NODE_TYPES.TSUnionType) | ||
? `(${t.text})` | ||
: t.text) | ||
.join(node.type === utils_1.AST_NODE_TYPES.TSIntersectionType ? ' & ' : ' | '); | ||
return fixer.replaceText(node, sorted); | ||
@@ -222,16 +243,13 @@ }; | ||
} | ||
return { | ||
return Object.assign(Object.assign({}, (checkIntersections && { | ||
TSIntersectionType(node) { | ||
if (checkIntersections === true) { | ||
checkSorting(node); | ||
} | ||
checkSorting(node); | ||
}, | ||
})), (checkUnions && { | ||
TSUnionType(node) { | ||
if (checkUnions === true) { | ||
checkSorting(node); | ||
} | ||
checkSorting(node); | ||
}, | ||
}; | ||
})); | ||
}, | ||
}); | ||
//# sourceMappingURL=sort-type-union-intersection-members.js.map |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -30,4 +34,3 @@ exports.default = util.createRule({ | ||
docs: { | ||
description: 'Enforces consistent spacing before function parenthesis', | ||
category: 'Stylistic Issues', | ||
description: 'Enforce consistent spacing before function parenthesis', | ||
recommended: false, | ||
@@ -67,6 +70,6 @@ extendsBaseRule: true, | ||
defaultOptions: ['always'], | ||
create(context) { | ||
create(context, [firstOption]) { | ||
const sourceCode = context.getSourceCode(); | ||
const baseConfig = typeof context.options[0] === 'string' ? context.options[0] : 'always'; | ||
const overrideConfig = typeof context.options[0] === 'object' ? context.options[0] : {}; | ||
const baseConfig = typeof firstOption === 'string' ? firstOption : 'always'; | ||
const overrideConfig = typeof firstOption === 'object' ? firstOption : {}; | ||
/** | ||
@@ -82,5 +85,5 @@ * Determines whether a function has a name. | ||
const parent = node.parent; | ||
return (parent.type === experimental_utils_1.AST_NODE_TYPES.MethodDefinition || | ||
parent.type === experimental_utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition || | ||
(parent.type === experimental_utils_1.AST_NODE_TYPES.Property && | ||
return (parent.type === utils_1.AST_NODE_TYPES.MethodDefinition || | ||
parent.type === utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition || | ||
(parent.type === utils_1.AST_NODE_TYPES.Property && | ||
(parent.kind === 'get' || parent.kind === 'set' || parent.method))); | ||
@@ -95,3 +98,3 @@ } | ||
var _a, _b, _c; | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.ArrowFunctionExpression) { | ||
if (node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) { | ||
// Always ignore non-async functions and arrow functions without parens, e.g. async foo => bar | ||
@@ -122,3 +125,4 @@ if (node.async && | ||
} | ||
let leftToken, rightToken; | ||
let leftToken; | ||
let rightToken; | ||
if (node.typeParameters) { | ||
@@ -132,2 +136,3 @@ leftToken = sourceCode.getLastToken(node.typeParameters); | ||
} | ||
// eslint-disable-next-line deprecation/deprecation -- TODO - switch once our min ESLint version is 6.7.0 | ||
const hasSpacing = sourceCode.isSpaceBetweenTokens(leftToken, rightToken); | ||
@@ -134,0 +139,0 @@ if (hasSpacing && functionConfig === 'never') { |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -21,10 +25,8 @@ if (k2 === undefined) k2 = k; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
var _a; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const space_infix_ops_1 = __importDefault(require("eslint/lib/rules/space-infix-ops")); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
const getESLintCoreRule_1 = require("../util/getESLintCoreRule"); | ||
const baseRule = (0, getESLintCoreRule_1.getESLintCoreRule)('space-infix-ops'); | ||
const UNIONS = ['|', '&']; | ||
exports.default = util.createRule({ | ||
@@ -35,12 +37,12 @@ name: 'space-infix-ops', | ||
docs: { | ||
description: 'This rule is aimed at ensuring there are spaces around infix operators.', | ||
category: 'Stylistic Issues', | ||
description: 'Require spacing around infix operators', | ||
recommended: false, | ||
extendsBaseRule: true, | ||
}, | ||
fixable: space_infix_ops_1.default.meta.fixable, | ||
schema: space_infix_ops_1.default.meta.schema, | ||
messages: (_a = space_infix_ops_1.default.meta.messages) !== null && _a !== void 0 ? _a : { | ||
missingSpace: "Operator '{{operator}}' must be spaced.", | ||
}, | ||
fixable: baseRule.meta.fixable, | ||
hasSuggestions: baseRule.meta.hasSuggestions, | ||
schema: baseRule.meta.schema, | ||
messages: Object.assign({ | ||
// @ts-expect-error -- we report on this messageId so we need to ensure it's there in case ESLint changes in future | ||
missingSpace: "Operator '{{operator}}' must be spaced." }, baseRule.meta.messages), | ||
}, | ||
@@ -53,49 +55,95 @@ defaultOptions: [ | ||
create(context) { | ||
const rules = space_infix_ops_1.default.create(context); | ||
const rules = baseRule.create(context); | ||
const sourceCode = context.getSourceCode(); | ||
/** | ||
* Check if it has an assignment char and report if it's faulty | ||
* @param node The node to report | ||
*/ | ||
function checkForAssignmentSpace(node) { | ||
if (!node.initializer) { | ||
function report(operator) { | ||
context.report({ | ||
node: operator, | ||
messageId: 'missingSpace', | ||
data: { | ||
operator: operator.value, | ||
}, | ||
fix(fixer) { | ||
const previousToken = sourceCode.getTokenBefore(operator); | ||
const afterToken = sourceCode.getTokenAfter(operator); | ||
let fixString = ''; | ||
if (operator.range[0] - previousToken.range[1] === 0) { | ||
fixString = ' '; | ||
} | ||
fixString += operator.value; | ||
if (afterToken.range[0] - operator.range[1] === 0) { | ||
fixString += ' '; | ||
} | ||
return fixer.replaceText(operator, fixString); | ||
}, | ||
}); | ||
} | ||
function isSpaceChar(token) { | ||
return (token.type === utils_1.AST_TOKEN_TYPES.Punctuator && /^[=?:]$/.test(token.value)); | ||
} | ||
function checkAndReportAssignmentSpace(leftNode, rightNode) { | ||
if (!rightNode || !leftNode) { | ||
return; | ||
} | ||
const leftNode = sourceCode.getTokenByRangeStart(node.id.range[0]); | ||
const rightNode = sourceCode.getTokenByRangeStart(node.initializer.range[0]); | ||
if (!rightNode) { | ||
return; | ||
} | ||
const operator = sourceCode.getFirstTokenBetween(leftNode, rightNode, token => token.type === experimental_utils_1.AST_TOKEN_TYPES.Punctuator && token.value === '='); | ||
const operator = sourceCode.getFirstTokenBetween(leftNode, rightNode, isSpaceChar); | ||
const prev = sourceCode.getTokenBefore(operator); | ||
const next = sourceCode.getTokenAfter(operator); | ||
if (operator && | ||
(!sourceCode.isSpaceBetweenTokens(prev, operator) || | ||
!sourceCode.isSpaceBetweenTokens(operator, next))) { | ||
context.report({ | ||
node: node, | ||
loc: operator.loc, | ||
messageId: 'missingSpace', | ||
data: { | ||
operator: operator.value, | ||
}, | ||
fix(fixer) { | ||
const previousToken = sourceCode.getTokenBefore(operator); | ||
const afterToken = sourceCode.getTokenAfter(operator); | ||
let fixString = ''; | ||
if (operator.range[0] - previousToken.range[1] === 0) { | ||
fixString = ' '; | ||
} | ||
fixString += operator.value; | ||
if (afterToken.range[0] - operator.range[1] === 0) { | ||
fixString += ' '; | ||
} | ||
return fixer.replaceText(operator, fixString); | ||
}, | ||
}); | ||
if (!sourceCode.isSpaceBetween(prev, operator) || | ||
!sourceCode.isSpaceBetween(operator, next)) { | ||
report(operator); | ||
} | ||
} | ||
return Object.assign(Object.assign({}, rules), { TSEnumMember: checkForAssignmentSpace }); | ||
/** | ||
* Check if it has an assignment char and report if it's faulty | ||
* @param node The node to report | ||
*/ | ||
function checkForEnumAssignmentSpace(node) { | ||
checkAndReportAssignmentSpace(node.id, node.initializer); | ||
} | ||
/** | ||
* Check if it has an assignment char and report if it's faulty | ||
* @param node The node to report | ||
*/ | ||
function checkForPropertyDefinitionAssignmentSpace(node) { | ||
var _a; | ||
const leftNode = node.optional && !node.typeAnnotation | ||
? sourceCode.getTokenAfter(node.key) | ||
: (_a = node.typeAnnotation) !== null && _a !== void 0 ? _a : node.key; | ||
checkAndReportAssignmentSpace(leftNode, node.value); | ||
} | ||
/** | ||
* Check if it is missing spaces between type annotations chaining | ||
* @param typeAnnotation TypeAnnotations list | ||
*/ | ||
function checkForTypeAnnotationSpace(typeAnnotation) { | ||
const types = typeAnnotation.types; | ||
types.forEach(type => { | ||
const skipFunctionParenthesis = type.type === utils_1.TSESTree.AST_NODE_TYPES.TSFunctionType | ||
? util.isNotOpeningParenToken | ||
: 0; | ||
const operator = sourceCode.getTokenBefore(type, skipFunctionParenthesis); | ||
if (operator != null && UNIONS.includes(operator.value)) { | ||
const prev = sourceCode.getTokenBefore(operator); | ||
const next = sourceCode.getTokenAfter(operator); | ||
if (!sourceCode.isSpaceBetween(prev, operator) || | ||
!sourceCode.isSpaceBetween(operator, next)) { | ||
report(operator); | ||
} | ||
} | ||
}); | ||
} | ||
/** | ||
* Check if it has an assignment char and report if it's faulty | ||
* @param node The node to report | ||
*/ | ||
function checkForTypeAliasAssignment(node) { | ||
var _a; | ||
checkAndReportAssignmentSpace((_a = node.typeParameters) !== null && _a !== void 0 ? _a : node.id, node.typeAnnotation); | ||
} | ||
function checkForTypeConditional(node) { | ||
checkAndReportAssignmentSpace(node.extendsType, node.trueType); | ||
checkAndReportAssignmentSpace(node.trueType, node.falseType); | ||
} | ||
return Object.assign(Object.assign({}, rules), { TSEnumMember: checkForEnumAssignmentSpace, PropertyDefinition: checkForPropertyDefinitionAssignmentSpace, TSTypeAliasDeclaration: checkForTypeAliasAssignment, TSUnionType: checkForTypeAnnotationSpace, TSIntersectionType: checkForTypeAnnotationSpace, TSConditionalType: checkForTypeConditional }); | ||
}, | ||
}); | ||
//# sourceMappingURL=space-infix-ops.js.map |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,5 +26,5 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const tsutils = __importStar(require("tsutils")); | ||
const ts = __importStar(require("typescript")); | ||
const tsutils = __importStar(require("tsutils")); | ||
const util = __importStar(require("../util")); | ||
@@ -31,5 +35,6 @@ exports.default = util.createRule({ | ||
type: 'suggestion', | ||
fixable: 'code', | ||
hasSuggestions: true, | ||
docs: { | ||
description: 'Restricts the types allowed in boolean expressions', | ||
category: 'Best Practices', | ||
description: 'Disallow certain types in boolean expressions', | ||
recommended: false, | ||
@@ -48,2 +53,3 @@ requiresTypeChecking: true, | ||
allowNullableNumber: { type: 'boolean' }, | ||
allowNullableEnum: { type: 'boolean' }, | ||
allowAny: { type: 'boolean' }, | ||
@@ -78,3 +84,16 @@ allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: { | ||
'An explicit null check is required.', | ||
conditionErrorNullableEnum: 'Unexpected nullable enum value in conditional. ' + | ||
'Please handle the nullish/zero/NaN cases explicitly.', | ||
noStrictNullCheck: 'This rule requires the `strictNullChecks` compiler option to be turned on to function correctly.', | ||
conditionFixDefaultFalse: 'Explicitly treat nullish value the same as false (`value ?? false`)', | ||
conditionFixDefaultEmptyString: 'Explicitly treat nullish value the same as an empty string (`value ?? ""`)', | ||
conditionFixDefaultZero: 'Explicitly treat nullish value the same as 0 (`value ?? 0`)', | ||
conditionFixCompareNullish: 'Change condition to check for null/undefined (`value != null`)', | ||
conditionFixCastBoolean: 'Explicitly cast value to a boolean (`Boolean(value)`)', | ||
conditionFixCompareTrue: 'Change condition to check if true (`value === true`)', | ||
conditionFixCompareFalse: 'Change condition to check if false (`value === false`)', | ||
conditionFixCompareStringLength: "Change condition to check string's length (`value.length !== 0`)", | ||
conditionFixCompareEmptyString: 'Change condition to check for empty string (`value !== ""`)', | ||
conditionFixCompareZero: 'Change condition to check for 0 (`value !== 0`)', | ||
conditionFixCompareNaN: 'Change condition to check for NaN (`!Number.isNaN(value)`)', | ||
}, | ||
@@ -90,2 +109,3 @@ }, | ||
allowNullableNumber: false, | ||
allowNullableEnum: true, | ||
allowAny: false, | ||
@@ -96,5 +116,6 @@ allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: false, | ||
create(context, [options]) { | ||
const service = util.getParserServices(context); | ||
const checker = service.program.getTypeChecker(); | ||
const compilerOptions = service.program.getCompilerOptions(); | ||
const parserServices = util.getParserServices(context); | ||
const typeChecker = parserServices.program.getTypeChecker(); | ||
const compilerOptions = parserServices.program.getCompilerOptions(); | ||
const sourceCode = context.getSourceCode(); | ||
const isStrictNullChecks = tsutils.isStrictCompilerOptionEnabled(compilerOptions, 'strictNullChecks'); | ||
@@ -111,46 +132,75 @@ if (!isStrictNullChecks && | ||
} | ||
const checkedNodes = new Set(); | ||
const traversedNodes = new Set(); | ||
return { | ||
ConditionalExpression: checkTestExpression, | ||
DoWhileStatement: checkTestExpression, | ||
ForStatement: checkTestExpression, | ||
IfStatement: checkTestExpression, | ||
WhileStatement: checkTestExpression, | ||
'LogicalExpression[operator!="??"]': checkNode, | ||
'UnaryExpression[operator="!"]': checkUnaryLogicalExpression, | ||
ConditionalExpression: traverseTestExpression, | ||
DoWhileStatement: traverseTestExpression, | ||
ForStatement: traverseTestExpression, | ||
IfStatement: traverseTestExpression, | ||
WhileStatement: traverseTestExpression, | ||
'LogicalExpression[operator!="??"]': traverseLogicalExpression, | ||
'UnaryExpression[operator="!"]': traverseUnaryLogicalExpression, | ||
}; | ||
function checkTestExpression(node) { | ||
/** | ||
* Inspects condition of a test expression. (`if`, `while`, `for`, etc.) | ||
*/ | ||
function traverseTestExpression(node) { | ||
if (node.test == null) { | ||
return; | ||
} | ||
checkNode(node.test, true); | ||
traverseNode(node.test, true); | ||
} | ||
function checkUnaryLogicalExpression(node) { | ||
checkNode(node.argument, true); | ||
/** | ||
* Inspects the argument of a unary logical expression (`!`). | ||
*/ | ||
function traverseUnaryLogicalExpression(node) { | ||
traverseNode(node.argument, true); | ||
} | ||
/** | ||
* This function analyzes the type of a node and checks if it is allowed in a boolean context. | ||
* It can recurse when checking nested logical operators, so that only the outermost operands are reported. | ||
* The right operand of a logical expression is ignored unless it's a part of a test expression (if/while/ternary/etc). | ||
* @param node The AST node to check. | ||
* @param isTestExpr Whether the node is a descendant of a test expression. | ||
* Inspects the arguments of a logical expression (`&&`, `||`). | ||
* | ||
* If the logical expression is a descendant of a test expression, | ||
* the `isCondition` flag should be set to true. | ||
* Otherwise, if the logical expression is there on it's own, | ||
* it's used for control flow and is not a condition itself. | ||
*/ | ||
function checkNode(node, isTestExpr = false) { | ||
function traverseLogicalExpression(node, isCondition = false) { | ||
// left argument is always treated as a condition | ||
traverseNode(node.left, true); | ||
// if the logical expression is used for control flow, | ||
// then it's right argument is used for it's side effects only | ||
traverseNode(node.right, isCondition); | ||
} | ||
/** | ||
* Inspects any node. | ||
* | ||
* If it's a logical expression then it recursively traverses its arguments. | ||
* If it's any other kind of node then it's type is finally checked against the rule, | ||
* unless `isCondition` flag is set to false, in which case | ||
* it's assumed to be used for side effects only and is skipped. | ||
*/ | ||
function traverseNode(node, isCondition) { | ||
// prevent checking the same node multiple times | ||
if (checkedNodes.has(node)) { | ||
if (traversedNodes.has(node)) { | ||
return; | ||
} | ||
checkedNodes.add(node); | ||
traversedNodes.add(node); | ||
// for logical operator, we check its operands | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.LogicalExpression && | ||
if (node.type === utils_1.AST_NODE_TYPES.LogicalExpression && | ||
node.operator !== '??') { | ||
checkNode(node.left, isTestExpr); | ||
// we ignore the right operand when not in a context of a test expression | ||
if (isTestExpr) { | ||
checkNode(node.right, isTestExpr); | ||
} | ||
traverseLogicalExpression(node, isCondition); | ||
return; | ||
} | ||
const tsNode = service.esTreeNodeToTSNodeMap.get(node); | ||
const type = util.getConstrainedTypeAtLocation(checker, tsNode); | ||
// skip if node is not a condition | ||
if (!isCondition) { | ||
return; | ||
} | ||
checkNode(node); | ||
} | ||
/** | ||
* This function does the actual type check on a node. | ||
* It analyzes the type of a node and checks if it is allowed in a boolean context. | ||
*/ | ||
function checkNode(node) { | ||
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node); | ||
const type = util.getConstrainedTypeAtLocation(typeChecker, tsNode); | ||
const types = inspectVariantTypes(tsutils.unionTypeParts(type)); | ||
@@ -160,3 +210,3 @@ const is = (...wantedTypes) => types.size === wantedTypes.length && | ||
// boolean | ||
if (is('boolean')) { | ||
if (is('boolean') || is('truthy boolean')) { | ||
// boolean is always okay | ||
@@ -176,13 +226,140 @@ return; | ||
} | ||
// Known edge case: boolean `true` and nullish values are always valid boolean expressions | ||
if (is('nullish', 'truthy boolean')) { | ||
return; | ||
} | ||
// nullable boolean | ||
if (is('nullish', 'boolean')) { | ||
if (!options.allowNullableBoolean) { | ||
context.report({ node, messageId: 'conditionErrorNullableBoolean' }); | ||
if (isLogicalNegationExpression(node.parent)) { | ||
// if (!nullableBoolean) | ||
context.report({ | ||
node, | ||
messageId: 'conditionErrorNullableBoolean', | ||
suggest: [ | ||
{ | ||
messageId: 'conditionFixDefaultFalse', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node, | ||
wrap: code => `${code} ?? false`, | ||
}), | ||
}, | ||
{ | ||
messageId: 'conditionFixCompareFalse', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node: node.parent, | ||
innerNode: node, | ||
wrap: code => `${code} === false`, | ||
}), | ||
}, | ||
], | ||
}); | ||
} | ||
else { | ||
// if (nullableBoolean) | ||
context.report({ | ||
node, | ||
messageId: 'conditionErrorNullableBoolean', | ||
suggest: [ | ||
{ | ||
messageId: 'conditionFixDefaultFalse', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node, | ||
wrap: code => `${code} ?? false`, | ||
}), | ||
}, | ||
{ | ||
messageId: 'conditionFixCompareTrue', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node, | ||
wrap: code => `${code} === true`, | ||
}), | ||
}, | ||
], | ||
}); | ||
} | ||
} | ||
return; | ||
} | ||
// Known edge case: truthy primitives and nullish values are always valid boolean expressions | ||
if ((options.allowNumber && is('nullish', 'truthy number')) || | ||
(options.allowString && is('nullish', 'truthy string'))) { | ||
return; | ||
} | ||
// string | ||
if (is('string')) { | ||
if (is('string') || is('truthy string')) { | ||
if (!options.allowString) { | ||
context.report({ node, messageId: 'conditionErrorString' }); | ||
if (isLogicalNegationExpression(node.parent)) { | ||
// if (!string) | ||
context.report({ | ||
node, | ||
messageId: 'conditionErrorString', | ||
suggest: [ | ||
{ | ||
messageId: 'conditionFixCompareStringLength', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node: node.parent, | ||
innerNode: node, | ||
wrap: code => `${code}.length === 0`, | ||
}), | ||
}, | ||
{ | ||
messageId: 'conditionFixCompareEmptyString', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node: node.parent, | ||
innerNode: node, | ||
wrap: code => `${code} === ""`, | ||
}), | ||
}, | ||
{ | ||
messageId: 'conditionFixCastBoolean', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node: node.parent, | ||
innerNode: node, | ||
wrap: code => `!Boolean(${code})`, | ||
}), | ||
}, | ||
], | ||
}); | ||
} | ||
else { | ||
// if (string) | ||
context.report({ | ||
node, | ||
messageId: 'conditionErrorString', | ||
suggest: [ | ||
{ | ||
messageId: 'conditionFixCompareStringLength', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node, | ||
wrap: code => `${code}.length > 0`, | ||
}), | ||
}, | ||
{ | ||
messageId: 'conditionFixCompareEmptyString', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node, | ||
wrap: code => `${code} !== ""`, | ||
}), | ||
}, | ||
{ | ||
messageId: 'conditionFixCastBoolean', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node, | ||
wrap: code => `Boolean(${code})`, | ||
}), | ||
}, | ||
], | ||
}); | ||
} | ||
} | ||
@@ -194,3 +371,70 @@ return; | ||
if (!options.allowNullableString) { | ||
context.report({ node, messageId: 'conditionErrorNullableString' }); | ||
if (isLogicalNegationExpression(node.parent)) { | ||
// if (!nullableString) | ||
context.report({ | ||
node, | ||
messageId: 'conditionErrorNullableString', | ||
suggest: [ | ||
{ | ||
messageId: 'conditionFixCompareNullish', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node: node.parent, | ||
innerNode: node, | ||
wrap: code => `${code} == null`, | ||
}), | ||
}, | ||
{ | ||
messageId: 'conditionFixDefaultEmptyString', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node, | ||
wrap: code => `${code} ?? ""`, | ||
}), | ||
}, | ||
{ | ||
messageId: 'conditionFixCastBoolean', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node: node.parent, | ||
innerNode: node, | ||
wrap: code => `!Boolean(${code})`, | ||
}), | ||
}, | ||
], | ||
}); | ||
} | ||
else { | ||
// if (nullableString) | ||
context.report({ | ||
node, | ||
messageId: 'conditionErrorNullableString', | ||
suggest: [ | ||
{ | ||
messageId: 'conditionFixCompareNullish', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node, | ||
wrap: code => `${code} != null`, | ||
}), | ||
}, | ||
{ | ||
messageId: 'conditionFixDefaultEmptyString', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node, | ||
wrap: code => `${code} ?? ""`, | ||
}), | ||
}, | ||
{ | ||
messageId: 'conditionFixCastBoolean', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node, | ||
wrap: code => `Boolean(${code})`, | ||
}), | ||
}, | ||
], | ||
}); | ||
} | ||
} | ||
@@ -200,5 +444,102 @@ return; | ||
// number | ||
if (is('number')) { | ||
if (is('number') || is('truthy number')) { | ||
if (!options.allowNumber) { | ||
context.report({ node, messageId: 'conditionErrorNumber' }); | ||
if (isArrayLengthExpression(node, typeChecker, parserServices)) { | ||
if (isLogicalNegationExpression(node.parent)) { | ||
// if (!array.length) | ||
context.report({ | ||
node, | ||
messageId: 'conditionErrorNumber', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node: node.parent, | ||
innerNode: node, | ||
wrap: code => `${code} === 0`, | ||
}), | ||
}); | ||
} | ||
else { | ||
// if (array.length) | ||
context.report({ | ||
node, | ||
messageId: 'conditionErrorNumber', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node, | ||
wrap: code => `${code} > 0`, | ||
}), | ||
}); | ||
} | ||
} | ||
else if (isLogicalNegationExpression(node.parent)) { | ||
// if (!number) | ||
context.report({ | ||
node, | ||
messageId: 'conditionErrorNumber', | ||
suggest: [ | ||
{ | ||
messageId: 'conditionFixCompareZero', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node: node.parent, | ||
innerNode: node, | ||
// TODO: we have to compare to 0n if the type is bigint | ||
wrap: code => `${code} === 0`, | ||
}), | ||
}, | ||
{ | ||
// TODO: don't suggest this for bigint because it can't be NaN | ||
messageId: 'conditionFixCompareNaN', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node: node.parent, | ||
innerNode: node, | ||
wrap: code => `Number.isNaN(${code})`, | ||
}), | ||
}, | ||
{ | ||
messageId: 'conditionFixCastBoolean', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node: node.parent, | ||
innerNode: node, | ||
wrap: code => `!Boolean(${code})`, | ||
}), | ||
}, | ||
], | ||
}); | ||
} | ||
else { | ||
// if (number) | ||
context.report({ | ||
node, | ||
messageId: 'conditionErrorNumber', | ||
suggest: [ | ||
{ | ||
messageId: 'conditionFixCompareZero', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node, | ||
wrap: code => `${code} !== 0`, | ||
}), | ||
}, | ||
{ | ||
messageId: 'conditionFixCompareNaN', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node, | ||
wrap: code => `!Number.isNaN(${code})`, | ||
}), | ||
}, | ||
{ | ||
messageId: 'conditionFixCastBoolean', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node, | ||
wrap: code => `Boolean(${code})`, | ||
}), | ||
}, | ||
], | ||
}); | ||
} | ||
} | ||
@@ -210,3 +551,70 @@ return; | ||
if (!options.allowNullableNumber) { | ||
context.report({ node, messageId: 'conditionErrorNullableNumber' }); | ||
if (isLogicalNegationExpression(node.parent)) { | ||
// if (!nullableNumber) | ||
context.report({ | ||
node, | ||
messageId: 'conditionErrorNullableNumber', | ||
suggest: [ | ||
{ | ||
messageId: 'conditionFixCompareNullish', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node: node.parent, | ||
innerNode: node, | ||
wrap: code => `${code} == null`, | ||
}), | ||
}, | ||
{ | ||
messageId: 'conditionFixDefaultZero', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node, | ||
wrap: code => `${code} ?? 0`, | ||
}), | ||
}, | ||
{ | ||
messageId: 'conditionFixCastBoolean', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node: node.parent, | ||
innerNode: node, | ||
wrap: code => `!Boolean(${code})`, | ||
}), | ||
}, | ||
], | ||
}); | ||
} | ||
else { | ||
// if (nullableNumber) | ||
context.report({ | ||
node, | ||
messageId: 'conditionErrorNullableNumber', | ||
suggest: [ | ||
{ | ||
messageId: 'conditionFixCompareNullish', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node, | ||
wrap: code => `${code} != null`, | ||
}), | ||
}, | ||
{ | ||
messageId: 'conditionFixDefaultZero', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node, | ||
wrap: code => `${code} ?? 0`, | ||
}), | ||
}, | ||
{ | ||
messageId: 'conditionFixCastBoolean', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node, | ||
wrap: code => `Boolean(${code})`, | ||
}), | ||
}, | ||
], | ||
}); | ||
} | ||
} | ||
@@ -224,10 +632,84 @@ return; | ||
if (!options.allowNullableObject) { | ||
context.report({ node, messageId: 'conditionErrorNullableObject' }); | ||
if (isLogicalNegationExpression(node.parent)) { | ||
// if (!nullableObject) | ||
context.report({ | ||
node, | ||
messageId: 'conditionErrorNullableObject', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node: node.parent, | ||
innerNode: node, | ||
wrap: code => `${code} == null`, | ||
}), | ||
}); | ||
} | ||
else { | ||
// if (nullableObject) | ||
context.report({ | ||
node, | ||
messageId: 'conditionErrorNullableObject', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node, | ||
wrap: code => `${code} != null`, | ||
}), | ||
}); | ||
} | ||
} | ||
return; | ||
} | ||
// nullable enum | ||
if (is('nullish', 'number', 'enum') || | ||
is('nullish', 'string', 'enum') || | ||
is('nullish', 'truthy number', 'enum') || | ||
is('nullish', 'truthy string', 'enum') || | ||
// mixed enums | ||
is('nullish', 'truthy number', 'truthy string', 'enum') || | ||
is('nullish', 'truthy number', 'string', 'enum') || | ||
is('nullish', 'truthy string', 'number', 'enum') || | ||
is('nullish', 'number', 'string', 'enum')) { | ||
if (!options.allowNullableEnum) { | ||
if (isLogicalNegationExpression(node.parent)) { | ||
context.report({ | ||
node, | ||
messageId: 'conditionErrorNullableEnum', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node: node.parent, | ||
innerNode: node, | ||
wrap: code => `${code} == null`, | ||
}), | ||
}); | ||
} | ||
else { | ||
context.report({ | ||
node, | ||
messageId: 'conditionErrorNullableEnum', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node, | ||
wrap: code => `${code} != null`, | ||
}), | ||
}); | ||
} | ||
} | ||
return; | ||
} | ||
// any | ||
if (is('any')) { | ||
if (!options.allowAny) { | ||
context.report({ node, messageId: 'conditionErrorAny' }); | ||
context.report({ | ||
node, | ||
messageId: 'conditionErrorAny', | ||
suggest: [ | ||
{ | ||
messageId: 'conditionFixCastBoolean', | ||
fix: util.getWrappingFixer({ | ||
sourceCode, | ||
node, | ||
wrap: code => `Boolean(${code})`, | ||
}), | ||
}, | ||
], | ||
}); | ||
} | ||
@@ -247,11 +729,36 @@ return; | ||
} | ||
if (types.some(type => tsutils.isTypeFlagSet(type, ts.TypeFlags.BooleanLike))) { | ||
const booleans = types.filter(type => tsutils.isTypeFlagSet(type, ts.TypeFlags.BooleanLike)); | ||
// If incoming type is either "true" or "false", there will be one type | ||
// object with intrinsicName set accordingly | ||
// If incoming type is boolean, there will be two type objects with | ||
// intrinsicName set "true" and "false" each because of tsutils.unionTypeParts() | ||
if (booleans.length === 1) { | ||
tsutils.isBooleanLiteralType(booleans[0], true) | ||
? variantTypes.add('truthy boolean') | ||
: variantTypes.add('boolean'); | ||
} | ||
else if (booleans.length === 2) { | ||
variantTypes.add('boolean'); | ||
} | ||
if (types.some(type => tsutils.isTypeFlagSet(type, ts.TypeFlags.StringLike))) { | ||
variantTypes.add('string'); | ||
const strings = types.filter(type => tsutils.isTypeFlagSet(type, ts.TypeFlags.StringLike)); | ||
if (strings.length) { | ||
if (strings.every(type => type.isStringLiteral() && type.value !== '')) { | ||
variantTypes.add('truthy string'); | ||
} | ||
else { | ||
variantTypes.add('string'); | ||
} | ||
} | ||
if (types.some(type => tsutils.isTypeFlagSet(type, ts.TypeFlags.NumberLike | ts.TypeFlags.BigIntLike))) { | ||
variantTypes.add('number'); | ||
const numbers = types.filter(type => tsutils.isTypeFlagSet(type, ts.TypeFlags.NumberLike | ts.TypeFlags.BigIntLike)); | ||
if (numbers.length) { | ||
if (numbers.every(type => type.isNumberLiteral() && type.value !== 0)) { | ||
variantTypes.add('truthy number'); | ||
} | ||
else { | ||
variantTypes.add('number'); | ||
} | ||
} | ||
if (types.some(type => tsutils.isTypeFlagSet(type, ts.TypeFlags.EnumLike))) { | ||
variantTypes.add('enum'); | ||
} | ||
if (types.some(type => !tsutils.isTypeFlagSet(type, ts.TypeFlags.Null | | ||
@@ -264,2 +771,3 @@ ts.TypeFlags.Undefined | | ||
ts.TypeFlags.BigIntLike | | ||
ts.TypeFlags.TypeParameter | | ||
ts.TypeFlags.Any | | ||
@@ -270,3 +778,5 @@ ts.TypeFlags.Unknown | | ||
} | ||
if (types.some(type => util.isTypeAnyType(type) || util.isTypeUnknownType(type))) { | ||
if (types.some(type => util.isTypeFlagSet(type, ts.TypeFlags.TypeParameter | | ||
ts.TypeFlags.Any | | ||
ts.TypeFlags.Unknown))) { | ||
variantTypes.add('any'); | ||
@@ -281,2 +791,19 @@ } | ||
}); | ||
function isLogicalNegationExpression(node) { | ||
return node.type === utils_1.AST_NODE_TYPES.UnaryExpression && node.operator === '!'; | ||
} | ||
function isArrayLengthExpression(node, typeChecker, parserServices) { | ||
if (node.type !== utils_1.AST_NODE_TYPES.MemberExpression) { | ||
return false; | ||
} | ||
if (node.computed) { | ||
return false; | ||
} | ||
if (node.property.name !== 'length') { | ||
return false; | ||
} | ||
const objectTsNode = parserServices.esTreeNodeToTSNodeMap.get(node.object); | ||
const objectType = util.getConstrainedTypeAtLocation(typeChecker, objectTsNode); | ||
return util.isTypeArrayTypeOrUnionOfArrayTypes(objectType, typeChecker); | ||
} | ||
//# sourceMappingURL=strict-boolean-expressions.js.map |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,6 +26,6 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const tsutils_1 = require("tsutils"); | ||
const ts = __importStar(require("typescript")); | ||
const util_1 = require("../util"); | ||
const tsutils_1 = require("tsutils"); | ||
exports.default = util_1.createRule({ | ||
exports.default = (0, util_1.createRule)({ | ||
name: 'switch-exhaustiveness-check', | ||
@@ -31,8 +35,7 @@ meta: { | ||
docs: { | ||
description: 'Exhaustiveness checking in switch with union type', | ||
category: 'Best Practices', | ||
description: 'Require switch-case statements to be exhaustive with union type', | ||
recommended: false, | ||
suggestion: true, | ||
requiresTypeChecking: true, | ||
}, | ||
hasSuggestions: true, | ||
schema: [], | ||
@@ -47,3 +50,3 @@ messages: { | ||
const sourceCode = context.getSourceCode(); | ||
const service = util_1.getParserServices(context); | ||
const service = (0, util_1.getParserServices)(context); | ||
const checker = service.program.getTypeChecker(); | ||
@@ -53,3 +56,3 @@ const compilerOptions = service.program.getCompilerOptions(); | ||
const tsNode = service.esTreeNodeToTSNodeMap.get(node); | ||
return util_1.getConstrainedTypeAtLocation(checker, tsNode); | ||
return (0, util_1.getConstrainedTypeAtLocation)(checker, tsNode); | ||
} | ||
@@ -83,3 +86,3 @@ function fixSwitch(fixer, node, missingBranchTypes, symbolName) { | ||
(missingBranchName || missingBranchName === '') && | ||
util_1.requiresQuoting(missingBranchName.toString(), compilerOptions.target)) { | ||
(0, util_1.requiresQuoting)(missingBranchName.toString(), compilerOptions.target)) { | ||
caseTest = `${symbolName}['${missingBranchName}']`; | ||
@@ -106,6 +109,6 @@ } | ||
if (discriminantType.isUnion()) { | ||
const unionTypes = tsutils_1.unionTypeParts(discriminantType); | ||
const unionTypes = (0, tsutils_1.unionTypeParts)(discriminantType); | ||
const caseTypes = new Set(); | ||
for (const switchCase of node.cases) { | ||
if (switchCase.test === null) { | ||
if (switchCase.test == null) { | ||
// Switch has 'default' branch - do nothing. | ||
@@ -128,3 +131,3 @@ return; | ||
var _a; | ||
return tsutils_1.isTypeFlagSet(missingType, ts.TypeFlags.ESSymbolLike) | ||
return (0, tsutils_1.isTypeFlagSet)(missingType, ts.TypeFlags.ESSymbolLike) | ||
? `typeof ${(_a = missingType.getSymbol()) === null || _a === void 0 ? void 0 : _a.escapedName}` | ||
@@ -131,0 +134,0 @@ : checker.typeToString(missingType); |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -30,4 +34,3 @@ exports.default = util.createRule({ | ||
docs: { | ||
description: 'Sets preference level for triple slash directives versus ES6-style import declarations', | ||
category: 'Best Practices', | ||
description: 'Disallow certain triple slash directives in favor of ES6-style import declarations', | ||
recommended: 'error', | ||
@@ -89,3 +92,3 @@ }, | ||
const reference = node.moduleReference; | ||
if (reference.type === experimental_utils_1.AST_NODE_TYPES.TSExternalModuleReference) { | ||
if (reference.type === utils_1.AST_NODE_TYPES.TSExternalModuleReference) { | ||
hasMatchingReference(reference.expression); | ||
@@ -96,3 +99,3 @@ } | ||
Program(node) { | ||
if (lib === 'always' && path === 'always' && types == 'always') { | ||
if (lib === 'always' && path === 'always' && types === 'always') { | ||
return; | ||
@@ -104,3 +107,3 @@ } | ||
commentsBefore.forEach(comment => { | ||
if (comment.type !== experimental_utils_1.AST_TOKEN_TYPES.Line) { | ||
if (comment.type !== utils_1.AST_TOKEN_TYPES.Line) { | ||
return; | ||
@@ -107,0 +110,0 @@ } |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -49,6 +53,6 @@ if (k2 === undefined) k2 = k; | ||
const scope = node === null || node === void 0 ? void 0 : node.parent; | ||
if (util_1.isVariableDeclarator(scope)) { | ||
if ((0, util_1.isVariableDeclarator)(scope)) { | ||
return rules.variable; | ||
} | ||
else if (util_1.isFunctionOrFunctionType(scope)) { | ||
else if ((0, util_1.isFunctionOrFunctionType)(scope)) { | ||
return rules.parameter; | ||
@@ -63,12 +67,12 @@ } | ||
const scope = (_a = node === null || node === void 0 ? void 0 : node.parent) === null || _a === void 0 ? void 0 : _a.parent; | ||
if (util_1.isTSFunctionType(scope) || util_1.isTSConstructorType(scope)) { | ||
if ((0, util_1.isTSFunctionType)(scope) || (0, util_1.isTSConstructorType)(scope)) { | ||
return rules.arrow; | ||
} | ||
else if (util_1.isIdentifier(scope)) { | ||
else if ((0, util_1.isIdentifier)(scope)) { | ||
return getIdentifierRules(rules, scope); | ||
} | ||
else if (util_1.isClassOrTypeElement(scope)) { | ||
else if ((0, util_1.isClassOrTypeElement)(scope)) { | ||
return rules.property; | ||
} | ||
else if (util_1.isFunction(scope)) { | ||
else if ((0, util_1.isFunction)(scope)) { | ||
return rules.returnType; | ||
@@ -86,3 +90,2 @@ } | ||
description: 'Require consistent spacing around type annotations', | ||
category: 'Stylistic Issues', | ||
recommended: false, | ||
@@ -96,2 +99,3 @@ }, | ||
unexpectedSpaceBefore: "Unexpected space before the '{{type}}'.", | ||
unexpectedSpaceBetween: "Unexpected space between the '{{previousToken}}' and the '{{type}}'.", | ||
}, | ||
@@ -145,2 +149,20 @@ schema: [ | ||
if (type === ':' && previousToken.value === '?') { | ||
if ( | ||
// eslint-disable-next-line deprecation/deprecation -- TODO - switch once our min ESLint version is 6.7.0 | ||
sourceCode.isSpaceBetweenTokens(previousToken, punctuatorTokenStart)) { | ||
context.report({ | ||
node: punctuatorTokenStart, | ||
messageId: 'unexpectedSpaceBetween', | ||
data: { | ||
type, | ||
previousToken: previousToken.value, | ||
}, | ||
fix(fixer) { | ||
return fixer.removeRange([ | ||
previousToken.range[1], | ||
punctuatorTokenStart.range[0], | ||
]); | ||
}, | ||
}); | ||
} | ||
// shift the start to the ? | ||
@@ -147,0 +169,0 @@ type = '?:'; |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -29,4 +33,3 @@ exports.default = util.createRule({ | ||
docs: { | ||
description: 'Requires type annotations to exist', | ||
category: 'Stylistic Issues', | ||
description: 'Require type annotations in certain places', | ||
recommended: false, | ||
@@ -42,10 +45,10 @@ }, | ||
properties: { | ||
["arrayDestructuring" /* ArrayDestructuring */]: { type: 'boolean' }, | ||
["arrowParameter" /* ArrowParameter */]: { type: 'boolean' }, | ||
["memberVariableDeclaration" /* MemberVariableDeclaration */]: { type: 'boolean' }, | ||
["objectDestructuring" /* ObjectDestructuring */]: { type: 'boolean' }, | ||
["parameter" /* Parameter */]: { type: 'boolean' }, | ||
["propertyDeclaration" /* PropertyDeclaration */]: { type: 'boolean' }, | ||
["variableDeclaration" /* VariableDeclaration */]: { type: 'boolean' }, | ||
["variableDeclarationIgnoreFunction" /* VariableDeclarationIgnoreFunction */]: { type: 'boolean' }, | ||
["arrayDestructuring" /* OptionKeys.ArrayDestructuring */]: { type: 'boolean' }, | ||
["arrowParameter" /* OptionKeys.ArrowParameter */]: { type: 'boolean' }, | ||
["memberVariableDeclaration" /* OptionKeys.MemberVariableDeclaration */]: { type: 'boolean' }, | ||
["objectDestructuring" /* OptionKeys.ObjectDestructuring */]: { type: 'boolean' }, | ||
["parameter" /* OptionKeys.Parameter */]: { type: 'boolean' }, | ||
["propertyDeclaration" /* OptionKeys.PropertyDeclaration */]: { type: 'boolean' }, | ||
["variableDeclaration" /* OptionKeys.VariableDeclaration */]: { type: 'boolean' }, | ||
["variableDeclarationIgnoreFunction" /* OptionKeys.VariableDeclarationIgnoreFunction */]: { type: 'boolean' }, | ||
}, | ||
@@ -58,13 +61,13 @@ }, | ||
{ | ||
["arrayDestructuring" /* ArrayDestructuring */]: false, | ||
["arrowParameter" /* ArrowParameter */]: false, | ||
["memberVariableDeclaration" /* MemberVariableDeclaration */]: false, | ||
["objectDestructuring" /* ObjectDestructuring */]: false, | ||
["parameter" /* Parameter */]: false, | ||
["propertyDeclaration" /* PropertyDeclaration */]: false, | ||
["variableDeclaration" /* VariableDeclaration */]: false, | ||
["variableDeclarationIgnoreFunction" /* VariableDeclarationIgnoreFunction */]: false, | ||
["arrayDestructuring" /* OptionKeys.ArrayDestructuring */]: false, | ||
["arrowParameter" /* OptionKeys.ArrowParameter */]: false, | ||
["memberVariableDeclaration" /* OptionKeys.MemberVariableDeclaration */]: false, | ||
["objectDestructuring" /* OptionKeys.ObjectDestructuring */]: false, | ||
["parameter" /* OptionKeys.Parameter */]: false, | ||
["propertyDeclaration" /* OptionKeys.PropertyDeclaration */]: false, | ||
["variableDeclaration" /* OptionKeys.VariableDeclaration */]: false, | ||
["variableDeclarationIgnoreFunction" /* OptionKeys.VariableDeclarationIgnoreFunction */]: false, | ||
}, | ||
], | ||
create(context, [options]) { | ||
create(context, [{ arrayDestructuring, arrowParameter, memberVariableDeclaration, objectDestructuring, parameter, propertyDeclaration, variableDeclaration, variableDeclarationIgnoreFunction, },]) { | ||
function report(location, name) { | ||
@@ -78,3 +81,3 @@ context.report({ | ||
function getNodeName(node) { | ||
return node.type === experimental_utils_1.AST_NODE_TYPES.Identifier ? node.name : undefined; | ||
return node.type === utils_1.AST_NODE_TYPES.Identifier ? node.name : undefined; | ||
} | ||
@@ -85,10 +88,10 @@ function isForOfStatementContext(node) { | ||
switch (current.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.VariableDeclarator: | ||
case experimental_utils_1.AST_NODE_TYPES.VariableDeclaration: | ||
case experimental_utils_1.AST_NODE_TYPES.ObjectPattern: | ||
case experimental_utils_1.AST_NODE_TYPES.ArrayPattern: | ||
case experimental_utils_1.AST_NODE_TYPES.Property: | ||
case utils_1.AST_NODE_TYPES.VariableDeclarator: | ||
case utils_1.AST_NODE_TYPES.VariableDeclaration: | ||
case utils_1.AST_NODE_TYPES.ObjectPattern: | ||
case utils_1.AST_NODE_TYPES.ArrayPattern: | ||
case utils_1.AST_NODE_TYPES.Property: | ||
current = current.parent; | ||
break; | ||
case experimental_utils_1.AST_NODE_TYPES.ForOfStatement: | ||
case utils_1.AST_NODE_TYPES.ForOfStatement: | ||
return true; | ||
@@ -105,10 +108,10 @@ default: | ||
switch (param.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.AssignmentPattern: | ||
case utils_1.AST_NODE_TYPES.AssignmentPattern: | ||
annotationNode = param.left; | ||
break; | ||
case experimental_utils_1.AST_NODE_TYPES.TSParameterProperty: | ||
case utils_1.AST_NODE_TYPES.TSParameterProperty: | ||
annotationNode = param.parameter; | ||
// Check TS parameter property with default value like `constructor(private param: string = 'something') {}` | ||
if (annotationNode && | ||
annotationNode.type === experimental_utils_1.AST_NODE_TYPES.AssignmentPattern) { | ||
annotationNode.type === utils_1.AST_NODE_TYPES.AssignmentPattern) { | ||
annotationNode = annotationNode.left; | ||
@@ -127,31 +130,41 @@ } | ||
function isVariableDeclarationIgnoreFunction(node) { | ||
return (!!options["variableDeclarationIgnoreFunction" /* VariableDeclarationIgnoreFunction */] && | ||
(node.type === experimental_utils_1.AST_NODE_TYPES.FunctionExpression || | ||
node.type === experimental_utils_1.AST_NODE_TYPES.ArrowFunctionExpression)); | ||
return (variableDeclarationIgnoreFunction === true && | ||
(node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression || | ||
node.type === utils_1.AST_NODE_TYPES.FunctionExpression)); | ||
} | ||
return { | ||
function isAncestorHasTypeAnnotation(node) { | ||
let ancestor = node.parent; | ||
while (ancestor) { | ||
if ((ancestor.type === utils_1.AST_NODE_TYPES.ObjectPattern || | ||
ancestor.type === utils_1.AST_NODE_TYPES.ArrayPattern) && | ||
ancestor.typeAnnotation) { | ||
return true; | ||
} | ||
ancestor = ancestor.parent; | ||
} | ||
return false; | ||
} | ||
return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (arrayDestructuring && { | ||
ArrayPattern(node) { | ||
var _a; | ||
if (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === experimental_utils_1.AST_NODE_TYPES.RestElement && | ||
var _a, _b; | ||
if (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.RestElement && | ||
node.parent.typeAnnotation) { | ||
return; | ||
} | ||
if (options["arrayDestructuring" /* ArrayDestructuring */] && | ||
!node.typeAnnotation && | ||
!isForOfStatementContext(node)) { | ||
if (!node.typeAnnotation && | ||
!isForOfStatementContext(node) && | ||
!isAncestorHasTypeAnnotation(node) && | ||
((_b = node.parent) === null || _b === void 0 ? void 0 : _b.type) !== utils_1.AST_NODE_TYPES.AssignmentExpression) { | ||
report(node); | ||
} | ||
}, | ||
})), (arrowParameter && { | ||
ArrowFunctionExpression(node) { | ||
if (options["arrowParameter" /* ArrowParameter */]) { | ||
checkParameters(node.params); | ||
} | ||
checkParameters(node.params); | ||
}, | ||
ClassProperty(node) { | ||
if (node.value && isVariableDeclarationIgnoreFunction(node.value)) { | ||
return; | ||
} | ||
if (options["memberVariableDeclaration" /* MemberVariableDeclaration */] && | ||
})), (memberVariableDeclaration && { | ||
PropertyDefinition(node) { | ||
if (!(node.value && isVariableDeclarationIgnoreFunction(node.value)) && | ||
!node.typeAnnotation) { | ||
report(node, node.key.type === experimental_utils_1.AST_NODE_TYPES.Identifier | ||
report(node, node.key.type === utils_1.AST_NODE_TYPES.Identifier | ||
? node.key.name | ||
@@ -161,17 +174,18 @@ : undefined); | ||
}, | ||
})), (parameter && { | ||
'FunctionDeclaration, FunctionExpression'(node) { | ||
if (options["parameter" /* Parameter */]) { | ||
checkParameters(node.params); | ||
} | ||
checkParameters(node.params); | ||
}, | ||
})), (objectDestructuring && { | ||
ObjectPattern(node) { | ||
if (options["objectDestructuring" /* ObjectDestructuring */] && | ||
!node.typeAnnotation && | ||
!isForOfStatementContext(node)) { | ||
if (!node.typeAnnotation && | ||
!isForOfStatementContext(node) && | ||
!isAncestorHasTypeAnnotation(node)) { | ||
report(node); | ||
} | ||
}, | ||
})), (propertyDeclaration && { | ||
'TSIndexSignature, TSPropertySignature'(node) { | ||
if (options["propertyDeclaration" /* PropertyDeclaration */] && !node.typeAnnotation) { | ||
report(node, node.type === experimental_utils_1.AST_NODE_TYPES.TSPropertySignature | ||
if (!node.typeAnnotation) { | ||
report(node, node.type === utils_1.AST_NODE_TYPES.TSPropertySignature | ||
? getNodeName(node.key) | ||
@@ -181,9 +195,9 @@ : undefined); | ||
}, | ||
VariableDeclarator(node) { | ||
if (!options["variableDeclaration" /* VariableDeclaration */] || | ||
})), { VariableDeclarator(node) { | ||
if (!variableDeclaration || | ||
node.id.typeAnnotation || | ||
(node.id.type === experimental_utils_1.AST_NODE_TYPES.ArrayPattern && | ||
!options["arrayDestructuring" /* ArrayDestructuring */]) || | ||
(node.id.type === experimental_utils_1.AST_NODE_TYPES.ObjectPattern && | ||
!options["objectDestructuring" /* ObjectDestructuring */]) || | ||
(node.id.type === utils_1.AST_NODE_TYPES.ArrayPattern && | ||
!arrayDestructuring) || | ||
(node.id.type === utils_1.AST_NODE_TYPES.ObjectPattern && | ||
!objectDestructuring) || | ||
(node.init && isVariableDeclarationIgnoreFunction(node.init))) { | ||
@@ -195,8 +209,8 @@ return; | ||
switch (current.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.VariableDeclaration: | ||
case utils_1.AST_NODE_TYPES.VariableDeclaration: | ||
// Keep looking upwards | ||
current = current.parent; | ||
break; | ||
case experimental_utils_1.AST_NODE_TYPES.ForOfStatement: | ||
case experimental_utils_1.AST_NODE_TYPES.ForInStatement: | ||
case utils_1.AST_NODE_TYPES.ForOfStatement: | ||
case utils_1.AST_NODE_TYPES.ForInStatement: | ||
// Stop traversing and don't report an error | ||
@@ -211,6 +225,5 @@ return; | ||
report(node, getNodeName(node.id)); | ||
}, | ||
}; | ||
} }); | ||
}, | ||
}); | ||
//# sourceMappingURL=typedef.js.map |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,6 +26,7 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const tsutils = __importStar(require("tsutils")); | ||
const ts = __importStar(require("typescript")); | ||
const util = __importStar(require("../util")); | ||
const util_1 = require("../util"); | ||
/** | ||
@@ -113,4 +118,5 @@ * The following is a list of exceptions to the rule | ||
}; | ||
const getNodeName = (node) => node.type === experimental_utils_1.AST_NODE_TYPES.Identifier ? node.name : null; | ||
const getNodeName = (node) => node.type === utils_1.AST_NODE_TYPES.Identifier ? node.name : null; | ||
const getMemberFullName = (node) => `${getNodeName(node.object)}.${getNodeName(node.property)}`; | ||
const BASE_MESSAGE = 'Avoid referencing unbound methods which may cause unintentional scoping of `this`.'; | ||
exports.default = util.createRule({ | ||
@@ -120,4 +126,3 @@ name: 'unbound-method', | ||
docs: { | ||
category: 'Best Practices', | ||
description: 'Enforces unbound methods are called with their expected scope', | ||
description: 'Enforce unbound methods are called with their expected scope', | ||
recommended: 'error', | ||
@@ -127,3 +132,6 @@ requiresTypeChecking: true, | ||
messages: { | ||
unbound: 'Avoid referencing unbound methods which may cause unintentional scoping of `this`.', | ||
unbound: BASE_MESSAGE, | ||
unboundWithoutThisAnnotation: BASE_MESSAGE + | ||
'\n' + | ||
'If your function does not access `this`, you can annotate it with `this: void`, or consider using an arrow function instead.', | ||
}, | ||
@@ -135,2 +143,3 @@ schema: [ | ||
ignoreStatic: { | ||
description: 'Whether to skip checking whether `static` methods are correctly bound.', | ||
type: 'boolean', | ||
@@ -153,2 +162,16 @@ }, | ||
const currentSourceFile = parserServices.program.getSourceFile(context.getFilename()); | ||
function checkMethodAndReport(node, symbol) { | ||
if (!symbol) { | ||
return; | ||
} | ||
const { dangerous, firstParamIsThis } = checkMethod(symbol, ignoreStatic); | ||
if (dangerous) { | ||
context.report({ | ||
messageId: firstParamIsThis === false | ||
? 'unboundWithoutThisAnnotation' | ||
: 'unbound', | ||
node, | ||
}); | ||
} | ||
} | ||
return { | ||
@@ -166,15 +189,9 @@ MemberExpression(node) { | ||
const originalNode = parserServices.esTreeNodeToTSNodeMap.get(node); | ||
const symbol = checker.getSymbolAtLocation(originalNode); | ||
if (symbol && isDangerousMethod(symbol, ignoreStatic)) { | ||
context.report({ | ||
messageId: 'unbound', | ||
node, | ||
}); | ||
} | ||
checkMethodAndReport(node, checker.getSymbolAtLocation(originalNode)); | ||
}, | ||
'VariableDeclarator, AssignmentExpression'(node) { | ||
const [idNode, initNode] = node.type === experimental_utils_1.AST_NODE_TYPES.VariableDeclarator | ||
const [idNode, initNode] = node.type === utils_1.AST_NODE_TYPES.VariableDeclarator | ||
? [node.id, node.init] | ||
: [node.left, node.right]; | ||
if (initNode && idNode.type === experimental_utils_1.AST_NODE_TYPES.ObjectPattern) { | ||
if (initNode && idNode.type === utils_1.AST_NODE_TYPES.ObjectPattern) { | ||
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(initNode); | ||
@@ -185,4 +202,4 @@ const rightSymbol = checker.getSymbolAtLocation(tsNode); | ||
idNode.properties.forEach(property => { | ||
if (property.type === experimental_utils_1.AST_NODE_TYPES.Property && | ||
property.key.type === experimental_utils_1.AST_NODE_TYPES.Identifier) { | ||
if (property.type === utils_1.AST_NODE_TYPES.Property && | ||
property.key.type === utils_1.AST_NODE_TYPES.Identifier) { | ||
if (notImported && | ||
@@ -193,9 +210,3 @@ util.isIdentifier(initNode) && | ||
} | ||
const symbol = initTypes.getProperty(property.key.name); | ||
if (symbol && isDangerousMethod(symbol, ignoreStatic)) { | ||
context.report({ | ||
messageId: 'unbound', | ||
node, | ||
}); | ||
} | ||
checkMethodAndReport(property.key, initTypes.getProperty(property.key.name)); | ||
} | ||
@@ -208,3 +219,3 @@ }); | ||
}); | ||
function isDangerousMethod(symbol, ignoreStatic) { | ||
function checkMethod(symbol, ignoreStatic) { | ||
var _a, _b; | ||
@@ -214,8 +225,10 @@ const { valueDeclaration } = symbol; | ||
// working around https://github.com/microsoft/TypeScript/issues/31294 | ||
return false; | ||
return { dangerous: false }; | ||
} | ||
switch (valueDeclaration.kind) { | ||
case ts.SyntaxKind.PropertyDeclaration: | ||
return (((_a = valueDeclaration.initializer) === null || _a === void 0 ? void 0 : _a.kind) === | ||
ts.SyntaxKind.FunctionExpression); | ||
return { | ||
dangerous: ((_a = valueDeclaration.initializer) === null || _a === void 0 ? void 0 : _a.kind) === | ||
ts.SyntaxKind.FunctionExpression, | ||
}; | ||
case ts.SyntaxKind.MethodDeclaration: | ||
@@ -225,11 +238,15 @@ case ts.SyntaxKind.MethodSignature: { | ||
const firstParam = decl.parameters[0]; | ||
const thisArgIsVoid = (firstParam === null || firstParam === void 0 ? void 0 : firstParam.name.kind) === ts.SyntaxKind.Identifier && | ||
(firstParam === null || firstParam === void 0 ? void 0 : firstParam.name.escapedText) === 'this' && | ||
const firstParamIsThis = (firstParam === null || firstParam === void 0 ? void 0 : firstParam.name.kind) === ts.SyntaxKind.Identifier && | ||
(firstParam === null || firstParam === void 0 ? void 0 : firstParam.name.escapedText) === 'this'; | ||
const thisArgIsVoid = firstParamIsThis && | ||
((_b = firstParam === null || firstParam === void 0 ? void 0 : firstParam.type) === null || _b === void 0 ? void 0 : _b.kind) === ts.SyntaxKind.VoidKeyword; | ||
return (!thisArgIsVoid && | ||
!(ignoreStatic && | ||
tsutils.hasModifier(valueDeclaration.modifiers, ts.SyntaxKind.StaticKeyword))); | ||
return { | ||
dangerous: !thisArgIsVoid && | ||
!(ignoreStatic && | ||
tsutils.hasModifier((0, util_1.getModifiers)(valueDeclaration), ts.SyntaxKind.StaticKeyword)), | ||
firstParamIsThis, | ||
}; | ||
} | ||
} | ||
return false; | ||
return { dangerous: false }; | ||
} | ||
@@ -239,16 +256,16 @@ function isSafeUse(node) { | ||
switch (parent === null || parent === void 0 ? void 0 : parent.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.IfStatement: | ||
case experimental_utils_1.AST_NODE_TYPES.ForStatement: | ||
case experimental_utils_1.AST_NODE_TYPES.MemberExpression: | ||
case experimental_utils_1.AST_NODE_TYPES.SwitchStatement: | ||
case experimental_utils_1.AST_NODE_TYPES.UpdateExpression: | ||
case experimental_utils_1.AST_NODE_TYPES.WhileStatement: | ||
case utils_1.AST_NODE_TYPES.IfStatement: | ||
case utils_1.AST_NODE_TYPES.ForStatement: | ||
case utils_1.AST_NODE_TYPES.MemberExpression: | ||
case utils_1.AST_NODE_TYPES.SwitchStatement: | ||
case utils_1.AST_NODE_TYPES.UpdateExpression: | ||
case utils_1.AST_NODE_TYPES.WhileStatement: | ||
return true; | ||
case experimental_utils_1.AST_NODE_TYPES.CallExpression: | ||
case utils_1.AST_NODE_TYPES.CallExpression: | ||
return parent.callee === node; | ||
case experimental_utils_1.AST_NODE_TYPES.ConditionalExpression: | ||
case utils_1.AST_NODE_TYPES.ConditionalExpression: | ||
return parent.test === node; | ||
case experimental_utils_1.AST_NODE_TYPES.TaggedTemplateExpression: | ||
case utils_1.AST_NODE_TYPES.TaggedTemplateExpression: | ||
return parent.tag === node; | ||
case experimental_utils_1.AST_NODE_TYPES.UnaryExpression: | ||
case utils_1.AST_NODE_TYPES.UnaryExpression: | ||
// the first case is safe for obvious | ||
@@ -258,12 +275,17 @@ // reasons. The second one is also fine | ||
return ['typeof', '!', 'void', 'delete'].includes(parent.operator); | ||
case experimental_utils_1.AST_NODE_TYPES.BinaryExpression: | ||
case utils_1.AST_NODE_TYPES.BinaryExpression: | ||
return ['instanceof', '==', '!=', '===', '!=='].includes(parent.operator); | ||
case experimental_utils_1.AST_NODE_TYPES.AssignmentExpression: | ||
return parent.operator === '=' && node === parent.left; | ||
case experimental_utils_1.AST_NODE_TYPES.ChainExpression: | ||
case experimental_utils_1.AST_NODE_TYPES.TSNonNullExpression: | ||
case experimental_utils_1.AST_NODE_TYPES.TSAsExpression: | ||
case experimental_utils_1.AST_NODE_TYPES.TSTypeAssertion: | ||
case utils_1.AST_NODE_TYPES.AssignmentExpression: | ||
return (parent.operator === '=' && | ||
(node === parent.left || | ||
(node.type === utils_1.AST_NODE_TYPES.MemberExpression && | ||
node.object.type === utils_1.AST_NODE_TYPES.Super && | ||
parent.left.type === utils_1.AST_NODE_TYPES.MemberExpression && | ||
parent.left.object.type === utils_1.AST_NODE_TYPES.ThisExpression))); | ||
case utils_1.AST_NODE_TYPES.ChainExpression: | ||
case utils_1.AST_NODE_TYPES.TSNonNullExpression: | ||
case utils_1.AST_NODE_TYPES.TSAsExpression: | ||
case utils_1.AST_NODE_TYPES.TSTypeAssertion: | ||
return isSafeUse(parent); | ||
case experimental_utils_1.AST_NODE_TYPES.LogicalExpression: | ||
case utils_1.AST_NODE_TYPES.LogicalExpression: | ||
if (parent.operator === '&&' && parent.left === node) { | ||
@@ -270,0 +292,0 @@ // this is safe, as && will return the left if and only if it's falsy |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -22,3 +26,3 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const util = __importStar(require("../util")); | ||
@@ -29,6 +33,5 @@ exports.default = util.createRule({ | ||
docs: { | ||
description: 'Warns for any two overloads that could be unified into one by using a union or an optional/rest parameter', | ||
category: 'Variables', | ||
description: 'Disallow two overloads that could be unified into one with a union or an optional/rest parameter', | ||
// too opinionated to be recommended | ||
recommended: false, | ||
recommended: 'strict', | ||
}, | ||
@@ -41,6 +44,21 @@ type: 'suggestion', | ||
}, | ||
schema: [], | ||
schema: [ | ||
{ | ||
additionalProperties: false, | ||
properties: { | ||
ignoreDifferentlyNamedParameters: { | ||
description: 'Whether two parameters with different names at the same index should be considered different even if their types are the same.', | ||
type: 'boolean', | ||
}, | ||
}, | ||
type: 'object', | ||
}, | ||
], | ||
}, | ||
defaultOptions: [], | ||
create(context) { | ||
defaultOptions: [ | ||
{ | ||
ignoreDifferentlyNamedParameters: false, | ||
}, | ||
], | ||
create(context, [{ ignoreDifferentlyNamedParameters }]) { | ||
const sourceCode = context.getSourceCode(); | ||
@@ -89,3 +107,3 @@ //---------------------------------------------------------------------- | ||
loc: extraParameter.loc, | ||
messageId: extraParameter.type === experimental_utils_1.AST_NODE_TYPES.RestElement | ||
messageId: extraParameter.type === utils_1.AST_NODE_TYPES.RestElement | ||
? 'omittingRestParameter' | ||
@@ -106,20 +124,11 @@ : 'omittingSingleParameter', | ||
for (const overloads of signatures) { | ||
if (overloads.length === 2) { | ||
const signature0 = overloads[0].value || overloads[0]; | ||
const signature1 = overloads[1].value || overloads[1]; | ||
forEachPair(overloads, (a, b) => { | ||
var _a, _b; | ||
const signature0 = (_a = a.value) !== null && _a !== void 0 ? _a : a; | ||
const signature1 = (_b = b.value) !== null && _b !== void 0 ? _b : b; | ||
const unify = compareSignatures(signature0, signature1, isTypeParameter); | ||
if (unify !== undefined) { | ||
result.push({ unify, only2: true }); | ||
result.push({ unify, only2: overloads.length === 2 }); | ||
} | ||
} | ||
else { | ||
forEachPair(overloads, (a, b) => { | ||
const signature0 = a.value || a; | ||
const signature1 = b.value || b; | ||
const unify = compareSignatures(signature0, signature1, isTypeParameter); | ||
if (unify !== undefined) { | ||
result.push({ unify, only2: false }); | ||
} | ||
}); | ||
} | ||
}); | ||
} | ||
@@ -140,2 +149,12 @@ return result; | ||
const bTypeParams = b.typeParameters !== undefined ? b.typeParameters.params : undefined; | ||
if (ignoreDifferentlyNamedParameters) { | ||
const commonParamsLength = Math.min(a.params.length, b.params.length); | ||
for (let i = 0; i < commonParamsLength; i += 1) { | ||
if (a.params[i].type === b.params[i].type && | ||
getStaticParameterName(a.params[i]) !== | ||
getStaticParameterName(b.params[i])) { | ||
return false; | ||
} | ||
} | ||
} | ||
return (typesAreEqual(a.returnType, b.returnType) && | ||
@@ -163,3 +182,3 @@ // Must take the same type parameters. | ||
return parametersHaveEqualSigils(a, b) && | ||
a.type !== experimental_utils_1.AST_NODE_TYPES.RestElement | ||
a.type !== utils_1.AST_NODE_TYPES.RestElement | ||
? { kind: 'single-parameter-difference', p0: a, p1: b } | ||
@@ -201,3 +220,3 @@ : undefined; | ||
if (minLength > 0 && | ||
shorter[minLength - 1].type === experimental_utils_1.AST_NODE_TYPES.RestElement) { | ||
shorter[minLength - 1].type === utils_1.AST_NODE_TYPES.RestElement) { | ||
return undefined; | ||
@@ -231,3 +250,3 @@ } | ||
} | ||
if (type.type === experimental_utils_1.AST_NODE_TYPES.TSTypeReference) { | ||
if (type.type === utils_1.AST_NODE_TYPES.TSTypeReference) { | ||
const typeName = type.typeName; | ||
@@ -244,3 +263,3 @@ if (isIdentifier(typeName) && isTypeParameter(typeName.name)) { | ||
return (node.type === | ||
experimental_utils_1.AST_NODE_TYPES.TSParameterProperty); | ||
utils_1.AST_NODE_TYPES.TSParameterProperty); | ||
} | ||
@@ -262,3 +281,3 @@ function parametersAreEqual(a, b) { | ||
: p.optional; | ||
return p.type === experimental_utils_1.AST_NODE_TYPES.RestElement || optional; | ||
return p.type === utils_1.AST_NODE_TYPES.RestElement || optional; | ||
} | ||
@@ -273,4 +292,4 @@ /** False if one is optional and the other isn't, or one is a rest parameter and the other isn't. */ | ||
: b.optional; | ||
return ((a.type === experimental_utils_1.AST_NODE_TYPES.RestElement) === | ||
(b.type === experimental_utils_1.AST_NODE_TYPES.RestElement) && | ||
return ((a.type === utils_1.AST_NODE_TYPES.RestElement) === | ||
(b.type === utils_1.AST_NODE_TYPES.RestElement) && | ||
(optionalA !== undefined) === (optionalB !== undefined)); | ||
@@ -382,4 +401,4 @@ } | ||
return node.parent && | ||
(node.parent.type === experimental_utils_1.AST_NODE_TYPES.ExportNamedDeclaration || | ||
node.parent.type === experimental_utils_1.AST_NODE_TYPES.ExportDefaultDeclaration) | ||
(node.parent.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration || | ||
node.parent.type === utils_1.AST_NODE_TYPES.ExportDefaultDeclaration) | ||
? node.parent | ||
@@ -396,5 +415,5 @@ : undefined; | ||
switch (node.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.TSConstructSignatureDeclaration: | ||
case utils_1.AST_NODE_TYPES.TSConstructSignatureDeclaration: | ||
return 'constructor'; | ||
case experimental_utils_1.AST_NODE_TYPES.TSCallSignatureDeclaration: | ||
case utils_1.AST_NODE_TYPES.TSCallSignatureDeclaration: | ||
return '()'; | ||
@@ -407,5 +426,15 @@ default: { | ||
} | ||
function getStaticParameterName(param) { | ||
switch (param.type) { | ||
case utils_1.AST_NODE_TYPES.Identifier: | ||
return param.name; | ||
case utils_1.AST_NODE_TYPES.RestElement: | ||
return getStaticParameterName(param.argument); | ||
default: | ||
return undefined; | ||
} | ||
} | ||
function isIdentifier(node) { | ||
return node.type === experimental_utils_1.AST_NODE_TYPES.Identifier; | ||
return node.type === utils_1.AST_NODE_TYPES.Identifier; | ||
} | ||
//# sourceMappingURL=unified-signatures.js.map |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -9,13 +13,23 @@ if (k2 === undefined) k2 = k; | ||
})); | ||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
}) : function(o, v) { | ||
o["default"] = v; | ||
}); | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
}; | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getNameLocationInGlobalDirectiveComment = void 0; | ||
const escapeRegExp_1 = __importDefault(require("lodash/escapeRegExp")); | ||
exports.forEachReturnStatement = exports.getNameLocationInGlobalDirectiveComment = void 0; | ||
const ts = __importStar(require("typescript")); | ||
const escapeRegExp_1 = require("./escapeRegExp"); | ||
// deeply re-export, for convenience | ||
__exportStar(require("@typescript-eslint/experimental-utils/dist/ast-utils"), exports); | ||
__exportStar(require("@typescript-eslint/utils/dist/ast-utils"), exports); | ||
// The following is copied from `eslint`'s source code since it doesn't exist in eslint@5. | ||
@@ -32,3 +46,3 @@ // https://github.com/eslint/eslint/blob/145aec1ab9052fbca96a44d04927c595951b1536/lib/rules/utils/ast-utils.js#L1751-L1779 | ||
function getNameLocationInGlobalDirectiveComment(sourceCode, comment, name) { | ||
const namePattern = new RegExp(`[\\s,]${escapeRegExp_1.default(name)}(?:$|[\\s,:])`, 'gu'); | ||
const namePattern = new RegExp(`[\\s,]${(0, escapeRegExp_1.escapeRegExp)(name)}(?:$|[\\s,:])`, 'gu'); | ||
// To ignore the first text "global". | ||
@@ -47,2 +61,32 @@ namePattern.lastIndex = comment.value.indexOf('global') + 6; | ||
exports.getNameLocationInGlobalDirectiveComment = getNameLocationInGlobalDirectiveComment; | ||
// Copied from typescript https://github.com/microsoft/TypeScript/blob/42b0e3c4630c129ca39ce0df9fff5f0d1b4dd348/src/compiler/utilities.ts#L1335 | ||
// Warning: This has the same semantics as the forEach family of functions, | ||
// in that traversal terminates in the event that 'visitor' supplies a truthy value. | ||
function forEachReturnStatement(body, visitor) { | ||
return traverse(body); | ||
function traverse(node) { | ||
switch (node.kind) { | ||
case ts.SyntaxKind.ReturnStatement: | ||
return visitor(node); | ||
case ts.SyntaxKind.CaseBlock: | ||
case ts.SyntaxKind.Block: | ||
case ts.SyntaxKind.IfStatement: | ||
case ts.SyntaxKind.DoStatement: | ||
case ts.SyntaxKind.WhileStatement: | ||
case ts.SyntaxKind.ForStatement: | ||
case ts.SyntaxKind.ForInStatement: | ||
case ts.SyntaxKind.ForOfStatement: | ||
case ts.SyntaxKind.WithStatement: | ||
case ts.SyntaxKind.SwitchStatement: | ||
case ts.SyntaxKind.CaseClause: | ||
case ts.SyntaxKind.DefaultClause: | ||
case ts.SyntaxKind.LabeledStatement: | ||
case ts.SyntaxKind.TryStatement: | ||
case ts.SyntaxKind.CatchClause: | ||
return ts.forEachChild(node, traverse); | ||
} | ||
return undefined; | ||
} | ||
} | ||
exports.forEachReturnStatement = forEachReturnStatement; | ||
//# sourceMappingURL=astUtils.js.map |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
}) : function(o, v) { | ||
o["default"] = v; | ||
}); | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { | ||
if (kind === "m") throw new TypeError("Private method is not writable"); | ||
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); | ||
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); | ||
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; | ||
}; | ||
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, privateMap, value) { | ||
if (!privateMap.has(receiver)) { | ||
throw new TypeError("attempted to set private field on non-instance"); | ||
} | ||
privateMap.set(receiver, value); | ||
return value; | ||
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { | ||
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); | ||
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); | ||
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); | ||
}; | ||
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, privateMap) { | ||
if (!privateMap.has(receiver)) { | ||
throw new TypeError("attempted to get private field on non-instance"); | ||
} | ||
return privateMap.get(receiver); | ||
}; | ||
var _scopeManager; | ||
var _UnusedVarsVisitor_scopeManager; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.collectUnusedVariables = void 0; | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const scope_manager_1 = require("@typescript-eslint/scope-manager"); | ||
const Visitor_1 = require("@typescript-eslint/scope-manager/dist/referencer/Visitor"); | ||
const util = __importStar(require(".")); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
class UnusedVarsVisitor extends Visitor_1.Visitor { | ||
@@ -47,3 +25,3 @@ // readonly #unusedVariables = new Set<TSESLint.Scope.Variable>(); | ||
}); | ||
_scopeManager.set(this, void 0); | ||
_UnusedVarsVisitor_scopeManager.set(this, void 0); | ||
//#endregion HELPERS | ||
@@ -65,3 +43,3 @@ //#region VISITORS | ||
this.TSMethodSignature = this.visitFunctionTypeSignature; | ||
__classPrivateFieldSet(this, _scopeManager, util.nullThrows(context.getSourceCode().scopeManager, 'Missing required scope manager')); | ||
__classPrivateFieldSet(this, _UnusedVarsVisitor_scopeManager, utils_1.ESLintUtils.nullThrows(context.getSourceCode().scopeManager, 'Missing required scope manager'), "f"); | ||
} | ||
@@ -107,6 +85,6 @@ static collectUnusedVariables(context) { | ||
// On Program node, get the outermost scope to avoid return Node.js special function scope or ES modules scope. | ||
const inner = currentNode.type !== experimental_utils_1.AST_NODE_TYPES.Program; | ||
const inner = currentNode.type !== utils_1.AST_NODE_TYPES.Program; | ||
let node = currentNode; | ||
while (node) { | ||
const scope = __classPrivateFieldGet(this, _scopeManager).acquire(node, inner); | ||
const scope = __classPrivateFieldGet(this, _UnusedVarsVisitor_scopeManager, "f").acquire(node, inner); | ||
if (scope) { | ||
@@ -120,3 +98,3 @@ if (scope.type === 'function-expression-name') { | ||
} | ||
return __classPrivateFieldGet(this, _scopeManager).scopes[0]; | ||
return __classPrivateFieldGet(this, _UnusedVarsVisitor_scopeManager, "f").scopes[0]; | ||
} | ||
@@ -204,4 +182,4 @@ markVariableAsUsed(variableOrIdentifierOrName, parent) { | ||
let idOrVariable; | ||
if (node.left.type === experimental_utils_1.AST_NODE_TYPES.VariableDeclaration) { | ||
const variable = __classPrivateFieldGet(this, _scopeManager).getDeclaredVariables(node.left)[0]; | ||
if (node.left.type === utils_1.AST_NODE_TYPES.VariableDeclaration) { | ||
const variable = __classPrivateFieldGet(this, _UnusedVarsVisitor_scopeManager, "f").getDeclaredVariables(node.left)[0]; | ||
if (!variable) { | ||
@@ -212,3 +190,3 @@ return; | ||
} | ||
if (node.left.type === experimental_utils_1.AST_NODE_TYPES.Identifier) { | ||
if (node.left.type === utils_1.AST_NODE_TYPES.Identifier) { | ||
idOrVariable = node.left; | ||
@@ -220,3 +198,3 @@ } | ||
let body = node.body; | ||
if (node.body.type === experimental_utils_1.AST_NODE_TYPES.BlockStatement) { | ||
if (node.body.type === utils_1.AST_NODE_TYPES.BlockStatement) { | ||
if (node.body.body.length !== 1) { | ||
@@ -227,3 +205,3 @@ return; | ||
} | ||
if (body.type !== experimental_utils_1.AST_NODE_TYPES.ReturnStatement) { | ||
if (body.type !== utils_1.AST_NODE_TYPES.ReturnStatement) { | ||
return; | ||
@@ -235,3 +213,3 @@ } | ||
const scope = this.getScope(node); | ||
if (scope.type === experimental_utils_1.TSESLint.Scope.ScopeType.function && | ||
if (scope.type === utils_1.TSESLint.Scope.ScopeType.function && | ||
node.name === 'this') { | ||
@@ -258,3 +236,3 @@ // this parameters should always be considered used as they're pseudo-parameters | ||
TSModuleDeclaration(node) { | ||
// global augmentation can be in any file, and they do not need exports | ||
// -- global augmentation can be in any file, and they do not need exports | ||
if (node.global === true) { | ||
@@ -267,8 +245,8 @@ this.markVariableAsUsed('global', node.parent); | ||
switch (node.parameter.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.AssignmentPattern: | ||
if (node.parameter.left.type === experimental_utils_1.AST_NODE_TYPES.Identifier) { | ||
case utils_1.AST_NODE_TYPES.AssignmentPattern: | ||
if (node.parameter.left.type === utils_1.AST_NODE_TYPES.Identifier) { | ||
identifier = node.parameter.left; | ||
} | ||
break; | ||
case experimental_utils_1.AST_NODE_TYPES.Identifier: | ||
case utils_1.AST_NODE_TYPES.Identifier: | ||
identifier = node.parameter; | ||
@@ -282,3 +260,3 @@ break; | ||
} | ||
_scopeManager = new WeakMap(); | ||
_UnusedVarsVisitor_scopeManager = new WeakMap(); | ||
UnusedVarsVisitor.RESULTS_CACHE = new WeakMap(); | ||
@@ -313,7 +291,7 @@ //#region private helpers | ||
const MERGABLE_TYPES = new Set([ | ||
experimental_utils_1.AST_NODE_TYPES.TSInterfaceDeclaration, | ||
experimental_utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration, | ||
experimental_utils_1.AST_NODE_TYPES.TSModuleDeclaration, | ||
experimental_utils_1.AST_NODE_TYPES.ClassDeclaration, | ||
experimental_utils_1.AST_NODE_TYPES.FunctionDeclaration, | ||
utils_1.AST_NODE_TYPES.TSInterfaceDeclaration, | ||
utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration, | ||
utils_1.AST_NODE_TYPES.TSModuleDeclaration, | ||
utils_1.AST_NODE_TYPES.ClassDeclaration, | ||
utils_1.AST_NODE_TYPES.FunctionDeclaration, | ||
]); | ||
@@ -332,8 +310,8 @@ /** | ||
// so we need to special case them | ||
if (def.type === experimental_utils_1.TSESLint.Scope.DefinitionType.Parameter) { | ||
if (def.type === utils_1.TSESLint.Scope.DefinitionType.Parameter) { | ||
continue; | ||
} | ||
if ((MERGABLE_TYPES.has(def.node.type) && | ||
((_a = def.node.parent) === null || _a === void 0 ? void 0 : _a.type) === experimental_utils_1.AST_NODE_TYPES.ExportNamedDeclaration) || | ||
((_b = def.node.parent) === null || _b === void 0 ? void 0 : _b.type) === experimental_utils_1.AST_NODE_TYPES.ExportDefaultDeclaration) { | ||
((_a = def.node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.ExportNamedDeclaration) || | ||
((_b = def.node.parent) === null || _b === void 0 ? void 0 : _b.type) === utils_1.AST_NODE_TYPES.ExportDefaultDeclaration) { | ||
return true; | ||
@@ -353,6 +331,6 @@ } | ||
let node = definition.node; | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.VariableDeclarator) { | ||
if (node.type === utils_1.AST_NODE_TYPES.VariableDeclarator) { | ||
node = node.parent; | ||
} | ||
else if (definition.type === experimental_utils_1.TSESLint.Scope.DefinitionType.Parameter) { | ||
else if (definition.type === utils_1.TSESLint.Scope.DefinitionType.Parameter) { | ||
return false; | ||
@@ -380,9 +358,9 @@ } | ||
// FunctionDeclarations | ||
if (def.type === experimental_utils_1.TSESLint.Scope.DefinitionType.FunctionName) { | ||
if (def.type === utils_1.TSESLint.Scope.DefinitionType.FunctionName) { | ||
functionDefinitions.add(def.node); | ||
} | ||
// FunctionExpressions | ||
if (def.type === experimental_utils_1.TSESLint.Scope.DefinitionType.Variable && | ||
(((_a = def.node.init) === null || _a === void 0 ? void 0 : _a.type) === experimental_utils_1.AST_NODE_TYPES.FunctionExpression || | ||
((_b = def.node.init) === null || _b === void 0 ? void 0 : _b.type) === experimental_utils_1.AST_NODE_TYPES.ArrowFunctionExpression)) { | ||
if (def.type === utils_1.TSESLint.Scope.DefinitionType.Variable && | ||
(((_a = def.node.init) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.FunctionExpression || | ||
((_b = def.node.init) === null || _b === void 0 ? void 0 : _b.type) === utils_1.AST_NODE_TYPES.ArrowFunctionExpression)) { | ||
functionDefinitions.add(def.node.init); | ||
@@ -396,4 +374,4 @@ } | ||
variable.defs.forEach(def => { | ||
if (def.node.type === experimental_utils_1.AST_NODE_TYPES.TSInterfaceDeclaration || | ||
def.node.type === experimental_utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration) { | ||
if (def.node.type === utils_1.AST_NODE_TYPES.TSInterfaceDeclaration || | ||
def.node.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration) { | ||
nodes.add(def.node); | ||
@@ -407,3 +385,3 @@ } | ||
variable.defs.forEach(def => { | ||
if (def.node.type === experimental_utils_1.AST_NODE_TYPES.TSModuleDeclaration) { | ||
if (def.node.type === utils_1.AST_NODE_TYPES.TSModuleDeclaration) { | ||
nodes.add(def.node); | ||
@@ -448,6 +426,6 @@ } | ||
while (currentNode) { | ||
if (util.isFunction(currentNode)) { | ||
if (utils_1.ASTUtils.isFunction(currentNode)) { | ||
break; | ||
} | ||
if (util.isLoop(currentNode)) { | ||
if (utils_1.ASTUtils.isLoop(currentNode)) { | ||
return true; | ||
@@ -472,4 +450,4 @@ } | ||
} | ||
if (parent.type === experimental_utils_1.AST_NODE_TYPES.AssignmentExpression && | ||
grandparent.type === experimental_utils_1.AST_NODE_TYPES.ExpressionStatement && | ||
if (parent.type === utils_1.AST_NODE_TYPES.AssignmentExpression && | ||
grandparent.type === utils_1.AST_NODE_TYPES.ExpressionStatement && | ||
id === parent.left && | ||
@@ -510,3 +488,3 @@ !canBeUsedLater) { | ||
while (currentNode) { | ||
if (util.isFunction(currentNode)) { | ||
if (utils_1.ASTUtils.isFunction(currentNode)) { | ||
return currentNode; | ||
@@ -533,3 +511,3 @@ } | ||
switch (parent.type) { | ||
case experimental_utils_1.AST_NODE_TYPES.SequenceExpression: | ||
case utils_1.AST_NODE_TYPES.SequenceExpression: | ||
if (parent.expressions[parent.expressions.length - 1] !== node) { | ||
@@ -539,8 +517,8 @@ return false; | ||
break; | ||
case experimental_utils_1.AST_NODE_TYPES.CallExpression: | ||
case experimental_utils_1.AST_NODE_TYPES.NewExpression: | ||
case utils_1.AST_NODE_TYPES.CallExpression: | ||
case utils_1.AST_NODE_TYPES.NewExpression: | ||
return parent.callee !== node; | ||
case experimental_utils_1.AST_NODE_TYPES.AssignmentExpression: | ||
case experimental_utils_1.AST_NODE_TYPES.TaggedTemplateExpression: | ||
case experimental_utils_1.AST_NODE_TYPES.YieldExpression: | ||
case utils_1.AST_NODE_TYPES.AssignmentExpression: | ||
case utils_1.AST_NODE_TYPES.TaggedTemplateExpression: | ||
case utils_1.AST_NODE_TYPES.YieldExpression: | ||
return true; | ||
@@ -572,7 +550,7 @@ default: | ||
// self update. e.g. `a += 1`, `a++` | ||
((parent.type === experimental_utils_1.AST_NODE_TYPES.AssignmentExpression && | ||
grandparent.type === experimental_utils_1.AST_NODE_TYPES.ExpressionStatement && | ||
((parent.type === utils_1.AST_NODE_TYPES.AssignmentExpression && | ||
grandparent.type === utils_1.AST_NODE_TYPES.ExpressionStatement && | ||
parent.left === id) || | ||
(parent.type === experimental_utils_1.AST_NODE_TYPES.UpdateExpression && | ||
grandparent.type === experimental_utils_1.AST_NODE_TYPES.ExpressionStatement) || | ||
(parent.type === utils_1.AST_NODE_TYPES.UpdateExpression && | ||
grandparent.type === utils_1.AST_NODE_TYPES.ExpressionStatement) || | ||
(!!rhsNode && | ||
@@ -579,0 +557,0 @@ isInside(id, rhsNode) && |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.createRule = void 0; | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
// 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; | ||
exports.createRule = experimental_utils_1.ESLintUtils.RuleCreator(name => `https://github.com/typescript-eslint/typescript-eslint/blob/v${version}/packages/eslint-plugin/docs/rules/${name}.md`); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
exports.createRule = utils_1.ESLintUtils.RuleCreator(name => `https://typescript-eslint.io/rules/${name}`); | ||
//# sourceMappingURL=createRule.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.isTypedFunctionExpression = exports.doesImmediatelyReturnFunctionExpression = exports.checkFunctionReturnType = exports.checkFunctionExpressionReturnType = void 0; | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
exports.ancestorHasReturnType = exports.isValidFunctionExpressionReturnType = exports.isTypedFunctionExpression = exports.doesImmediatelyReturnFunctionExpression = exports.checkFunctionReturnType = exports.checkFunctionExpressionReturnType = void 0; | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const astUtils_1 = require("./astUtils"); | ||
const nullThrows_1 = require("./nullThrows"); | ||
const getFunctionHeadLoc_1 = require("./getFunctionHeadLoc"); | ||
/** | ||
* Creates a report location for the given function. | ||
* The location only encompasses the "start" of the function, and not the body | ||
* | ||
* eg. | ||
* function foo(args) {} | ||
* ^^^^^^^^^^^^^^^^^^ | ||
* | ||
* get y(args) {} | ||
* ^^^^^^^^^^^ | ||
* | ||
* const x = (args) => {} | ||
* ^^^^^^^^^ | ||
*/ | ||
function getReporLoc(node, sourceCode) { | ||
/** | ||
* Returns start column position | ||
* @param node | ||
*/ | ||
function getLocStart() { | ||
/* highlight method name */ | ||
const parent = node.parent; | ||
if (parent && | ||
(parent.type === experimental_utils_1.AST_NODE_TYPES.MethodDefinition || | ||
(parent.type === experimental_utils_1.AST_NODE_TYPES.Property && parent.method))) { | ||
return parent.loc.start; | ||
} | ||
return node.loc.start; | ||
} | ||
/** | ||
* Returns end column position | ||
* @param node | ||
*/ | ||
function getLocEnd() { | ||
/* highlight `=>` */ | ||
if (node.type === experimental_utils_1.AST_NODE_TYPES.ArrowFunctionExpression) { | ||
return sourceCode.getTokenBefore(node.body, token => token.type === experimental_utils_1.AST_TOKEN_TYPES.Punctuator && token.value === '=>').loc.end; | ||
} | ||
return sourceCode.getTokenBefore(node.body).loc.end; | ||
} | ||
return { | ||
start: getLocStart(), | ||
end: getLocEnd(), | ||
}; | ||
} | ||
/** | ||
* Checks if a node is a variable declarator with a type annotation. | ||
@@ -59,3 +14,3 @@ * ``` | ||
function isVariableDeclaratorWithTypeAnnotation(node) { | ||
return (node.type === experimental_utils_1.AST_NODE_TYPES.VariableDeclarator && !!node.id.typeAnnotation); | ||
return (node.type === utils_1.AST_NODE_TYPES.VariableDeclarator && !!node.id.typeAnnotation); | ||
} | ||
@@ -68,4 +23,4 @@ /** | ||
*/ | ||
function isClassPropertyWithTypeAnnotation(node) { | ||
return node.type === experimental_utils_1.AST_NODE_TYPES.ClassProperty && !!node.typeAnnotation; | ||
function isPropertyDefinitionWithTypeAnnotation(node) { | ||
return (node.type === utils_1.AST_NODE_TYPES.PropertyDefinition && !!node.typeAnnotation); | ||
} | ||
@@ -80,6 +35,6 @@ /** | ||
function isConstructorArgument(node) { | ||
return node.type === experimental_utils_1.AST_NODE_TYPES.NewExpression; | ||
return node.type === utils_1.AST_NODE_TYPES.NewExpression; | ||
} | ||
/** | ||
* Checks if a node belongs to: | ||
* Checks if a node is a property or a nested property of a typed object: | ||
* ``` | ||
@@ -89,6 +44,7 @@ * const x: Foo = { prop: () => {} } | ||
* const x = <Foo>{ prop: () => {} } | ||
* const x: Foo = { bar: { prop: () => {} } } | ||
* ``` | ||
*/ | ||
function isPropertyOfObjectWithType(property) { | ||
if (!property || property.type !== experimental_utils_1.AST_NODE_TYPES.Property) { | ||
if (!property || property.type !== utils_1.AST_NODE_TYPES.Property) { | ||
return false; | ||
@@ -98,3 +54,3 @@ } | ||
/* istanbul ignore if */ if (!objectExpr || | ||
objectExpr.type !== experimental_utils_1.AST_NODE_TYPES.ObjectExpression) { | ||
objectExpr.type !== utils_1.AST_NODE_TYPES.ObjectExpression) { | ||
return false; | ||
@@ -106,6 +62,7 @@ } | ||
} | ||
return (astUtils_1.isTypeAssertion(parent) || | ||
isClassPropertyWithTypeAnnotation(parent) || | ||
return ((0, astUtils_1.isTypeAssertion)(parent) || | ||
isPropertyDefinitionWithTypeAnnotation(parent) || | ||
isVariableDeclaratorWithTypeAnnotation(parent) || | ||
isFunctionArgument(parent)); | ||
isFunctionArgument(parent) || | ||
isPropertyOfObjectWithType(parent)); | ||
} | ||
@@ -129,6 +86,6 @@ /** | ||
// Check if body is a block with a single statement | ||
if (body.type === experimental_utils_1.AST_NODE_TYPES.BlockStatement && body.body.length === 1) { | ||
if (body.type === utils_1.AST_NODE_TYPES.BlockStatement && body.body.length === 1) { | ||
const [statement] = body.body; | ||
// Check if that statement is a return statement with an argument | ||
if (statement.type === experimental_utils_1.AST_NODE_TYPES.ReturnStatement && | ||
if (statement.type === utils_1.AST_NODE_TYPES.ReturnStatement && | ||
!!statement.argument) { | ||
@@ -140,4 +97,4 @@ // If so, check that returned argument as body | ||
// Check if the body being returned is a function expression | ||
return (body.type === experimental_utils_1.AST_NODE_TYPES.ArrowFunctionExpression || | ||
body.type === experimental_utils_1.AST_NODE_TYPES.FunctionExpression); | ||
return (body.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression || | ||
body.type === utils_1.AST_NODE_TYPES.FunctionExpression); | ||
} | ||
@@ -152,3 +109,3 @@ exports.doesImmediatelyReturnFunctionExpression = doesImmediatelyReturnFunctionExpression; | ||
function isFunctionArgument(parent, callee) { | ||
return (parent.type === experimental_utils_1.AST_NODE_TYPES.CallExpression && | ||
return (parent.type === utils_1.AST_NODE_TYPES.CallExpression && | ||
// make sure this isn't an IIFE | ||
@@ -165,7 +122,7 @@ parent.callee !== callee); | ||
const { body } = node; | ||
if (astUtils_1.isTypeAssertion(body)) { | ||
if ((0, astUtils_1.isTypeAssertion)(body)) { | ||
const { typeAnnotation } = body; | ||
if (typeAnnotation.type === experimental_utils_1.AST_NODE_TYPES.TSTypeReference) { | ||
if (typeAnnotation.type === utils_1.AST_NODE_TYPES.TSTypeReference) { | ||
const { typeName } = typeAnnotation; | ||
if (typeName.type === experimental_utils_1.AST_NODE_TYPES.Identifier && | ||
if (typeName.type === utils_1.AST_NODE_TYPES.Identifier && | ||
typeName.name === 'const') { | ||
@@ -182,9 +139,9 @@ return true; | ||
function isTypedFunctionExpression(node, options) { | ||
const parent = nullThrows_1.nullThrows(node.parent, nullThrows_1.NullThrowsReasons.MissingParent); | ||
const parent = utils_1.ESLintUtils.nullThrows(node.parent, utils_1.ESLintUtils.NullThrowsReasons.MissingParent); | ||
if (!options.allowTypedFunctionExpressions) { | ||
return false; | ||
} | ||
return (astUtils_1.isTypeAssertion(parent) || | ||
return ((0, astUtils_1.isTypeAssertion)(parent) || | ||
isVariableDeclaratorWithTypeAnnotation(parent) || | ||
isClassPropertyWithTypeAnnotation(parent) || | ||
isPropertyDefinitionWithTypeAnnotation(parent) || | ||
isPropertyOfObjectWithType(parent) || | ||
@@ -203,18 +160,16 @@ isFunctionArgument(parent, node) || | ||
} | ||
const parent = nullThrows_1.nullThrows(node.parent, nullThrows_1.NullThrowsReasons.MissingParent); | ||
const parent = utils_1.ESLintUtils.nullThrows(node.parent, utils_1.ESLintUtils.NullThrowsReasons.MissingParent); | ||
if (options.allowExpressions && | ||
parent.type !== experimental_utils_1.AST_NODE_TYPES.VariableDeclarator && | ||
parent.type !== experimental_utils_1.AST_NODE_TYPES.MethodDefinition && | ||
parent.type !== experimental_utils_1.AST_NODE_TYPES.ExportDefaultDeclaration && | ||
parent.type !== experimental_utils_1.AST_NODE_TYPES.ClassProperty) { | ||
parent.type !== utils_1.AST_NODE_TYPES.VariableDeclarator && | ||
parent.type !== utils_1.AST_NODE_TYPES.MethodDefinition && | ||
parent.type !== utils_1.AST_NODE_TYPES.ExportDefaultDeclaration && | ||
parent.type !== utils_1.AST_NODE_TYPES.PropertyDefinition) { | ||
return true; | ||
} | ||
// https://github.com/typescript-eslint/typescript-eslint/issues/653 | ||
if (options.allowDirectConstAssertionInArrowFunctions && | ||
node.type === experimental_utils_1.AST_NODE_TYPES.ArrowFunctionExpression && | ||
returnsConstAssertionDirectly(node)) { | ||
return true; | ||
} | ||
return false; | ||
return (options.allowDirectConstAssertionInArrowFunctions === true && | ||
node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression && | ||
returnsConstAssertionDirectly(node)); | ||
} | ||
exports.isValidFunctionExpressionReturnType = isValidFunctionExpressionReturnType; | ||
/** | ||
@@ -228,6 +183,5 @@ * Check that the function expression or declaration is valid. | ||
} | ||
if (node.returnType || astUtils_1.isConstructor(node.parent) || astUtils_1.isSetter(node.parent)) { | ||
return true; | ||
} | ||
return false; | ||
return (node.returnType != null || | ||
(0, astUtils_1.isConstructor)(node.parent) || | ||
(0, astUtils_1.isSetter)(node.parent)); | ||
} | ||
@@ -241,3 +195,3 @@ /** | ||
} | ||
report(getReporLoc(node, sourceCode)); | ||
report((0, getFunctionHeadLoc_1.getFunctionHeadLoc)(node, sourceCode)); | ||
} | ||
@@ -255,2 +209,39 @@ exports.checkFunctionReturnType = checkFunctionReturnType; | ||
exports.checkFunctionExpressionReturnType = checkFunctionExpressionReturnType; | ||
/** | ||
* Check whether any ancestor of the provided function has a valid return type. | ||
*/ | ||
function ancestorHasReturnType(node) { | ||
let ancestor = node.parent; | ||
if ((ancestor === null || ancestor === void 0 ? void 0 : ancestor.type) === utils_1.AST_NODE_TYPES.Property) { | ||
ancestor = ancestor.value; | ||
} | ||
// if the ancestor is not a return, then this function was not returned at all, so we can exit early | ||
const isReturnStatement = (ancestor === null || ancestor === void 0 ? void 0 : ancestor.type) === utils_1.AST_NODE_TYPES.ReturnStatement; | ||
const isBodylessArrow = (ancestor === null || ancestor === void 0 ? void 0 : ancestor.type) === utils_1.AST_NODE_TYPES.ArrowFunctionExpression && | ||
ancestor.body.type !== utils_1.AST_NODE_TYPES.BlockStatement; | ||
if (!isReturnStatement && !isBodylessArrow) { | ||
return false; | ||
} | ||
while (ancestor) { | ||
switch (ancestor.type) { | ||
case utils_1.AST_NODE_TYPES.ArrowFunctionExpression: | ||
case utils_1.AST_NODE_TYPES.FunctionExpression: | ||
case utils_1.AST_NODE_TYPES.FunctionDeclaration: | ||
if (ancestor.returnType) { | ||
return true; | ||
} | ||
break; | ||
// const x: Foo = () => {}; | ||
// Assume that a typed variable types the function expression | ||
case utils_1.AST_NODE_TYPES.VariableDeclarator: | ||
if (ancestor.id.typeAnnotation) { | ||
return true; | ||
} | ||
break; | ||
} | ||
ancestor = ancestor.parent; | ||
} | ||
return false; | ||
} | ||
exports.ancestorHasReturnType = ancestorHasReturnType; | ||
//# sourceMappingURL=explicitReturnTypeUtils.js.map |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -13,16 +17,20 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getParserServices = exports.isObjectNotArray = exports.deepMerge = exports.applyDefault = void 0; | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
exports.NullThrowsReasons = exports.nullThrows = exports.getParserServices = exports.isObjectNotArray = exports.deepMerge = exports.applyDefault = void 0; | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
__exportStar(require("./astUtils"), exports); | ||
__exportStar(require("./collectUnusedVariables"), exports); | ||
__exportStar(require("./createRule"), exports); | ||
__exportStar(require("./isTypeReadonly"), exports); | ||
__exportStar(require("./getFunctionHeadLoc"), exports); | ||
__exportStar(require("./getOperatorPrecedence"), exports); | ||
__exportStar(require("./getStringLength"), exports); | ||
__exportStar(require("./getThisExpression"), exports); | ||
__exportStar(require("./getWrappingFixer"), exports); | ||
__exportStar(require("./isNodeEqual"), exports); | ||
__exportStar(require("./isNullLiteral"), exports); | ||
__exportStar(require("./isUndefinedIdentifier"), exports); | ||
__exportStar(require("./misc"), exports); | ||
__exportStar(require("./nullThrows"), exports); | ||
__exportStar(require("./objectIterators"), exports); | ||
__exportStar(require("./propertyTypes"), exports); | ||
__exportStar(require("./requiresQuoting"), exports); | ||
__exportStar(require("./types"), exports); | ||
// this is done for convenience - saves migrating all of the old rules | ||
const { applyDefault, deepMerge, isObjectNotArray, getParserServices, } = experimental_utils_1.ESLintUtils; | ||
__exportStar(require("@typescript-eslint/type-utils"), exports); | ||
const { applyDefault, deepMerge, isObjectNotArray, getParserServices, nullThrows, NullThrowsReasons, } = utils_1.ESLintUtils; | ||
exports.applyDefault = applyDefault; | ||
@@ -32,2 +40,4 @@ exports.deepMerge = deepMerge; | ||
exports.getParserServices = getParserServices; | ||
exports.nullThrows = nullThrows; | ||
exports.NullThrowsReasons = NullThrowsReasons; | ||
//# sourceMappingURL=index.js.map |
@@ -5,5 +5,35 @@ "use strict"; | ||
*/ | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
}) : function(o, v) { | ||
o["default"] = v; | ||
}); | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.upperCaseFirst = exports.isDefinitionFile = exports.getNameFromMember = exports.getNameFromIndexSignature = exports.getEnumNames = exports.findFirstResult = exports.arraysAreEqual = void 0; | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
exports.findLastIndex = exports.upperCaseFirst = exports.typeNodeRequiresParentheses = exports.MemberNameType = exports.isDefinitionFile = exports.getNameFromMember = exports.getNameFromIndexSignature = exports.getEnumNames = exports.formatWordList = exports.findFirstResult = exports.arraysAreEqual = exports.arrayGroupByToMap = void 0; | ||
const type_utils_1 = require("@typescript-eslint/type-utils"); | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const ts = __importStar(require("typescript")); | ||
const DEFINITION_EXTENSIONS = [ | ||
ts.Extension.Dts, | ||
ts.Extension.Dcts, | ||
ts.Extension.Dmts, | ||
]; | ||
/** | ||
@@ -13,3 +43,9 @@ * Check if the context file name is *.d.ts or *.d.tsx | ||
function isDefinitionFile(fileName) { | ||
return /\.d\.tsx?$/i.test(fileName || ''); | ||
const lowerFileName = fileName.toLowerCase(); | ||
for (const definitionExt of DEFINITION_EXTENSIONS) { | ||
if (lowerFileName.endsWith(definitionExt)) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
@@ -24,2 +60,17 @@ exports.isDefinitionFile = isDefinitionFile; | ||
exports.upperCaseFirst = upperCaseFirst; | ||
function arrayGroupByToMap(array, getKey) { | ||
const groups = new Map(); | ||
for (const item of array) { | ||
const key = getKey(item); | ||
const existing = groups.get(key); | ||
if (existing) { | ||
existing.push(item); | ||
} | ||
else { | ||
groups.set(key, [item]); | ||
} | ||
} | ||
return groups; | ||
} | ||
exports.arrayGroupByToMap = arrayGroupByToMap; | ||
function arraysAreEqual(a, b, eq) { | ||
@@ -48,18 +99,49 @@ return (a === b || | ||
function getNameFromIndexSignature(node) { | ||
const propName = node.parameters.find((parameter) => parameter.type === experimental_utils_1.AST_NODE_TYPES.Identifier); | ||
const propName = node.parameters.find((parameter) => parameter.type === utils_1.AST_NODE_TYPES.Identifier); | ||
return propName ? propName.name : '(index signature)'; | ||
} | ||
exports.getNameFromIndexSignature = getNameFromIndexSignature; | ||
var MemberNameType; | ||
(function (MemberNameType) { | ||
MemberNameType[MemberNameType["Private"] = 1] = "Private"; | ||
MemberNameType[MemberNameType["Quoted"] = 2] = "Quoted"; | ||
MemberNameType[MemberNameType["Normal"] = 3] = "Normal"; | ||
MemberNameType[MemberNameType["Expression"] = 4] = "Expression"; | ||
})(MemberNameType || (exports.MemberNameType = MemberNameType = {})); | ||
/** | ||
* Gets a string name representation of the name of the given MethodDefinition | ||
* or ClassProperty node, with handling for computed property names. | ||
* or PropertyDefinition node, with handling for computed property names. | ||
*/ | ||
function getNameFromMember(member, sourceCode) { | ||
if (member.key.type === experimental_utils_1.AST_NODE_TYPES.Identifier) { | ||
return member.key.name; | ||
if (member.key.type === utils_1.AST_NODE_TYPES.Identifier) { | ||
return { | ||
type: MemberNameType.Normal, | ||
name: member.key.name, | ||
}; | ||
} | ||
if (member.key.type === experimental_utils_1.AST_NODE_TYPES.Literal) { | ||
return `${member.key.value}`; | ||
if (member.key.type === utils_1.AST_NODE_TYPES.PrivateIdentifier) { | ||
return { | ||
type: MemberNameType.Private, | ||
name: `#${member.key.name}`, | ||
}; | ||
} | ||
return sourceCode.text.slice(...member.key.range); | ||
if (member.key.type === utils_1.AST_NODE_TYPES.Literal) { | ||
const name = `${member.key.value}`; | ||
if ((0, type_utils_1.requiresQuoting)(name)) { | ||
return { | ||
type: MemberNameType.Quoted, | ||
name: `"${name}"`, | ||
}; | ||
} | ||
else { | ||
return { | ||
type: MemberNameType.Normal, | ||
name, | ||
}; | ||
} | ||
} | ||
return { | ||
type: MemberNameType.Expression, | ||
name: sourceCode.text.slice(...member.key.range), | ||
}; | ||
} | ||
@@ -71,2 +153,44 @@ exports.getNameFromMember = getNameFromMember; | ||
exports.getEnumNames = getEnumNames; | ||
/** | ||
* Given an array of words, returns an English-friendly concatenation, separated with commas, with | ||
* the `and` clause inserted before the last item. | ||
* | ||
* Example: ['foo', 'bar', 'baz' ] returns the string "foo, bar, and baz". | ||
*/ | ||
function formatWordList(words) { | ||
if (!(words === null || words === void 0 ? void 0 : words.length)) { | ||
return ''; | ||
} | ||
if (words.length === 1) { | ||
return words[0]; | ||
} | ||
return [words.slice(0, -1).join(', '), words.slice(-1)[0]].join(' and '); | ||
} | ||
exports.formatWordList = formatWordList; | ||
/** | ||
* Iterates the array in reverse and returns the index of the first element it | ||
* finds which passes the predicate function. | ||
* | ||
* @returns Returns the index of the element if it finds it or -1 otherwise. | ||
*/ | ||
function findLastIndex(members, predicate) { | ||
let idx = members.length - 1; | ||
while (idx >= 0) { | ||
const valid = predicate(members[idx]); | ||
if (valid) { | ||
return idx; | ||
} | ||
idx--; | ||
} | ||
return -1; | ||
} | ||
exports.findLastIndex = findLastIndex; | ||
function typeNodeRequiresParentheses(node, text) { | ||
return (node.type === utils_1.AST_NODE_TYPES.TSFunctionType || | ||
node.type === utils_1.AST_NODE_TYPES.TSConstructorType || | ||
node.type === utils_1.AST_NODE_TYPES.TSConditionalType || | ||
(node.type === utils_1.AST_NODE_TYPES.TSUnionType && text.startsWith('|')) || | ||
(node.type === utils_1.AST_NODE_TYPES.TSIntersectionType && text.startsWith('&'))); | ||
} | ||
exports.typeNodeRequiresParentheses = typeNodeRequiresParentheses; | ||
//# sourceMappingURL=misc.js.map |
@@ -1,11 +0,19 @@ | ||
# Require that member overloads be consecutive (`adjacent-overload-signatures`) | ||
--- | ||
description: 'Require that function overload signatures be consecutive.' | ||
--- | ||
Grouping overloaded members together can improve readability of the code. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/adjacent-overload-signatures** for documentation. | ||
## Rule Details | ||
Function overload signatures represent multiple ways a function can be called, potentially with different return types. | ||
It's typical for an interface or type alias describing a function to place all overload signatures next to each other. | ||
If Signatures placed elsewhere in the type are easier to be missed by future developers reading the code. | ||
This rule aims to standardize the way overloaded members are organized. | ||
## Examples | ||
The following patterns are considered warnings: | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -46,3 +54,3 @@ declare namespace Foo { | ||
The following patterns are not warnings: | ||
### ✅ Correct | ||
@@ -87,5 +95,1 @@ ```ts | ||
If you don't care about the general structure of the code, then you will not need this rule. | ||
## Compatibility | ||
- TSLint: [adjacent-overload-signatures](https://palantir.github.io/tslint/rules/adjacent-overload-signatures/) |
@@ -1,30 +0,15 @@ | ||
# Requires using either `T[]` or `Array<T>` for arrays (`array-type`) | ||
--- | ||
description: 'Require consistently using either `T[]` or `Array<T>` for arrays.' | ||
--- | ||
Using the same style for array definitions across your codebase makes it easier for your developers to read and understand the types. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/array-type** for documentation. | ||
## Rule Details | ||
TypeScript provides two equivalent ways to define an array type: `T[]` and `Array<T>`. | ||
The two styles are functionally equivalent. | ||
Using the same style consistently across your codebase makes it easier for developers to read and understand array types. | ||
This rule aims to standardize usage of array types within your codebase. | ||
## Options | ||
```ts | ||
type ArrayOption = 'array' | 'generic' | 'array-simple'; | ||
type Options = { | ||
default: ArrayOption; | ||
readonly?: ArrayOption; | ||
}; | ||
const defaultOptions: Options = { | ||
default: 'array', | ||
}; | ||
``` | ||
The rule accepts an options object with the following properties: | ||
- `default` - sets the array type expected for mutable cases. | ||
- `readonly` - sets the array type expected for readonly arrays. If this is omitted, then the value for `default` will be used. | ||
Each property can be set to one of three strings: `'array' | 'generic' | 'array-simple'`. | ||
The default config will enforce that all mutable and readonly arrays use the `'array'` syntax. | ||
@@ -36,4 +21,6 @@ | ||
Incorrect code for `"array"`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -44,3 +31,3 @@ const x: Array<string> = ['a', 'b']; | ||
Correct code for `"array"`: | ||
#### ✅ Correct | ||
@@ -56,4 +43,6 @@ ```ts | ||
Incorrect code for `"generic"`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -64,3 +53,3 @@ const x: string[] = ['a', 'b']; | ||
Correct code for `"generic"`: | ||
#### ✅ Correct | ||
@@ -77,4 +66,6 @@ ```ts | ||
Incorrect code for `"array-simple"`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -89,3 +80,3 @@ const a: (string | number)[] = ['a', 'b']; | ||
Correct code for `"array-simple"`: | ||
#### ✅ Correct | ||
@@ -101,3 +92,3 @@ ```ts | ||
## Combination matrix | ||
## Combination Matrix | ||
@@ -120,5 +111,1 @@ This matrix lists all possible option combinations and their expected results for different types of Arrays. | ||
| `generic` | `generic` | `Array<number>` | `Array<Foo & Bar>` | `ReadonlyArray<number>` | `ReadonlyArray<Foo & Bar>` | | ||
## Related to | ||
- TSLint: [array-type](https://palantir.github.io/tslint/rules/array-type/) |
@@ -1,10 +0,21 @@ | ||
# Disallows awaiting a value that is not a Thenable (`await-thenable`) | ||
--- | ||
description: 'Disallow awaiting a value that is not a Thenable.' | ||
--- | ||
This rule disallows awaiting a value that is not a "Thenable" (an object which has `then` method, such as a Promise). | ||
While it is valid JavaScript to await a non-`Promise`-like value (it will resolve immediately), this pattern is often a programmer error, such as forgetting to add parenthesis to call a function that returns a Promise. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/await-thenable** for documentation. | ||
## Rule Details | ||
A "Thenable" value is an object which has a `then` method, such as a Promise. | ||
The `await` keyword is generally used to retrieve the result of calling a Thenable's `then` method. | ||
Examples of **incorrect** code for this rule: | ||
If the `await` keyword is used on a value that is not a Thenable, the value is directly resolved immediately. | ||
While doing so is valid JavaScript, it is often a programmer error, such as forgetting to add parenthesis to call a function that returns a Promise. | ||
## Examples | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -17,3 +28,3 @@ await 'value'; | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -31,5 +42,1 @@ ```ts | ||
This is generally not preferred, but can sometimes be useful for visual consistency. | ||
## Related to | ||
- TSLint: ['await-promise'](https://palantir.github.io/tslint/rules/await-promise) |
@@ -1,5 +0,12 @@ | ||
# Bans `@ts-<directive>` comments from being used or requires descriptions after directive (`ban-ts-comment`) | ||
--- | ||
description: 'Disallow `@ts-<directive>` comments or require descriptions after directives.' | ||
--- | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/ban-ts-comment** for documentation. | ||
TypeScript provides several directive comments that can be used to alter how it processes files. | ||
Using these to suppress TypeScript Compiler Errors reduces the effectiveness of TypeScript overall. | ||
Using these to suppress TypeScript compiler errors reduces the effectiveness of TypeScript overall. | ||
Instead, it's generally better to correct the types of code, to make directives unnecessary. | ||
@@ -15,27 +22,8 @@ The directive comments supported by TypeScript are: | ||
## Rule Details | ||
This rule lets you set which directive comments you want to allow in your codebase. | ||
By default, only `@ts-check` is allowed, as it enables rather than suppresses errors. | ||
The configuration looks like this: | ||
## Options | ||
```ts | ||
interface Options { | ||
'ts-expect-error'?: boolean | 'allow-with-description'; | ||
'ts-ignore'?: boolean | 'allow-with-description'; | ||
'ts-nocheck'?: boolean | 'allow-with-description'; | ||
'ts-check'?: boolean | 'allow-with-description'; | ||
minimumDescriptionLength?: number; | ||
} | ||
By default, only `@ts-check` is allowed, as it enables rather than suppresses errors. | ||
const defaultOptions: Options = { | ||
'ts-expect-error': 'allow-with-description', | ||
'ts-ignore': true, | ||
'ts-nocheck': true, | ||
'ts-check': false, | ||
minimumDescriptionLength: 3, | ||
}; | ||
``` | ||
### `ts-expect-error`, `ts-ignore`, `ts-nocheck`, `ts-check` directives | ||
@@ -45,4 +33,6 @@ | ||
For example, with the defaults above the following patterns are considered warnings for single line or comment block lines: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -61,3 +51,3 @@ if (false) { | ||
The following patterns are not warnings: | ||
#### ✅ Correct | ||
@@ -75,4 +65,8 @@ ```ts | ||
For example, with `{ 'ts-expect-error': 'allow-with-description' }` the following patterns are considered a warning: | ||
For example, with `{ 'ts-expect-error': 'allow-with-description' }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -89,3 +83,3 @@ if (false) { | ||
The following patterns are not a warning: | ||
#### ✅ Correct | ||
@@ -105,2 +99,24 @@ ```ts | ||
### `descriptionFormat` | ||
For each directive type, you can specify a custom format in the form of a regular expression. Only description that matches the pattern will be allowed. | ||
For example, with `{ 'ts-expect-error': { descriptionFormat: '^: TS\\d+ because .+$' } }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
// @ts-expect-error: the library definition is wrong | ||
const a = doSomething('hello'); | ||
``` | ||
#### ✅ Correct | ||
```ts | ||
// @ts-expect-error: TS1234 because the library definition is wrong | ||
const a = doSomething('hello'); | ||
``` | ||
### `minimumDescriptionLength` | ||
@@ -110,4 +126,8 @@ | ||
For example, with `{ 'ts-expect-error': 'allow-with-description', minimumDescriptionLength: 10 }` the following pattern is considered a warning: | ||
For example, with `{ 'ts-expect-error': 'allow-with-description', minimumDescriptionLength: 10 }` the following pattern is: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -120,3 +140,3 @@ if (false) { | ||
The following pattern is not a warning: | ||
#### ✅ Correct | ||
@@ -137,5 +157,1 @@ ```ts | ||
- TypeScript [Type Checking JavaScript Files](https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html) | ||
## Compatibility | ||
- TSLint: [ban-ts-ignore](https://palantir.github.io/tslint/rules/ban-ts-ignore/) |
@@ -1,11 +0,19 @@ | ||
# Bans `// tslint:<rule-flag>` comments from being used (`ban-tslint-comment`) | ||
--- | ||
description: 'Disallow `// tslint:<rule-flag>` comments.' | ||
--- | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/ban-tslint-comment** for documentation. | ||
Useful when migrating from TSLint to ESLint. Once TSLint has been removed, this rule helps locate TSLint annotations (e.g. `// tslint:disable`). | ||
## Rule Details | ||
> See the [TSLint rule flags docs](https://palantir.github.io/tslint/usage/rule-flags) for reference. | ||
Examples of **incorrect** code for this rule: | ||
## Examples | ||
All TSLint [rule flags](https://palantir.github.io/tslint/usage/rule-flags/) | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```js | ||
@@ -21,6 +29,8 @@ /* tslint:disable */ | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
```js | ||
// This is a comment that just happens to mention tslint | ||
/* This is a multiline comment that just happens to mention tslint */ | ||
someCode(); // This is a comment that just happens to mention tslint | ||
``` | ||
@@ -27,0 +37,0 @@ |
@@ -1,68 +0,64 @@ | ||
# Bans specific types from being used (`ban-types`) | ||
--- | ||
description: 'Disallow certain types.' | ||
--- | ||
Some builtin types have aliases, some types are considered dangerous or harmful. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/ban-types** for documentation. | ||
Some built-in types have aliases, while some types are considered dangerous or harmful. | ||
It's often a good idea to ban certain types to help with consistency and safety. | ||
## Rule Details | ||
This rule bans specific types and can suggest alternatives. | ||
Note that it does not ban the corresponding runtime objects from being used. | ||
## Options | ||
## Examples | ||
Examples of code with the default options: | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
type Options = { | ||
types?: { | ||
[typeName: string]: | ||
| false | ||
| string | ||
| { | ||
message: string; | ||
fixWith?: string; | ||
}; | ||
}; | ||
extendDefaults?: boolean; | ||
}; | ||
// use lower-case primitives for consistency | ||
const str: String = 'foo'; | ||
const bool: Boolean = true; | ||
const num: Number = 1; | ||
const symb: Symbol = Symbol('foo'); | ||
const bigInt: BigInt = 1n; | ||
// use a proper function type | ||
const func: Function = () => 1; | ||
// use safer object types | ||
const lowerObj: Object = {}; | ||
const capitalObj: Object = { a: 'string' }; | ||
const curly1: {} = 1; | ||
const curly2: {} = { a: 'string' }; | ||
``` | ||
The rule accepts a single object as options, with the following keys: | ||
### ✅ Correct | ||
- `types` - An object whose keys are the types you want to ban, and the values are error messages. | ||
- The type can either be a type name literal (`Foo`), a type name with generic parameter instantiation(s) (`Foo<Bar>`), the empty object literal (`{}`), or the empty tuple type (`[]`). | ||
- The values can be a string, which is the error message to be reported, `false` to specifically disable this type | ||
or it can be an object with the following properties: | ||
- `message: string` - the message to display when the type is matched. | ||
- `fixWith?: string` - a string to replace the banned type with when the fixer is run. If this is omitted, no fix will be done. | ||
- `extendDefaults` - if you're specifying custom `types`, you can set this to `true` to extend the default `types` configuration. | ||
- This is a convenience option to save you copying across the defaults when adding another type. | ||
- If this is `false`, the rule will _only_ use the types defined in your configuration. | ||
```ts | ||
// use lower-case primitives for consistency | ||
const str: string = 'foo'; | ||
const bool: boolean = true; | ||
const num: number = 1; | ||
const symb: symbol = Symbol('foo'); | ||
const bigInt: bigint = 1n; | ||
Example configuration: | ||
// use a proper function type | ||
const func: () => number = () => 1; | ||
```jsonc | ||
{ | ||
"@typescript-eslint/ban-types": [ | ||
"error", | ||
{ | ||
"types": { | ||
// add a custom message to help explain why not to use it | ||
"Foo": "Don't use Foo because it is unsafe", | ||
// use safer object types | ||
const lowerObj: object = {}; | ||
const capitalObj: { a: string } = { a: 'string' }; | ||
// add a custom message, AND tell the plugin how to fix it | ||
"String": { | ||
"message": "Use string instead", | ||
"fixWith": "string" | ||
}, | ||
"{}": { | ||
"message": "Use object instead", | ||
"fixWith": "object" | ||
} | ||
} | ||
} | ||
] | ||
} | ||
const curly1: number = 1; | ||
const curly2: Record<'a', string> = { a: 'string' }; | ||
``` | ||
### Default Options | ||
## Options | ||
@@ -78,7 +74,3 @@ The default options provide a set of "best practices", intended to provide safety and standardization in your codebase: | ||
- See [this comment for more information](https://github.com/typescript-eslint/typescript-eslint/issues/2063#issuecomment-675156492). | ||
- Avoid the `object` type, as it is currently hard to use due to not being able to assert that keys exist. | ||
- See [microsoft/TypeScript#21732](https://github.com/microsoft/TypeScript/issues/21732). | ||
**_Important note:_** the default options suggest using `Record<string, unknown>`; this was a stylistic decision, as the built-in `Record` type is considered to look cleaner. | ||
<details> | ||
@@ -105,3 +97,6 @@ <summary>Default Options</summary> | ||
}, | ||
BigInt: { | ||
message: 'Use bigint instead', | ||
fixWith: 'bigint', | ||
}, | ||
Function: { | ||
@@ -115,3 +110,2 @@ message: [ | ||
}, | ||
// object typing | ||
@@ -121,5 +115,7 @@ Object: { | ||
'The `Object` type actually means "any non-nullish value", so it is marginally better than `unknown`.', | ||
'- If you want a type meaning "any object", you probably want `Record<string, unknown>` instead.', | ||
'- If you want a type meaning "any object", you probably want `object` instead.', | ||
'- If you want a type meaning "any value", you probably want `unknown` instead.', | ||
'- If you really want a type meaning "any non-nullish value", you probably want `NonNullable<unknown>` instead.', | ||
].join('\n'), | ||
suggest: ['object', 'unknown', 'NonNullable<unknown>'], | ||
}, | ||
@@ -129,12 +125,14 @@ '{}': { | ||
'`{}` actually means "any non-nullish value".', | ||
'- If you want a type meaning "any object", you probably want `Record<string, unknown>` instead.', | ||
'- If you want a type meaning "any object", you probably want `object` instead.', | ||
'- If you want a type meaning "any value", you probably want `unknown` instead.', | ||
'- If you want a type meaning "empty object", you probably want `Record<string, never>` instead.', | ||
'- If you really want a type meaning "any non-nullish value", you probably want `NonNullable<unknown>` instead.', | ||
].join('\n'), | ||
suggest: [ | ||
'object', | ||
'unknown', | ||
'Record<string, never>', | ||
'NonNullable<unknown>', | ||
], | ||
}, | ||
object: { | ||
message: [ | ||
'The `object` type is currently hard to use ([see this issue](https://github.com/microsoft/TypeScript/issues/21732)).', | ||
'Consider using `Record<string, unknown>` instead, as it allows you to more easily inspect and use the keys.', | ||
].join('\n'), | ||
}, | ||
}; | ||
@@ -145,50 +143,47 @@ ``` | ||
### Examples | ||
### `types` | ||
Examples of **incorrect** code with the default options: | ||
An object whose keys are the types you want to ban, and the values are error messages. | ||
```ts | ||
// use lower-case primitives for consistency | ||
const str: String = 'foo'; | ||
const bool: Boolean = true; | ||
const num: Number = 1; | ||
const symb: Symbol = Symbol('foo'); | ||
The type can either be a type name literal (`Foo`), a type name with generic parameter instantiation(s) (`Foo<Bar>`), the empty object literal (`{}`), or the empty tuple type (`[]`). | ||
// use a proper function type | ||
const func: Function = () => 1; | ||
The values can be: | ||
// use safer object types | ||
const lowerObj: object = {}; | ||
- A string, which is the error message to be reported; or | ||
- `false` to specifically un-ban this type (useful when you are using `extendDefaults`); or | ||
- An object with the following properties: | ||
- `message: string` - the message to display when the type is matched. | ||
- `fixWith?: string` - a string to replace the banned type with when the fixer is run. If this is omitted, no fix will be done. | ||
- `suggest?: string[]` - a list of suggested replacements for the banned type. | ||
const capitalObj1: Object = 1; | ||
const capitalObj2: Object = { a: 'string' }; | ||
### `extendDefaults` | ||
const curly1: {} = 1; | ||
const curly2: {} = { a: 'string' }; | ||
``` | ||
If you're specifying custom `types`, you can set this to `true` to extend the default `types` configuration. This is a convenience option to save you copying across the defaults when adding another type. | ||
Examples of **correct** code with the default options: | ||
If this is `false`, the rule will _only_ use the types defined in your configuration. | ||
```ts | ||
// use lower-case primitives for consistency | ||
const str: string = 'foo'; | ||
const bool: boolean = true; | ||
const num: number = 1; | ||
const symb: symbol = Symbol('foo'); | ||
Example configuration: | ||
// use a proper function type | ||
const func: () => number = () => 1; | ||
```jsonc | ||
{ | ||
"@typescript-eslint/ban-types": [ | ||
"error", | ||
{ | ||
"types": { | ||
// add a custom message to help explain why not to use it | ||
"Foo": "Don't use Foo because it is unsafe", | ||
// use safer object types | ||
const lowerObj: Record<string, unknown> = {}; | ||
// add a custom message, AND tell the plugin how to fix it | ||
"OldAPI": { | ||
"message": "Use NewAPI instead", | ||
"fixWith": "NewAPI" | ||
}, | ||
const capitalObj1: number = 1; | ||
const capitalObj2: { a: string } = { a: 'string' }; | ||
const curly1: number = 1; | ||
const curly2: Record<'a', string> = { a: 'string' }; | ||
// un-ban a type that's banned by default | ||
"{}": false | ||
}, | ||
"extendDefaults": true | ||
} | ||
] | ||
} | ||
``` | ||
## Compatibility | ||
- TSLint: [ban-types](https://palantir.github.io/tslint/rules/ban-types/) |
@@ -1,22 +0,12 @@ | ||
# Enforce consistent brace style for blocks (`brace-style`) | ||
--- | ||
description: 'Enforce consistent brace style for blocks.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/brace-style** for documentation. | ||
## Examples | ||
This rule extends the base [`eslint/brace-style`](https://eslint.org/docs/rules/brace-style) rule. | ||
It adds support for `enum`, `interface`, `namespace` and `module` declarations. | ||
## How to use | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"brace-style": "off", | ||
"@typescript-eslint/brace-style": ["error"] | ||
} | ||
``` | ||
## Options | ||
See [`eslint/brace-style` options](https://eslint.org/docs/rules/brace-style#options). | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/brace-style.md)</sup> |
@@ -1,5 +0,7 @@ | ||
## DEPRECATED | ||
:::danger Deprecated | ||
This rule has been deprecated in favour of the [`naming-convention`](./naming-convention.md) rule. | ||
::: | ||
<!-- | ||
@@ -6,0 +8,0 @@ This doc file has been left on purpose because `camelcase` is a core eslint rule. |
@@ -1,7 +0,11 @@ | ||
# Ensures that literals on classes are exposed in a consistent style (`class-literal-property-style`) | ||
--- | ||
description: 'Enforce that literals on classes are exposed in a consistent style.' | ||
--- | ||
When writing TypeScript applications, it's typically safe to store literal values on classes using fields with the `readonly` modifier to prevent them from being reassigned. | ||
When writing TypeScript libraries that could be used by JavaScript users however, it's typically safer to expose these literals using `getter`s, since the `readonly` modifier is enforced at compile type. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/class-literal-property-style** for documentation. | ||
## Rule Details | ||
Some TypeScript applications store literal values on classes using fields with the `readonly` modifier to prevent them from being reassigned. | ||
When writing TypeScript libraries that could be used by JavaScript users, however, it's typically safer to expose these literals using `getter`s, since the `readonly` modifier is enforced at compile type. | ||
@@ -11,11 +15,21 @@ This rule aims to ensure that literals exposed by classes are done so consistently, in one of the two style described above. | ||
Note that this rule only checks for constant _literal_ values (string, template string, number, bigint, boolean, regexp, null). It does not check objects or arrays, because a readonly field behaves differently to a getter in those cases. It also does not check functions, as it is a common pattern to use readonly fields with arrow function values as auto-bound methods. | ||
## Options | ||
:::note | ||
This rule only checks for constant _literal_ values (string, template string, number, bigint, boolean, regexp, null). It does not check objects or arrays, because a readonly field behaves differently to a getter in those cases. It also does not check functions, as it is a common pattern to use readonly fields with arrow function values as auto-bound methods. | ||
This is because these types can be mutated and carry with them more complex implications about their usage. | ||
### The `fields` style | ||
::: | ||
### `"fields"` | ||
This style checks for any getter methods that return literal values, and requires them to be defined using fields with the `readonly` modifier instead. | ||
Examples of **correct** code with the `fields` style: | ||
Examples of code with the `fields` style: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -25,11 +39,8 @@ /* eslint @typescript-eslint/class-literal-property-style: ["error", "fields"] */ | ||
class Mx { | ||
public readonly myField1 = 1; | ||
public static get myField1() { | ||
return 1; | ||
} | ||
// not a literal | ||
public readonly myField2 = [1, 2, 3]; | ||
private readonly ['myField3'] = 'hello world'; | ||
public get myField4() { | ||
return `hello from ${window.location.href}`; | ||
private get ['myField2']() { | ||
return 'hello world'; | ||
} | ||
@@ -39,3 +50,3 @@ } | ||
Examples of **incorrect** code with the `fields` style: | ||
#### ✅ Correct | ||
@@ -46,8 +57,11 @@ ```ts | ||
class Mx { | ||
public static get myField1() { | ||
return 1; | ||
} | ||
public readonly myField1 = 1; | ||
private get ['myField2']() { | ||
return 'hello world'; | ||
// not a literal | ||
public readonly myField2 = [1, 2, 3]; | ||
private readonly ['myField3'] = 'hello world'; | ||
public get myField4() { | ||
return `hello from ${window.location.href}`; | ||
} | ||
@@ -57,3 +71,3 @@ } | ||
### The `getters` style | ||
### `"getters"` | ||
@@ -64,4 +78,8 @@ This style checks for any `readonly` fields that are assigned literal values, and requires them to be defined as getters instead. | ||
Examples of **correct** code with the `getters` style: | ||
Examples of code with the `getters` style: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -71,2 +89,14 @@ /* eslint @typescript-eslint/class-literal-property-style: ["error", "getters"] */ | ||
class Mx { | ||
readonly myField1 = 1; | ||
readonly myField2 = `hello world`; | ||
private readonly myField3 = 'hello world'; | ||
} | ||
``` | ||
#### ✅ Correct | ||
```ts | ||
/* eslint @typescript-eslint/class-literal-property-style: ["error", "getters"] */ | ||
class Mx { | ||
// no readonly modifier | ||
@@ -88,14 +118,2 @@ public myField1 = 'hello'; | ||
Examples of **incorrect** code with the `getters` style: | ||
```ts | ||
/* eslint @typescript-eslint/class-literal-property-style: ["error", "getters"] */ | ||
class Mx { | ||
readonly myField1 = 1; | ||
readonly myField2 = `hello world`; | ||
private readonly myField3 = 'hello world'; | ||
} | ||
``` | ||
## When Not To Use It | ||
@@ -102,0 +120,0 @@ |
@@ -1,5 +0,11 @@ | ||
# Require or disallow trailing comma (`comma-dangle`) | ||
--- | ||
description: 'Require or disallow trailing commas.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/comma-dangle** for documentation. | ||
## Examples | ||
This rule extends the base [`eslint/comma-dangle`](https://eslint.org/docs/rules/comma-dangle) rule. | ||
@@ -10,26 +16,8 @@ It adds support for TypeScript syntax. | ||
## Rule Changes | ||
## How to Use | ||
```cjson | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"comma-dangle": "off", | ||
"@typescript-eslint/comma-dangle": ["error"] | ||
} | ||
``` | ||
In addition to the options supported by the `comma-dangle` rule in ESLint core, the rule adds the following options: | ||
## Options | ||
This rule has a string option and an object option. | ||
- Object option: | ||
- `"enums"` is for trailing comma in enum. (e.g. `enum Foo = {Bar,}`) | ||
- `"generics"` is for trailing comma in generic. (e.g. `function foo<T,>() {}`) | ||
- `"tuples"` is for trailing comma in tuple. (e.g. `type Foo = [string,]`) | ||
- [See the other options allowed](https://github.com/eslint/eslint/blob/master/docs/rules/comma-dangle.md#options) | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/comma-dangle.md)</sup> | ||
- `"enums"` is for trailing comma in enum. (e.g. `enum Foo = {Bar,}`) | ||
- `"generics"` is for trailing comma in generic. (e.g. `function foo<T,>() {}`) | ||
- `"tuples"` is for trailing comma in tuple. (e.g. `type Foo = [string,]`) |
@@ -1,22 +0,12 @@ | ||
# Enforces consistent spacing before and after commas (`comma-spacing`) | ||
--- | ||
description: 'Enforce consistent spacing before and after commas.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/comma-spacing** for documentation. | ||
## Examples | ||
This rule extends the base [`eslint/comma-spacing`](https://eslint.org/docs/rules/comma-spacing) rule. | ||
It adds support for trailing comma in a types parameters list. | ||
## How to use | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"comma-spacing": "off", | ||
"@typescript-eslint/comma-spacing": ["error"] | ||
} | ||
``` | ||
## Options | ||
See [`eslint/comma-spacing` options](https://eslint.org/docs/rules/comma-spacing#options). | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/comma-spacing.md)</sup> |
@@ -1,5 +0,11 @@ | ||
# Enforce or disallow the use of the record type (`consistent-indexed-object-style`) | ||
--- | ||
description: 'Require or disallow the `Record` type.' | ||
--- | ||
TypeScript supports defining object show keys can be flexible using an index signature. TypeScript also has a builtin type named `Record` to create an empty object defining only an index signature. For example, the following types are equal: | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/consistent-indexed-object-style** for documentation. | ||
TypeScript supports defining arbitrary object keys using an index signature. TypeScript also has a builtin type named `Record` to create an empty object defining only an index signature. For example, the following types are equal: | ||
```ts | ||
@@ -17,21 +23,18 @@ interface Foo { | ||
Keeping to one declaration form consistently improve code readability. | ||
## Options | ||
- `"record"`: Set to `"record"` to only allow the `Record` type. Set to `"index-signature"` to only allow index signatures. (Defaults to `"record"`) | ||
- `"record"` _(default)_: only allow the `Record` type. | ||
- `"index-signature"`: only allow index signatures. | ||
For example: | ||
### `record` | ||
```CJSON | ||
{ | ||
"@typescript-eslint/consistent-indexed-object-style": ["error", "index-signature"] | ||
} | ||
``` | ||
<!--tabs--> | ||
## Rule details | ||
#### ❌ Incorrect | ||
This rule enforces a consistent way to define records. | ||
```ts | ||
/* eslint @typescript-eslint/consistent-indexed-object-style: ["error", "record"] */ | ||
Examples of **incorrect** code with `record` option. | ||
```ts | ||
interface Foo { | ||
@@ -46,17 +49,27 @@ [key: string]: unknown; | ||
Examples of **correct** code with `record` option. | ||
#### ✅ Correct | ||
```ts | ||
/* eslint @typescript-eslint/consistent-indexed-object-style: ["error", "record"] */ | ||
type Foo = Record<string, unknown>; | ||
``` | ||
Examples of **incorrect** code with `index-signature` option. | ||
### `index-signature` | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
/* eslint @typescript-eslint/consistent-indexed-object-style: ["error", "index-signature"] */ | ||
type Foo = Record<string, unknown>; | ||
``` | ||
Examples of **correct** code with `index-signature` option. | ||
#### ✅ Correct | ||
```ts | ||
/* eslint @typescript-eslint/consistent-indexed-object-style: ["error", "index-signature"] */ | ||
interface Foo { | ||
@@ -63,0 +76,0 @@ [key: string]: unknown; |
@@ -1,31 +0,28 @@ | ||
# Enforces consistent usage of type assertions (`consistent-type-assertions`) | ||
--- | ||
description: 'Enforce consistent usage of type assertions.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/consistent-type-assertions** for documentation. | ||
TypeScript provides two syntaxes for "type assertions": | ||
- Angle brackets: `<Type>value` | ||
- As: `value as Type` | ||
This rule aims to standardize the use of type assertion style across the codebase. | ||
Keeping to one syntax consistently helps with code readability. | ||
Type assertions are also commonly referred as "type casting" in TypeScript (even though it is technically slightly different to what is understood by type casting in other languages), so you can think of type assertions and type casting referring to the same thing. It is essentially you saying to the TypeScript compiler, "in this case, I know better than you!". | ||
:::note | ||
Type assertions are also commonly referred as "type casting" in TypeScript. | ||
However, that term is technically slightly different to what is understood by type casting in other languages. | ||
Type assertions are a way to say to the TypeScript compiler, _"I know better than you, it's actually this different type!"_. | ||
::: | ||
In addition to ensuring that type assertions are written in a consistent way, this rule also helps make your codebase more type-safe. | ||
[`const` assertions](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions) are always allowed by this rule. | ||
Examples of them include `let x = "hello" as const;` and `let x = <const>"hello";`. | ||
`const` assertions, [introduced in TypeScript 3.4](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions), is always allowed by this rule. Examples of it include `let x = "hello" as const;` and `let x = <const>"hello";`. | ||
## Options | ||
```ts | ||
type Options = | ||
| { | ||
assertionStyle: 'as' | 'angle-bracket'; | ||
objectLiteralTypeAssertions: 'allow' | 'allow-as-parameter' | 'never'; | ||
} | ||
| { | ||
assertionStyle: 'never'; | ||
}; | ||
const defaultOptions: Options = { | ||
assertionStyle: 'as', | ||
objectLiteralTypeAssertions: 'allow', | ||
}; | ||
``` | ||
### `assertionStyle` | ||
@@ -53,4 +50,8 @@ | ||
Examples of **incorrect** code for `{ assertionStyle: 'as', objectLiteralTypeAssertions: 'never' }` (and for `{ assertionStyle: 'as', objectLiteralTypeAssertions: 'allow-as-parameter' }`) | ||
Examples of code for `{ assertionStyle: 'as', objectLiteralTypeAssertions: 'never' }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -64,3 +65,3 @@ const x = { ... } as T; | ||
Examples of **correct** code for `{ assertionStyle: 'as', objectLiteralTypeAssertions: 'never' }`. | ||
#### ✅ Correct | ||
@@ -77,4 +78,20 @@ ```ts | ||
Examples of **correct** code for `{ assertionStyle: 'as', objectLiteralTypeAssertions: 'allow-as-parameter' }`. | ||
<!--/tabs--> | ||
Examples of code for `{ assertionStyle: 'as', objectLiteralTypeAssertions: 'allow-as-parameter' }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
const x = { ... } as T; | ||
function foo() { | ||
return { ... } as T; | ||
} | ||
``` | ||
#### ✅ Correct | ||
```tsx | ||
@@ -90,9 +107,6 @@ const x: T = { ... }; | ||
<!--/tabs--> | ||
## When Not To Use It | ||
If you do not want to enforce consistent type assertions. | ||
## Compatibility | ||
- TSLint: [no-angle-bracket-type-assertion](https://palantir.github.io/tslint/rules/no-angle-bracket-type-assertion/) | ||
- TSLint: [no-object-literal-type-assertion](https://palantir.github.io/tslint/rules/no-object-literal-type-assertion/) |
@@ -1,5 +0,11 @@ | ||
# Consistent with type definition either `interface` or `type` (`consistent-type-definitions`) | ||
--- | ||
description: 'Enforce type definitions to consistently use either `interface` or `type`.' | ||
--- | ||
There are two ways to define a type. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/consistent-type-definitions** for documentation. | ||
TypeScript provides two common ways to define an object type: `interface` and `type`. | ||
```ts | ||
@@ -19,29 +25,27 @@ // type alias | ||
The two are generally very similar, and can often be used interchangeably. | ||
Using the same type declaration style consistently helps with code readability. | ||
## Options | ||
This rule accepts one string option: | ||
- `"interface"`: enforce using `interface`s for object type definitions. | ||
- `"interface"` _(default)_: enforce using `interface`s for object type definitions. | ||
- `"type"`: enforce using `type`s for object type definitions. | ||
For example: | ||
### `interface` | ||
```jsonc | ||
{ | ||
// Use type for object definitions | ||
"@typescript-eslint/consistent-type-definitions": ["error", "type"] | ||
} | ||
``` | ||
<!--tabs--> | ||
## Rule Details | ||
#### ❌ Incorrect | ||
Examples of **incorrect** code with `interface` option. | ||
```ts | ||
/* eslint @typescript-eslint/consistent-type-definitions: ["error", "interface"] */ | ||
```ts | ||
type T = { x: number }; | ||
``` | ||
Examples of **correct** code with `interface` option. | ||
#### ✅ Correct | ||
```ts | ||
/* eslint @typescript-eslint/consistent-type-definitions: ["error", "interface"] */ | ||
type T = string; | ||
@@ -55,5 +59,11 @@ type Foo = string | {}; | ||
Examples of **incorrect** code with `type` option. | ||
### `type` | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
/* eslint @typescript-eslint/consistent-type-definitions: ["error", "type"] */ | ||
interface T { | ||
@@ -64,5 +74,7 @@ x: number; | ||
Examples of **correct** code with `type` option. | ||
#### ✅ Correct | ||
```ts | ||
/* eslint @typescript-eslint/consistent-type-definitions: ["error", "type"] */ | ||
type T = { x: number }; | ||
@@ -74,5 +86,1 @@ ``` | ||
If you specifically want to use an interface or type literal for stylistic reasons, you can disable this rule. | ||
## Compatibility | ||
- TSLint: [interface-over-type-literal](https://palantir.github.io/tslint/rules/interface-over-type-literal/) |
@@ -1,24 +0,16 @@ | ||
# Enforces consistent usage of type imports (`consistent-type-imports`) | ||
--- | ||
description: 'Enforce consistent usage of type imports.' | ||
--- | ||
TypeScript 3.8 added support for type-only imports. | ||
Type-only imports allow you to specify that an import can only be used in a type location, allowing certain optimizations within compilers. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/consistent-type-imports** for documentation. | ||
## Rule Details | ||
TypeScript allows specifying a `type` keyword on imports to indicate that the export exists only in the type system, not at runtime. | ||
This allows transpilers to drop imports without knowing the types of the dependencies. | ||
This rule aims to standardize the use of type imports style across the codebase. | ||
> See [Blog > Consistent Type Exports and Imports: Why and How](/blog/consistent-type-imports-and-exports-why-and-how) for more details. | ||
## Options | ||
```ts | ||
type Options = { | ||
prefer: 'type-imports' | 'no-type-imports'; | ||
disallowTypeAnnotations: boolean; | ||
}; | ||
const defaultOptions: Options = { | ||
prefer: 'type-imports', | ||
disallowTypeAnnotations: true, | ||
}; | ||
``` | ||
### `prefer` | ||
@@ -28,3 +20,3 @@ | ||
- `type-imports` will enforce that you always use `import type Foo from '...'` except referenced by metadata of decorators. It is default. | ||
- `type-imports` will enforce that you always use `import type Foo from '...'` except referenced by metadata of decorators. It is the default. | ||
- `no-type-imports` will enforce that you always use `import Foo from '...'`. | ||
@@ -50,8 +42,46 @@ | ||
### `fixStyle` | ||
This option defines the expected type modifier to be added when an import is detected as used only in the type position. Valid values for `fixStyle` are: | ||
- `separate-type-imports` will add the type keyword after the import keyword `import type { A } from '...'`. It is the default. | ||
- `inline-type-imports` will inline the type keyword `import { type A } from '...'` and is only available in TypeScript 4.5 and onwards. See [documentation here](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-5.html#type-modifiers-on-import-names 'TypeScript 4.5 documentation on type modifiers and import names'). | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
import { Foo } from 'Foo'; | ||
import Bar from 'Bar'; | ||
type T = Foo; | ||
const x: Bar = 1; | ||
``` | ||
#### ✅ With `separate-type-imports` | ||
```ts | ||
import type { Foo } from 'Foo'; | ||
import type Bar from 'Bar'; | ||
type T = Foo; | ||
const x: Bar = 1; | ||
``` | ||
#### ✅ With `inline-type-imports` | ||
```ts | ||
import { type Foo } from 'Foo'; | ||
import type Bar from 'Bar'; | ||
type T = Foo; | ||
const x: Bar = 1; | ||
``` | ||
<!--tabs--> | ||
### `disallowTypeAnnotations` | ||
If `true`, type imports in type annotations (`import()`) is not allowed. | ||
If `true`, type imports in type annotations (`import()`) are not allowed. | ||
Default is `true`. | ||
Examples of **incorrect** code with `{disallowTypeAnnotations: true}`. | ||
Examples of **incorrect** code with `{disallowTypeAnnotations: true}`: | ||
@@ -63,7 +93,16 @@ ```ts | ||
## Usage with `emitDecoratorMetadata` | ||
The `emitDecoratorMetadata` compiler option changes the code the TypeScript emits. In short - it causes TypeScript to create references to value imports when they are used in a type-only location. If you are using `emitDecoratorMetadata` then our tooling will require additional information in order for the rule to work correctly. | ||
If you are using [type-aware linting](https://typescript-eslint.io/linting/typed-linting), then you just need to ensure that the `tsconfig.json` you've configured for `parserOptions.project` has `emitDecoratorMetadata` turned on. Otherwise you can explicitly tell our tooling to analyze your code as if the compiler option was turned on [by setting `parserOptions.emitDecoratorMetadata` to `true`](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/README.md#parseroptionsemitdecoratormetadata). | ||
## When Not To Use It | ||
- If you are not using TypeScript 3.8 (or greater), then you will not be able to use this rule, as type-only imports are not allowed. | ||
- Certain libraries use the non-inlined imports to infer information about the variables. For example, for dependency injection. | ||
type-only imports cannot be used with these libraries. See [#2559](https://github.com/typescript-eslint/typescript-eslint/issues/2559#issuecomment-692780580) | ||
- If you specifically want to use both import kinds for stylistic reasons, you can disable this rule. | ||
## Related To | ||
- [`no-import-type-side-effects`](./no-import-type-side-effects.md) | ||
- [`import/consistent-type-specifier-style`](https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/consistent-type-specifier-style.md) | ||
- [`import/no-duplicates` with `{"prefer-inline": true}`](https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-duplicates.md#inline-type-imports) |
@@ -1,12 +0,20 @@ | ||
# Enforce default parameters to be last (`default-param-last`) | ||
--- | ||
description: 'Enforce default parameters to be last.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/default-param-last** for documentation. | ||
## Examples | ||
This rule extends the base [`eslint/default-param-last`](https://eslint.org/docs/rules/default-param-last) rule. | ||
It adds support for optional parameters. | ||
Examples of **incorrect** code for this rule: | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
/* eslint @typescript-eslint/default-param-last: ["error"] */ | ||
/* eslint @typescript-eslint/default-param-last: "error" */ | ||
@@ -24,6 +32,6 @@ function f(a = 0, b: number) {} | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
```ts | ||
/* eslint @typescript-eslint/default-param-last: ["error"] */ | ||
/* eslint @typescript-eslint/default-param-last: "error" */ | ||
@@ -42,17 +50,1 @@ function f(a = 0) {} | ||
``` | ||
## How to use | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"default-param-last": "off", | ||
"@typescript-eslint/default-param-last": ["error"] | ||
} | ||
``` | ||
## Options | ||
See [`eslint/default-param-last` options](https://eslint.org/docs/rules/default-param-last#options). | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/default-param-last.md)</sup> |
@@ -1,21 +0,19 @@ | ||
# enforce dot notation whenever possible (`dot-notation`) | ||
--- | ||
description: 'Enforce dot notation whenever possible.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/dot-notation** for documentation. | ||
## Examples | ||
This rule extends the base [`eslint/dot-notation`](https://eslint.org/docs/rules/dot-notation) rule. | ||
It adds support for optionally ignoring computed `private` member access. | ||
It adds: | ||
## How to use | ||
- Support for optionally ignoring computed `private` and/or `protected` member access. | ||
- Compatibility with TypeScript's `noPropertyAccessFromIndexSignature` option. | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"dot-notation": "off", | ||
"@typescript-eslint/dot-notation": ["error"] | ||
} | ||
``` | ||
## Options | ||
See [`eslint/dot-notation`](https://eslint.org/docs/rules/dot-notation#options) options. | ||
This rule adds the following options: | ||
@@ -27,3 +25,5 @@ | ||
allowProtectedClassPropertyAccess?: boolean; | ||
allowIndexSignaturePropertyAccess?: boolean; | ||
} | ||
const defaultOptions: Options = { | ||
@@ -33,8 +33,11 @@ ...baseDotNotationDefaultOptions, | ||
allowProtectedClassPropertyAccess: false, | ||
allowIndexSignaturePropertyAccess: false, | ||
}; | ||
``` | ||
If the TypeScript compiler option `noPropertyAccessFromIndexSignature` is set to `true`, then this rule always allows the use of square bracket notation to access properties of types that have a `string` index signature, even if `allowIndexSignaturePropertyAccess` is `false`. | ||
### `allowPrivateClassPropertyAccess` | ||
Example of a correct code when `allowPrivateClassPropertyAccess` is set to `true` | ||
Example of a correct code when `allowPrivateClassPropertyAccess` is set to `true`: | ||
@@ -52,3 +55,3 @@ ```ts | ||
Example of a correct code when `allowProtectedClassPropertyAccess` is set to `true` | ||
Example of a correct code when `allowProtectedClassPropertyAccess` is set to `true`: | ||
@@ -64,2 +67,15 @@ ```ts | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/dot-notation.md)</sup> | ||
### `allowIndexSignaturePropertyAccess` | ||
Example of correct code when `allowIndexSignaturePropertyAccess` is set to `true`: | ||
```ts | ||
class X { | ||
[key: string]: number; | ||
} | ||
const x = new X(); | ||
x['hello'] = 123; | ||
``` | ||
If the TypeScript compiler option `noPropertyAccessFromIndexSignature` is set to `true`, then the above code is always allowed, even if `allowIndexSignaturePropertyAccess` is `false`. |
@@ -1,14 +0,23 @@ | ||
# Require explicit return types on functions and class methods (`explicit-function-return-type`) | ||
--- | ||
description: 'Require explicit return types on functions and class methods.' | ||
--- | ||
Explicit types for function return values makes it clear to any calling code what type is returned. | ||
This ensures that the return value is assigned to a variable of the correct type; or in the case | ||
where there is no return value, that the calling code doesn't try to use the undefined value when it | ||
shouldn't. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/explicit-function-return-type** for documentation. | ||
## Rule Details | ||
Functions in TypeScript often don't need to be given an explicit return type annotation. | ||
Leaving off the return type is less code to read or write and allows the compiler to infer it from the contents of the function. | ||
This rule aims to ensure that the values returned from functions are of the expected type. | ||
However, explicit return types do make it visually more clear what type is returned by a function. | ||
They can also speed up TypeScript type checking performance in large codebases with many large functions. | ||
The following patterns are considered warnings: | ||
This rule enforces that functions do have an explicit return type annotation. | ||
## Examples | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -36,3 +45,3 @@ // Should indicate that no value is returned (void) | ||
The following patterns are not warnings: | ||
### ✅ Correct | ||
@@ -63,30 +72,5 @@ ```ts | ||
The rule accepts an options object with the following properties: | ||
```ts | ||
type Options = { | ||
// if true, only functions which are part of a declaration will be checked | ||
allowExpressions?: boolean; | ||
// if true, type annotations are also allowed on the variable of a function expression rather than on the function directly | ||
allowTypedFunctionExpressions?: boolean; | ||
// if true, functions immediately returning another function expression will not be checked | ||
allowHigherOrderFunctions?: boolean; | ||
// if true, arrow functions immediately returning a `as const` value will not be checked | ||
allowDirectConstAssertionInArrowFunctions?: boolean; | ||
// if true, concise arrow functions that start with the void keyword will not be checked | ||
allowConciseArrowFunctionExpressionsStartingWithVoid?: boolean; | ||
}; | ||
const defaults = { | ||
allowExpressions: false, | ||
allowTypedFunctionExpressions: true, | ||
allowHigherOrderFunctions: true, | ||
allowDirectConstAssertionInArrowFunctions: true, | ||
allowConciseArrowFunctionExpressionsStartingWithVoid: false, | ||
}; | ||
``` | ||
### Configuring in a mixed JS/TS codebase | ||
If you are working on a codebase within which you lint non-TypeScript code (i.e. `.js`/`.jsx`), you should ensure that you should use [ESLint `overrides`](https://eslint.org/docs/user-guide/configuring#disabling-rules-only-for-a-group-of-files) to only enable the rule on `.ts`/`.tsx` files. If you don't, then you will get unfixable lint errors reported within `.js`/`.jsx` files. | ||
If you are working on a codebase within which you lint non-TypeScript code (i.e. `.js`/`.mjs`/`.cjs`/`.jsx`), you should ensure that you should use [ESLint `overrides`](https://eslint.org/docs/user-guide/configuring#disabling-rules-only-for-a-group-of-files) to only enable the rule on `.ts`/`.mts`/`.cts`/`.tsx` files. If you don't, then you will get unfixable lint errors reported within `.js`/`.mjs`/`.cjs`/`.jsx` files. | ||
@@ -102,5 +86,5 @@ ```jsonc | ||
// enable the rule specifically for TypeScript files | ||
"files": ["*.ts", "*.tsx"], | ||
"files": ["*.ts", "*.mts", "*.cts", "*.tsx"], | ||
"rules": { | ||
"@typescript-eslint/explicit-function-return-type": ["error"] | ||
"@typescript-eslint/explicit-function-return-type": "error" | ||
} | ||
@@ -114,4 +98,8 @@ } | ||
Examples of **incorrect** code for this rule with `{ allowExpressions: true }`: | ||
Examples of code for this rule with `{ allowExpressions: true }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -125,3 +113,3 @@ function test() {} | ||
Examples of **correct** code for this rule with `{ allowExpressions: true }`: | ||
#### ✅ Correct | ||
@@ -138,4 +126,8 @@ ```ts | ||
Examples of **incorrect** code for this rule with `{ allowTypedFunctionExpressions: true }`: | ||
Examples of code for this rule with `{ allowTypedFunctionExpressions: true }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -153,3 +145,3 @@ let arrowFn = () => 'test'; | ||
Examples of additional **correct** code for this rule with `{ allowTypedFunctionExpressions: true }`: | ||
#### ✅ Correct | ||
@@ -194,4 +186,8 @@ ```ts | ||
Examples of **incorrect** code for this rule with `{ allowHigherOrderFunctions: true }`: | ||
Examples of code for this rule with `{ allowHigherOrderFunctions: true }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -205,3 +201,3 @@ var arrowFn = () => () => {}; | ||
Examples of **correct** code for this rule with `{ allowHigherOrderFunctions: true }`: | ||
#### ✅ Correct | ||
@@ -218,4 +214,8 @@ ```ts | ||
Examples of **incorrect** code for this rule with `{ allowDirectConstAssertionInArrowFunctions: true }`: | ||
Examples of code for this rule with `{ allowDirectConstAssertionInArrowFunctions: true }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -226,3 +226,3 @@ const func = (value: number) => ({ type: 'X', value } as any); | ||
Examples of **correct** code for this rule with `{ allowDirectConstAssertionInArrowFunctions: true }`: | ||
#### ✅ Correct | ||
@@ -236,4 +236,8 @@ ```ts | ||
Examples of **incorrect** code for this rule with `{ allowConciseArrowFunctionExpressionsStartingWithVoid: true }`: | ||
Examples of code for this rule with `{ allowConciseArrowFunctionExpressionsStartingWithVoid: true }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -247,3 +251,3 @@ var join = (a: string, b: string) => `${a}${b}`; | ||
Examples of **correct** code for this rule with `{ allowConciseArrowFunctionExpressionsStartingWithVoid: true }`: | ||
#### ✅ Correct | ||
@@ -254,2 +258,69 @@ ```ts | ||
### `allowFunctionsWithoutTypeParameters` | ||
Examples of code for this rule with `{ allowFunctionsWithoutTypeParameters: true }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
function foo<T>(t: T) { | ||
return t; | ||
} | ||
const bar = <T>(t: T) => t; | ||
``` | ||
#### ✅ Correct | ||
```ts | ||
function foo<T>(t: T): T { | ||
return t; | ||
} | ||
const bar = <T>(t: T): T => t; | ||
const allowedFunction(x: string) { | ||
return x; | ||
} | ||
const allowedArrow = (x: string) => x; | ||
``` | ||
### `allowedNames` | ||
You may pass function/method names you would like this rule to ignore, like so: | ||
```json | ||
{ | ||
"@typescript-eslint/explicit-function-return-type": [ | ||
"error", | ||
{ | ||
"allowedNames": ["ignoredFunctionName", "ignoredMethodName"] | ||
} | ||
] | ||
} | ||
``` | ||
### `allowIIFE` | ||
Examples of code for this rule with `{ allowIIFE: true }`: | ||
#### ❌ Incorrect | ||
```ts | ||
var func = () => 'foo'; | ||
``` | ||
#### ✅ Correct | ||
```ts | ||
var foo = (() => 'foo')(); | ||
var bar = (function () { | ||
return 'bar'; | ||
})(); | ||
``` | ||
## When Not To Use It | ||
@@ -256,0 +327,0 @@ |
@@ -1,10 +0,20 @@ | ||
# Require explicit accessibility modifiers on class properties and methods (`explicit-member-accessibility`) | ||
--- | ||
description: 'Require explicit accessibility modifiers on class properties and methods.' | ||
--- | ||
Leaving off accessibility modifier and making everything public can make | ||
your interface hard to use by others. | ||
If you make all internal pieces private or protected, your interface will | ||
be easier to use. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/explicit-member-accessibility** for documentation. | ||
## Rule Details | ||
TypeScript allows placing explicit `public`, `protected`, and `private` accessibility modifiers in front of class members. | ||
The modifiers exist solely in the type system and just server to describe who is allowed to access those members. | ||
Leaving off accessibility modifiers makes for less code to read and write. | ||
Members are `public` by default. | ||
However, adding in explicit accessibility modifiers can be helpful in codebases with many classes for enforcing proper privacy of members. | ||
Some developers also find it preferable for code readability to keep member publicity explicit. | ||
## Examples | ||
This rule aims to make code more readable and explicit about who can use | ||
@@ -15,28 +25,5 @@ which properties. | ||
```ts | ||
type AccessibilityLevel = | ||
| 'explicit' // require an accessor (including public) | ||
| 'no-public' // don't require public | ||
| 'off'; // don't check | ||
type Options = { | ||
accessibility?: AccessibilityLevel; | ||
ignoredMethodNames?: string[]; | ||
overrides?: { | ||
accessors?: AccessibilityLevel; | ||
constructors?: AccessibilityLevel; | ||
methods?: AccessibilityLevel; | ||
properties?: AccessibilityLevel; | ||
parameterProperties?: AccessibilityLevel; | ||
}; | ||
}; | ||
const defaultOptions: Options = { | ||
accessibility: 'explicit', | ||
}; | ||
``` | ||
### Configuring in a mixed JS/TS codebase | ||
If you are working on a codebase within which you lint non-TypeScript code (i.e. `.js`/`.jsx`), you should ensure that you should use [ESLint `overrides`](https://eslint.org/docs/user-guide/configuring#disabling-rules-only-for-a-group-of-files) to only enable the rule on `.ts`/`.tsx` files. If you don't, then you will get unfixable lint errors reported within `.js`/`.jsx` files. | ||
If you are working on a codebase within which you lint non-TypeScript code (i.e. `.js`/`.mjs`/`.cjs`/`.jsx`), you should ensure that you should use [ESLint `overrides`](https://eslint.org/docs/user-guide/configuring#disabling-rules-only-for-a-group-of-files) to only enable the rule on `.ts`/`.mts`/`.cts`/`.tsx` files. If you don't, then you will get unfixable lint errors reported within `.js`/`.mjs`/`.cjs`/`.jsx` files. | ||
@@ -52,5 +39,5 @@ ```jsonc | ||
// enable the rule specifically for TypeScript files | ||
"files": ["*.ts", "*.tsx"], | ||
"files": ["*.ts", "*.mts", "*.cts", "*.tsx"], | ||
"rules": { | ||
"@typescript-eslint/explicit-member-accessibility": ["error"] | ||
"@typescript-eslint/explicit-member-accessibility": "error" | ||
} | ||
@@ -346,6 +333,2 @@ } | ||
- TypeScript [Accessibility Modifiers](https://www.typescriptlang.org/docs/handbook/classes.html#public-private-and-protected-modifiers) | ||
## Compatibility | ||
- TSLint: [member-access](http://palantir.github.io/tslint/rules/member-access/) | ||
- TypeScript [Accessibility Modifiers Handbook Docs](https://www.typescriptlang.org/docs/handbook/2/classes.html#member-visibility) |
@@ -1,13 +0,19 @@ | ||
# Require explicit return and argument types on exported functions' and classes' public class methods (`explicit-module-boundary-types`) | ||
--- | ||
description: "Require explicit return and argument types on exported functions' and classes' public class methods." | ||
--- | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/explicit-module-boundary-types** for documentation. | ||
Explicit types for function return values and arguments makes it clear to any calling code what is the module boundary's input and output. | ||
Adding explicit type annotations for those types can help improve code readability. | ||
It can also improve TypeScript type checking performance on larger codebases. | ||
Consider using this rule in place of [`no-untyped-public-signature`](./no-untyped-public-signature.md) which has been deprecated. | ||
## Examples | ||
## Rule Details | ||
<!--tabs--> | ||
This rule aims to ensure that the values returned from a module are of the expected type. | ||
### ❌ Incorrect | ||
The following patterns are considered warnings: | ||
```ts | ||
@@ -39,3 +45,3 @@ // Should indicate that no value is returned (void) | ||
The following patterns are not warnings: | ||
### ✅ Correct | ||
@@ -54,3 +60,3 @@ ```ts | ||
// A return value of type string | ||
export var arrowFn = (arg: string): string => `test ${arg}`; | ||
export var arrowFn = (): string => 'test'; | ||
@@ -71,45 +77,5 @@ // All arguments should be typed | ||
The rule accepts an options object with the following properties: | ||
```ts | ||
type Options = { | ||
/** | ||
* If true, the rule will not report for arguments that are explicitly typed as `any` | ||
*/ | ||
allowArgumentsExplicitlyTypedAsAny?: boolean; | ||
/** | ||
* If true, body-less arrow functions that return an `as const` type assertion will not | ||
* require an explicit return value annotation. | ||
* You must still type the parameters of the function. | ||
*/ | ||
allowDirectConstAssertionInArrowFunctions?: boolean; | ||
/** | ||
* An array of function/method names that will not have their arguments or their return values checked. | ||
*/ | ||
allowedNames?: string[]; | ||
/** | ||
* If true, functions immediately returning another function expression will not | ||
* require an explicit return value annotation. | ||
* You must still type the parameters of the function. | ||
*/ | ||
allowHigherOrderFunctions?: boolean; | ||
/** | ||
* If true, type annotations are also allowed on the variable of a function expression | ||
* rather than on the function arguments/return value directly. | ||
*/ | ||
allowTypedFunctionExpressions?: boolean; | ||
}; | ||
const defaults = { | ||
allowArgumentsExplicitlyTypedAsAny: false, | ||
allowDirectConstAssertionInArrowFunctions: true, | ||
allowedNames: [], | ||
allowHigherOrderFunctions: true, | ||
allowTypedFunctionExpressions: true, | ||
}; | ||
``` | ||
### Configuring in a mixed JS/TS codebase | ||
If you are working on a codebase within which you lint non-TypeScript code (i.e. `.js`/`.jsx`), you should ensure that you should use [ESLint `overrides`](https://eslint.org/docs/user-guide/configuring#disabling-rules-only-for-a-group-of-files) to only enable the rule on `.ts`/`.tsx` files. If you don't, then you will get unfixable lint errors reported within `.js`/`.jsx` files. | ||
If you are working on a codebase within which you lint non-TypeScript code (i.e. `.js`/`.mjs`/`.cjs`/`.jsx`), you should ensure that you should use [ESLint `overrides`](https://eslint.org/docs/user-guide/configuring#disabling-rules-only-for-a-group-of-files) to only enable the rule on `.ts`/`.mts`/`.cts`/`.tsx` files. If you don't, then you will get unfixable lint errors reported within `.js`/`.mjs`/`.cjs`/`.jsx` files. | ||
@@ -125,5 +91,5 @@ ```jsonc | ||
// enable the rule specifically for TypeScript files | ||
"files": ["*.ts", "*.tsx"], | ||
"files": ["*.ts", "*.mts", "*.cts", "*.tsx"], | ||
"rules": { | ||
"@typescript-eslint/explicit-module-boundary-types": ["error"] | ||
"@typescript-eslint/explicit-module-boundary-types": "error" | ||
} | ||
@@ -137,4 +103,8 @@ } | ||
Examples of **incorrect** code for this rule with `{ allowArgumentsExplicitlyTypedAsAny: false }`: | ||
Examples of code for this rule with `{ allowArgumentsExplicitlyTypedAsAny: false }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -144,3 +114,3 @@ export const func = (value: any): number => value + 1; | ||
Examples of **correct** code for this rule with `{ allowArgumentsExplicitlyTypedAsAny: true }`: | ||
#### ✅ Correct | ||
@@ -153,14 +123,17 @@ ```ts | ||
Examples of **incorrect** code for this rule with `{ allowDirectConstAssertionInArrowFunctions: false }`: | ||
Examples of code for this rule with `{ allowDirectConstAssertionInArrowFunctions: false }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
export const func = (value: number) => ({ type: 'X', value }); | ||
export const foo = () => | ||
({ | ||
bar: true, | ||
} as const); | ||
export const foo = () => ({ | ||
bar: true, | ||
}); | ||
export const bar = () => 1; | ||
``` | ||
Examples of **correct** code for this rule with `{ allowDirectConstAssertionInArrowFunctions: true }`: | ||
#### ✅ Correct | ||
@@ -193,17 +166,21 @@ ```ts | ||
Examples of **incorrect** code for this rule with `{ allowHigherOrderFunctions: false }`: | ||
Examples of code for this rule with `{ allowHigherOrderFunctions: false }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
export const arrowFn = () => (): void => {}; | ||
export const arrowFn = () => () => {}; | ||
export function fn() { | ||
return function (): void {}; | ||
return function () {}; | ||
} | ||
export function foo(outer: string) { | ||
return function (inner: string): void {}; | ||
return function (inner: string) {}; | ||
} | ||
``` | ||
Examples of **correct** code for this rule with `{ allowHigherOrderFunctions: true }`: | ||
#### ✅ Correct | ||
@@ -224,4 +201,8 @@ ```ts | ||
Examples of **incorrect** code for this rule with `{ allowTypedFunctionExpressions: false }`: | ||
Examples of code for this rule with `{ allowTypedFunctionExpressions: false }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -241,3 +222,3 @@ export let arrowFn = () => 'test'; | ||
Examples of additional **correct** code for this rule with `{ allowTypedFunctionExpressions: true }`: | ||
#### ✅ Correct | ||
@@ -244,0 +225,0 @@ ```ts |
@@ -1,22 +0,12 @@ | ||
# Require or disallow spacing between function identifiers and their invocations (`func-call-spacing`) | ||
--- | ||
description: 'Require or disallow spacing between function identifiers and their invocations.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/func-call-spacing** for documentation. | ||
## Examples | ||
This rule extends the base [`eslint/func-call-spacing`](https://eslint.org/docs/rules/func-call-spacing) rule. | ||
It adds support for generic type parameters on function calls. | ||
## How to use | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"func-call-spacing": "off", | ||
"@typescript-eslint/func-call-spacing": ["error"] | ||
} | ||
``` | ||
## Options | ||
See [`eslint/func-call-spacing` options](https://eslint.org/docs/rules/func-call-spacing#options). | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/func-call-spacing.md)</sup> |
@@ -1,24 +0,20 @@ | ||
# Enforce consistent indentation (`indent`) | ||
--- | ||
description: 'Enforce consistent indentation.' | ||
--- | ||
## PLEASE READ THIS ISSUE BEFORE USING THIS RULE [#1824](https://github.com/typescript-eslint/typescript-eslint/issues/1824) | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/indent** for documentation. | ||
## Rule Details | ||
## Warning | ||
This rule extends the base [`eslint/indent`](https://eslint.org/docs/rules/indent) rule. | ||
It adds support for TypeScript nodes. | ||
:::warning | ||
## How to use | ||
Please read [Issue #1824: Problems with the indent rule](https://github.com/typescript-eslint/typescript-eslint/issues/1824) before using this rule! | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"indent": "off", | ||
"@typescript-eslint/indent": ["error"] | ||
} | ||
``` | ||
::: | ||
## Options | ||
## Examples | ||
See [`eslint/indent` options](https://eslint.org/docs/rules/indent#options). | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/indent.md)</sup> | ||
This rule extends the base [`eslint/indent`](https://eslint.org/docs/rules/indent) rule. | ||
It adds support for TypeScript nodes. |
@@ -1,22 +0,12 @@ | ||
# require or disallow initialization in variable declarations (`init-declarations`) | ||
--- | ||
description: 'Require or disallow initialization in variable declarations.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/init-declarations** for documentation. | ||
## Examples | ||
This rule extends the base [`eslint/init-declarations`](https://eslint.org/docs/rules/init-declarations) rule. | ||
It adds support for TypeScript's `declare` variables. | ||
## How to use | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"init-declarations": "off", | ||
"@typescript-eslint/init-declarations": ["error"] | ||
} | ||
``` | ||
## Options | ||
See [`eslint/init-declarations` options](https://eslint.org/docs/rules/init-declarations#options). | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/init-declarations.md)</sup> |
@@ -1,22 +0,12 @@ | ||
# Enforce consistent spacing before and after keywords (`keyword-spacing`) | ||
--- | ||
description: 'Enforce consistent spacing before and after keywords.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/keyword-spacing** for documentation. | ||
## Examples | ||
This rule extends the base [`eslint/keyword-spacing`](https://eslint.org/docs/rules/keyword-spacing) rule. | ||
This version adds support for generic type parameters on function calls. | ||
## How to use | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"keyword-spacing": "off", | ||
"@typescript-eslint/keyword-spacing": ["error"] | ||
} | ||
``` | ||
## Options | ||
See [`eslint/keyword-spacing` options](https://eslint.org/docs/rules/keyword-spacing#options). | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/keyword-spacing.md)</sup> |
@@ -1,28 +0,20 @@ | ||
# Require or disallow an empty line between class members (`lines-between-class-members`) | ||
--- | ||
description: 'Require or disallow an empty line between class members.' | ||
--- | ||
This rule improves readability by enforcing lines between class members. It will not check empty lines before the first member and after the last member. This rule require or disallow an empty line between class members. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/lines-between-class-members** for documentation. | ||
## Rule Details | ||
This rule improves readability by enforcing lines between class members. It will not check empty lines before the first member and after the last member. This rule will require or disallow an empty line between class members. | ||
## Examples | ||
This rule extends the base [`eslint/lines-between-class-members`](https://eslint.org/docs/rules/lines-between-class-members) rule. | ||
It adds support for ignoring overload methods in a class. | ||
See the [ESLint documentation](https://eslint.org/docs/rules/lines-between-class-members) for more details on the `lines-between-class-members` rule. | ||
## Options | ||
## Rule Changes | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"lines-between-class-members": "off", | ||
"@typescript-eslint/lines-between-class-members": ["error"] | ||
} | ||
``` | ||
In addition to the options supported by the `lines-between-class-members` rule in ESLint core, the rule adds the following options: | ||
## Options | ||
This rule has a string option and an object option. | ||
- Object option: | ||
@@ -33,3 +25,3 @@ | ||
- [See the other options allowed](https://github.com/eslint/eslint/blob/master/docs/rules/lines-between-class-members.md#options) | ||
- [See the other options allowed](https://github.com/eslint/eslint/blob/main/docs/rules/lines-between-class-members.md#options) | ||
@@ -73,3 +65,1 @@ ### `exceptAfterOverload: true` | ||
``` | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/lines-between-class-members.md)</sup> |
@@ -1,6 +0,10 @@ | ||
# Require a specific member delimiter style for interfaces and type literals (`member-delimiter-style`) | ||
--- | ||
description: 'Require a specific member delimiter style for interfaces and type literals.' | ||
--- | ||
Enforces a consistent member delimiter style in interfaces and type literals. There are three member delimiter styles primarily used in TypeScript: | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/member-delimiter-style** for documentation. | ||
- Semicolon style (default, preferred in TypeScript). | ||
TypeScript allows three delimiters between members in interfaces and type aliases: | ||
@@ -10,82 +14,33 @@ <!-- prettier-ignore --> | ||
interface Foo { | ||
// Semicolons (default, preferred in TypeScript): | ||
name: string; | ||
greet(): void; | ||
} | ||
type Bar = { | ||
name: string; | ||
greet(): void; | ||
} | ||
``` | ||
- Comma style (JSON style). | ||
<!-- prettier-ignore --> | ||
```ts | ||
interface Foo { | ||
// Commas (JSON-like): | ||
name: string, | ||
greet(): void, | ||
} | ||
type Bar = { | ||
name: string, | ||
greet(): void, | ||
} | ||
``` | ||
- Line break (none) style. | ||
<!-- prettier-ignore --> | ||
```ts | ||
interface Foo { | ||
// Line breaks (none): | ||
name: string | ||
greet(): void | ||
} | ||
type Bar = { | ||
name: string | ||
greet(): void | ||
} | ||
``` | ||
The rule also enforces the presence (or absence) of the delimiter in the last member of the interface and/or type literal. | ||
Finally, this rule can enforce separate delimiter syntax for single line declarations. | ||
For code readability, it's generally best to use the same style consistently in your codebase. | ||
## Rule Details | ||
This rule enforces keeping to one configurable code style. | ||
It can also standardize the presence (or absence) of a delimiter in the last member of a construct, as well as a separate delimiter syntax for single line declarations. | ||
This rule aims to standardize the way interface and type literal members are delimited. | ||
## Options | ||
```ts | ||
interface BaseConfig { | ||
multiline?: { | ||
delimiter?: 'none' | 'semi' | 'comma'; | ||
requireLast?: boolean; | ||
}; | ||
singleline?: { | ||
delimiter?: 'semi' | 'comma'; | ||
requireLast?: boolean; | ||
}; | ||
} | ||
type Config = BaseConfig & { | ||
overrides?: { | ||
interface?: BaseConfig; | ||
typeLiteral?: BaseConfig; | ||
}; | ||
}; | ||
``` | ||
Default config: | ||
```JSON | ||
```json | ||
{ | ||
"multiline": { | ||
"delimiter": "semi", | ||
"requireLast": true | ||
}, | ||
"singleline": { | ||
"delimiter": "semi", | ||
"requireLast": false | ||
} | ||
"multiline": { | ||
"delimiter": "semi", | ||
"requireLast": true | ||
}, | ||
"singleline": { | ||
"delimiter": "semi", | ||
"requireLast": false | ||
}, | ||
"multilineDetection": "brackets" | ||
} | ||
@@ -98,2 +53,7 @@ ``` | ||
`multilineDetection` determines what counts as multiline | ||
- `"brackets"` (default) any newlines in the type or interface make it multiline. | ||
- `"last-member"` if the last member of the interface is on the same line as the last bracket, it is counted as a single line. | ||
### `delimiter` | ||
@@ -106,4 +66,7 @@ | ||
- `none` - each member should be delimited with nothing. | ||
- NOTE - this is not an option for `singleline` because having no delimiter between members on a single line is a syntax error in TS. | ||
:::note | ||
`none` is not an option for `singleline` because having no delimiter between members on a single line is a syntax error in TS. | ||
::: | ||
### `requireLast` | ||
@@ -122,20 +85,20 @@ | ||
```JSON | ||
```json | ||
{ | ||
"multiline": { | ||
"delimiter": "comma", | ||
"multiline": { | ||
"delimiter": "comma", | ||
"requireLast": true | ||
}, | ||
"singleline": { | ||
"delimiter": "comma", | ||
"requireLast": true | ||
}, | ||
"overrides": { | ||
"interface": { | ||
"multiline": { | ||
"delimiter": "semi", | ||
"requireLast": true | ||
}, | ||
"singleline": { | ||
"delimiter": "comma", | ||
"requireLast": true | ||
}, | ||
"overrides": { | ||
"interface": { | ||
"multiline": { | ||
"delimiter": "semi", | ||
"requireLast": true | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
@@ -146,4 +109,8 @@ ``` | ||
Examples of **incorrect** code for this rule with the default config: | ||
Examples of code for this rule with the default config: | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
<!-- prettier-ignore --> | ||
@@ -176,3 +143,3 @@ ```ts | ||
Examples of **correct** code for this rule with the default config: | ||
### ✅ Correct | ||
@@ -179,0 +146,0 @@ <!-- prettier-ignore --> |
@@ -1,196 +0,77 @@ | ||
# Require a consistent member declaration order (`member-ordering`) | ||
--- | ||
description: 'Require a consistent member declaration order.' | ||
--- | ||
A consistent ordering of fields, methods and constructors can make interfaces, type literals, classes and class expressions easier to read, navigate and edit. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/member-ordering** for documentation. | ||
## Rule Details | ||
This rule aims to standardize the way classes, interfaces, and type literals are structured and ordered. | ||
A consistent ordering of fields, methods and constructors can make code easier to read, navigate, and edit. | ||
This rule aims to standardize the way class declarations, class expressions, interfaces and type literals are structured and ordered. | ||
### Grouping and sorting member groups | ||
It allows to group members by their type (e.g. `public-static-field`, `protected-static-field`, `private-static-field`, `public-instance-field`, ...) and enforce a certain order for these groups. By default, their order is the same inside `classes`, `classExpressions`, `interfaces` and `typeLiterals` (note: not all member types apply to `interfaces` and `typeLiterals`). It is possible to define the order for any of those individually or to change the default order for all of them by setting the `default` option. | ||
### Sorting members | ||
Besides grouping the members and sorting their groups, this rule also allows to sort the members themselves (e.g. `a`, `b`, `c`, ...). You have 2 options: Sort all of them while ignoring their type or sort them while respecting their types (e.g. sort all fields in an interface alphabetically). | ||
## Options | ||
These options allow to specify how to group the members and sort their groups. | ||
- Sort groups, don't enforce member order: Use `memberTypes` | ||
- Sort members, don't enforce group order: Use `order` | ||
- Sort members within groups: Use `memberTypes` and `order` | ||
```ts | ||
type TypeOptions<T> = | ||
| { | ||
memberTypes: Array<T> | 'never', | ||
order?: 'alphabetically' | 'as-written', | ||
} | ||
| { | ||
order: 'alphabetically', | ||
}; | ||
interface Options { | ||
default?: OrderConfig; | ||
classes?: OrderConfig; | ||
classExpressions?: OrderConfig; | ||
interfaces?: OrderConfig; | ||
typeLiterals?: OrderConfig; | ||
} | ||
{ | ||
default?: TypeOptions<MemberTypes>, | ||
type OrderConfig = MemberType[] | SortedOrderConfig | 'never'; | ||
classes?: TypeOptions<MemberTypes>, | ||
classExpressions?: TypeOptions<MemberTypes>, | ||
interfaces?: TypeOptions<'signature' | 'field' | 'method' | 'constructor'>, | ||
typeLiterals?: TypeOptions<'signature' | 'field' | 'method' | 'constructor'>, | ||
interface SortedOrderConfig { | ||
memberTypes?: MemberType[] | 'never'; | ||
optionalityOrder?: 'optional-first' | 'required-first'; | ||
order: | ||
| 'alphabetically' | ||
| 'alphabetically-case-insensitive' | ||
| 'as-written' | ||
| 'natural' | ||
| 'natural-case-insensitive'; | ||
} | ||
``` | ||
See below for the possible definitions of `MemberType`. | ||
### Deprecated syntax | ||
Note: There is a deprecated syntax to specify the member types as an array. | ||
### Member types (granular form) | ||
There are multiple ways to specify the member types. The most explicit and granular form is the following: | ||
```jsonc | ||
[ | ||
// Index signature | ||
"signature", | ||
// Fields | ||
"public-static-field", | ||
"protected-static-field", | ||
"private-static-field", | ||
"public-decorated-field", | ||
"protected-decorated-field", | ||
"private-decorated-field", | ||
"public-instance-field", | ||
"protected-instance-field", | ||
"private-instance-field", | ||
"public-abstract-field", | ||
"protected-abstract-field", | ||
"private-abstract-field", | ||
// Constructors | ||
"public-constructor", | ||
"protected-constructor", | ||
"private-constructor", | ||
// Methods | ||
"public-static-method", | ||
"protected-static-method", | ||
"private-static-method", | ||
"public-decorated-method", | ||
"protected-decorated-method", | ||
"private-decorated-method", | ||
"public-instance-method", | ||
"protected-instance-method", | ||
"private-instance-method", | ||
"public-abstract-method", | ||
"protected-abstract-method", | ||
"private-abstract-method" | ||
] | ||
// See below for the more specific MemberType strings | ||
type MemberType = string | string[]; | ||
``` | ||
Note: If you only specify some of the possible types, the non-specified ones can have any particular order. This means that they can be placed before, within or after the specified types and the linter won't complain about it. | ||
You can configure `OrderConfig` options for: | ||
### Member group types (with accessibility, ignoring scope) | ||
- **`default`**: all constructs (used as a fallback) | ||
- **`classes`**?: override ordering specifically for classes | ||
- **`classExpressions`**?: override ordering specifically for class expressions | ||
- **`interfaces`**?: override ordering specifically for interfaces | ||
- **`typeLiterals`**?: override ordering specifically for type literals | ||
It is also possible to group member types by their accessibility (`static`, `instance`, `abstract`), ignoring their scope. | ||
The `OrderConfig` settings for each kind of construct may configure sorting on up to three levels: | ||
```jsonc | ||
[ | ||
// Index signature | ||
// No accessibility for index signature. See above. | ||
- **`memberTypes`**: organizing on member type groups such as methods vs. properties | ||
- **`optionalityOrder`**: whether to put all optional members first or all required members first | ||
- **`order`**: organizing based on member names, such as alphabetically | ||
// Fields | ||
"public-field", // = ["public-static-field", "public-instance-field"] | ||
"protected-field", // = ["protected-static-field", "protected-instance-field"] | ||
"private-field", // = ["private-static-field", "private-instance-field"] | ||
### Groups | ||
// Constructors | ||
// Only the accessibility of constructors is configurable. See below. | ||
You can define many different groups based on different attributes of members. | ||
The supported member attributes are, in order: | ||
// Methods | ||
"public-method", // = ["public-static-method", "public-instance-method"] | ||
"protected-method", // = ["protected-static-method", "protected-instance-method"] | ||
"private-method" // = ["private-static-method", "private-instance-method"] | ||
] | ||
``` | ||
- **Accessibility** (`'public' | 'protected' | 'private' | '#private'`) | ||
- **Decoration** (`'decorated'`): Whether the member has an explicit accessibility decorator | ||
- **Kind** (`'call-signature' | 'constructor' | 'field' | 'readonly-field' | 'get' | 'method' | 'set' | 'signature' | 'readonly-signature'`) | ||
### Member group types (with accessibility and a decorator) | ||
Member attributes may be joined with a `'-'` to combine into more specific groups. | ||
For example, `'public-field'` would come before `'private-field'`. | ||
It is also possible to group methods or fields with a decorator separately, optionally specifying | ||
their accessibility. | ||
### Orders | ||
```jsonc | ||
[ | ||
// Index signature | ||
// No decorators for index signature. | ||
The `order` value specifies what order members should be within a group. | ||
It defaults to `as-written`, meaning any order is fine. | ||
Other allowed values are: | ||
// Fields | ||
"public-decorated-field", | ||
"protected-decorated-field", | ||
"private-decorated-field", | ||
- `alphabetically`: Sorted in a-z alphabetical order, directly using string `<` comparison (so `B` comes before `a`) | ||
- `alphabetically-case-insensitive`: Sorted in a-z alphabetical order, ignoring case (so `a` comes before `B`) | ||
- `natural`: Same as `alphabetically`, but using [`natural-compare-lite`](https://github.com/litejs/natural-compare-lite) for more friendly sorting of numbers | ||
- `natural-case-insensitive`: Same as `alphabetically-case-insensitive`, but using [`natural-compare-lite`](https://github.com/litejs/natural-compare-lite) for more friendly sorting of numbers | ||
"decorated-field", // = ["public-decorated-field", "protected-decorated-field", "private-decorated-field"] | ||
// Constructors | ||
// There are no decorators for constructors. | ||
"public-decorated-method", | ||
"protected-decorated-method", | ||
"private-decorated-method", | ||
"decorated-method" // = ["public-decorated-method", "protected-decorated-method", "private-decorated-method"] | ||
] | ||
``` | ||
### Member group types (with scope, ignoring accessibility) | ||
Another option is to group the member types by their scope (`public`, `protected`, `private`), ignoring their accessibility. | ||
```jsonc | ||
[ | ||
// Index signature | ||
// No scope for index signature. See above. | ||
// Fields | ||
"static-field", // = ["public-static-field", "protected-static-field", "private-static-field"] | ||
"instance-field", // = ["public-instance-field", "protected-instance-field", "private-instance-field"] | ||
"abstract-field", // = ["public-abstract-field", "protected-abstract-field", "private-abstract-field"] | ||
// Constructors | ||
"constructor", // = ["public-constructor", "protected-constructor", "private-constructor"] | ||
// Methods | ||
"static-method", // = ["public-static-method", "protected-static-method", "private-static-method"] | ||
"instance-method", // = ["public-instance-method", "protected-instance-method", "private-instance-method"] | ||
"abstract-method" // = ["public-abstract-method", "protected-abstract-method", "private-abstract-method"] | ||
] | ||
``` | ||
### Member group types (with scope and accessibility) | ||
The third grouping option is to ignore both scope and accessibility. | ||
```jsonc | ||
[ | ||
// Index signature | ||
// No grouping for index signature. See above. | ||
// Fields | ||
"field", // = ["public-static-field", "protected-static-field", "private-static-field", "public-instance-field", "protected-instance-field", "private-instance-field", | ||
// "public-abstract-field", "protected-abstract-field", private-abstract-field"] | ||
// Constructors | ||
// Only the accessibility of constructors is configurable. See above. | ||
// Methods | ||
"method" // = ["public-static-method", "protected-static-method", "private-static-method", "public-instance-method", "protected-instance-method", "private-instance-method", | ||
// "public-abstract-method", "protected-abstract-method", "private-abstract-method"] | ||
] | ||
``` | ||
### Default configuration | ||
@@ -205,2 +86,3 @@ | ||
"signature", | ||
"call-signature", | ||
@@ -211,2 +93,3 @@ // Fields | ||
"private-static-field", | ||
"#private-static-field", | ||
@@ -220,6 +103,6 @@ "public-decorated-field", | ||
"private-instance-field", | ||
"#private-instance-field", | ||
"public-abstract-field", | ||
"protected-abstract-field", | ||
"private-abstract-field", | ||
@@ -229,2 +112,3 @@ "public-field", | ||
"private-field", | ||
"#private-field", | ||
@@ -239,2 +123,5 @@ "static-field", | ||
// Static initialization | ||
"static-initialization", | ||
// Constructors | ||
@@ -247,2 +134,64 @@ "public-constructor", | ||
// Getters | ||
"public-static-get", | ||
"protected-static-get", | ||
"private-static-get", | ||
"#private-static-get", | ||
"public-decorated-get", | ||
"protected-decorated-get", | ||
"private-decorated-get", | ||
"public-instance-get", | ||
"protected-instance-get", | ||
"private-instance-get", | ||
"#private-instance-get", | ||
"public-abstract-get", | ||
"protected-abstract-get", | ||
"public-get", | ||
"protected-get", | ||
"private-get", | ||
"#private-get", | ||
"static-get", | ||
"instance-get", | ||
"abstract-get", | ||
"decorated-get", | ||
"get", | ||
// Setters | ||
"public-static-set", | ||
"protected-static-set", | ||
"private-static-set", | ||
"#private-static-set", | ||
"public-decorated-set", | ||
"protected-decorated-set", | ||
"private-decorated-set", | ||
"public-instance-set", | ||
"protected-instance-set", | ||
"private-instance-set", | ||
"#private-instance-set", | ||
"public-abstract-set", | ||
"protected-abstract-set", | ||
"public-set", | ||
"protected-set", | ||
"private-set", | ||
"#private-set", | ||
"static-set", | ||
"instance-set", | ||
"abstract-set", | ||
"decorated-set", | ||
"set", | ||
// Methods | ||
@@ -252,2 +201,3 @@ "public-static-method", | ||
"private-static-method", | ||
"#private-static-method", | ||
@@ -261,6 +211,6 @@ "public-decorated-method", | ||
"private-instance-method", | ||
"#private-instance-method", | ||
"public-abstract-method", | ||
"protected-abstract-method", | ||
"private-abstract-method", | ||
@@ -270,2 +220,3 @@ "public-method", | ||
"private-method", | ||
"#private-method", | ||
@@ -283,16 +234,36 @@ "static-method", | ||
Note: The default configuration contains member group types which contain other member types (see above). This is intentional to provide better error messages. | ||
:::note | ||
The default configuration contains member group types which contain other member types. | ||
This is intentional to provide better error messages. | ||
::: | ||
Note: By default, the members are not sorted. If you want to sort them alphabetically, you have to provide a custom configuration. | ||
:::tip | ||
By default, the members are not sorted. | ||
If you want to sort them alphabetically, you have to provide a custom configuration. | ||
::: | ||
## Examples | ||
### Custom `default` configuration | ||
### General Order on All Constructs | ||
Note: The `default` options are overwritten in these examples. | ||
This config specifies the order for all constructs. | ||
It ignores member types other than signatures, methods, constructors, and fields. | ||
It also ignores accessibility and scope. | ||
#### Configuration: `{ "default": ["signature", "method", "constructor", "field"] }` | ||
```jsonc | ||
// .eslintrc.json | ||
{ | ||
"rules": { | ||
"@typescript-eslint/member-ordering": [ | ||
"error", | ||
{ "default": ["signature", "method", "constructor", "field"] } | ||
] | ||
} | ||
} | ||
``` | ||
##### Incorrect examples | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -310,4 +281,2 @@ interface Foo { | ||
Note: Wrong order. | ||
```ts | ||
@@ -325,4 +294,2 @@ type Foo = { | ||
Note: Not all specified member types have to exist. | ||
```ts | ||
@@ -343,4 +310,2 @@ class Foo { | ||
Note: Accessibility or scope are ignored with this configuration. | ||
```ts | ||
@@ -362,6 +327,4 @@ const Foo = class { | ||
Note: Not all members have to be grouped to find rule violations. | ||
#### ✅ Correct | ||
##### Correct examples | ||
```ts | ||
@@ -421,8 +384,26 @@ interface Foo { | ||
#### Configuration: `{ "default": ["public-instance-method", "public-static-field"] }` | ||
### Classes | ||
Note: This configuration does not apply to interfaces/type literals as accessibility and scope are not part of interfaces/type literals. | ||
#### Public Instance Methods Before Public Static Fields | ||
##### Incorrect examples | ||
This config specifies that public instance methods should come first before public static fields. | ||
Everything else can be placed anywhere. | ||
It doesn't apply to interfaces or type literals as accessibility and scope are not part of them. | ||
```jsonc | ||
// .eslintrc.json | ||
{ | ||
"rules": { | ||
"@typescript-eslint/member-ordering": [ | ||
"error", | ||
{ "default": ["public-instance-method", "public-static-field"] } | ||
] | ||
} | ||
} | ||
``` | ||
<!--tabs--> | ||
##### ❌ Incorrect | ||
```ts | ||
@@ -446,4 +427,2 @@ class Foo { | ||
Note: Public instance methods should come first before public static fields. Everything else can be placed anywhere. | ||
```ts | ||
@@ -467,6 +446,4 @@ const Foo = class { | ||
Note: Public instance methods should come first before public static fields. Everything else can be placed anywhere. | ||
##### ✅ Correct | ||
##### Correct examples | ||
```ts | ||
@@ -508,8 +485,22 @@ class Foo { | ||
#### Configuration: `{ "default": ["public-static-field", "static-field", "instance-field"] }` | ||
#### Static Fields Before Instance Fields | ||
Note: This configuration does not apply to interfaces/type literals as accessibility and scope are not part of interfaces/type literals. | ||
This config specifies that static fields should come before instance fields, with public static fields first. | ||
It doesn't apply to interfaces or type literals as accessibility and scope are not part of them. | ||
##### Incorrect examples | ||
```jsonc | ||
{ | ||
"rules": { | ||
"@typescript-eslint/member-ordering": [ | ||
"error", | ||
{ "default": ["public-static-field", "static-field", "instance-field"] } | ||
] | ||
} | ||
} | ||
``` | ||
<!--tabs--> | ||
##### ❌ Incorrect | ||
```ts | ||
@@ -529,11 +520,9 @@ class Foo { | ||
Note: Public static fields should come first, followed by static fields and instance fields. | ||
```ts | ||
const foo = class { | ||
public T(): void {} // (irrelevant) | ||
public T(): void {} // method (irrelevant) | ||
private static B: string; // -> static field | ||
constructor() {} // (irrelevant) | ||
constructor() {} // constructor (irrelevant) | ||
@@ -545,3 +534,3 @@ private E: string; // -> instance field | ||
[Z: string]: any; // (irrelevant) | ||
[Z: string]: any; // signature (irrelevant) | ||
@@ -552,6 +541,4 @@ public static A: string; // -> public static field | ||
Note: Public static fields should come first, followed by static fields and instance fields. | ||
##### ✅ Correct | ||
##### Correct examples | ||
```ts | ||
@@ -566,2 +553,4 @@ class Foo { | ||
private E: string; // -> instance field | ||
[Z: string]: any; // (irrelevant) | ||
} | ||
@@ -572,7 +561,7 @@ ``` | ||
const foo = class { | ||
[Z: string]: any; // -> signature | ||
[Z: string]: any; // -> signature (irrelevant) | ||
public static A: string; // -> public static field | ||
constructor() {} // -> constructor | ||
constructor() {} // -> constructor (irrelevant) | ||
@@ -585,15 +574,27 @@ private static B: string; // -> static field | ||
public T(): void {} // -> method | ||
public T(): void {} // -> method (irrelevant) | ||
}; | ||
``` | ||
### Custom `classes` configuration | ||
#### Class Declarations | ||
Note: If this is not set, the `default` will automatically be applied to classes as well. If a `classes` configuration is provided, only this configuration will be used for `classes` (i.e. nothing will be merged with `default`). | ||
This config only specifies an order for classes: methods, then the constructor, then fields. | ||
It does not apply to class expressions (use `classExpressions` for them). | ||
Default settings will be used for class declarations and all other syntax constructs other than class declarations. | ||
Note: The configuration for `classes` does not apply to class expressions (use `classExpressions` for them). | ||
```jsonc | ||
// .eslintrc.json | ||
{ | ||
"rules": { | ||
"@typescript-eslint/member-ordering": [ | ||
"error", | ||
{ "classes": ["method", "constructor", "field"] } | ||
] | ||
} | ||
} | ||
``` | ||
#### Configuration: `{ "classes": ["method", "constructor", "field"] }` | ||
<!--tabs--> | ||
##### Incorrect example | ||
##### ❌ Incorrect | ||
@@ -613,3 +614,3 @@ ```ts | ||
##### Correct example | ||
##### ✅ Correct | ||
@@ -629,50 +630,24 @@ ```ts | ||
#### Configuration: `{ "classes": ["public-instance-method", "public-static-field"] }` | ||
#### Class Expressions | ||
##### Incorrect example | ||
This config only specifies an order for classes expressions: methods, then the constructor, then fields. | ||
It does not apply to class declarations (use `classes` for them). | ||
Default settings will be used for class declarations and all other syntax constructs other than class expressions. | ||
```ts | ||
class Foo { | ||
private C: string; // (irrelevant) | ||
public D: string; // (irrelevant) | ||
public static E: string; // -> public static field | ||
constructor() {} // (irrelevant) | ||
public static A(): void {} // (irrelevant) | ||
public B(): void {} // -> public instance method | ||
```jsonc | ||
// .eslintrc.json | ||
{ | ||
"rules": { | ||
"@typescript-eslint/member-ordering": [ | ||
"error", | ||
{ "classExpressions": ["method", "constructor", "field"] } | ||
] | ||
} | ||
} | ||
``` | ||
##### Correct example | ||
<!--tabs--> | ||
```ts | ||
class Foo { | ||
private C: string; // (irrelevant) | ||
##### ❌ Incorrect | ||
public D: string; // (irrelevant) | ||
public B(): void {} // -> public instance method | ||
constructor() {} // (irrelevant) | ||
public static A(): void {} // (irrelevant) | ||
public static E: string; // -> public static field | ||
} | ||
``` | ||
### Custom `classExpressions` configuration | ||
Note: If this is not set, the `default` will automatically be applied to classes expressions as well. If a `classExpressions` configuration is provided, only this configuration will be used for `classExpressions` (i.e. nothing will be merged with `default`). | ||
Note: The configuration for `classExpressions` does not apply to classes (use `classes` for them). | ||
#### Configuration: `{ "classExpressions": ["method", "constructor", "field"] }` | ||
##### Incorrect example | ||
```ts | ||
@@ -691,3 +666,3 @@ const foo = class { | ||
##### Correct example | ||
##### ✅ Correct | ||
@@ -707,52 +682,28 @@ ```ts | ||
#### Configuration: `{ "classExpressions": ["public-instance-method", "public-static-field"] }` | ||
### Interfaces | ||
##### Incorrect example | ||
This config only specifies an order for interfaces: signatures, then methods, then constructors, then fields. | ||
It does not apply to type literals (use `typeLiterals` for them). | ||
Default settings will be used for type literals and all other syntax constructs other than class expressions. | ||
```ts | ||
const foo = class { | ||
private C: string; // (irrelevant) | ||
:::note | ||
These member types are the only ones allowed for `interfaces`. | ||
::: | ||
public D: string; // (irrelevant) | ||
public static E: string; // -> public static field | ||
constructor() {} // (irrelevant) | ||
public static A(): void {} // (irrelevant) | ||
public B(): void {} // -> public instance method | ||
}; | ||
```jsonc | ||
// .eslintrc.json | ||
{ | ||
"rules": { | ||
"@typescript-eslint/member-ordering": [ | ||
"error", | ||
{ "interfaces": ["signature", "method", "constructor", "field"] } | ||
] | ||
} | ||
} | ||
``` | ||
##### Correct example | ||
<!--tabs--> | ||
```ts | ||
const foo = class { | ||
private C: string; // (irrelevant) | ||
#### ❌ Incorrect | ||
public D: string; // (irrelevant) | ||
public B(): void {} // -> public instance method | ||
public static E: string; // -> public static field | ||
constructor() {} // (irrelevant) | ||
public static A(): void {} // (irrelevant) | ||
}; | ||
``` | ||
### Custom `interfaces` configuration | ||
Note: If this is not set, the `default` will automatically be applied to classes expressions as well. If a `interfaces` configuration is provided, only this configuration will be used for `interfaces` (i.e. nothing will be merged with `default`). | ||
Note: The configuration for `interfaces` only allows a limited set of member types: `signature`, `field`, `constructor` and `method`. | ||
Note: The configuration for `interfaces` does not apply to type literals (use `typeLiterals` for them). | ||
#### Configuration: `{ "interfaces": ["signature", "method", "constructor", "field"] }` | ||
##### Incorrect example | ||
```ts | ||
@@ -770,3 +721,3 @@ interface Foo { | ||
##### Correct example | ||
#### ✅ Correct | ||
@@ -785,13 +736,27 @@ ```ts | ||
### Custom `typeLiterals` configuration | ||
### Type Literals | ||
Note: If this is not set, the `default` will automatically be applied to classes expressions as well. If a `typeLiterals` configuration is provided, only this configuration will be used for `typeLiterals` (i.e. nothing will be merged with `default`). | ||
This config only specifies an order for type literals: signatures, then methods, then constructors, then fields. | ||
It does not apply to interfaces (use `interfaces` for them). | ||
Default settings will be used for interfaces and all other syntax constructs other than class expressions. | ||
Note: The configuration for `typeLiterals` only allows a limited set of member types: `signature`, `field`, `constructor` and `method`. | ||
:::note | ||
These member types are the only ones allowed for `typeLiterals`. | ||
::: | ||
Note: The configuration for `typeLiterals` does not apply to interfaces (use `interfaces` for them). | ||
```jsonc | ||
// .eslintrc.json | ||
{ | ||
"rules": { | ||
"@typescript-eslint/member-ordering": [ | ||
"error", | ||
{ "typeLiterals": ["signature", "method", "constructor", "field"] } | ||
] | ||
} | ||
} | ||
``` | ||
#### Configuration: `{ "typeLiterals": ["signature", "method", "constructor", "field"] }` | ||
<!--tabs--> | ||
##### Incorrect example | ||
#### ❌ Incorrect | ||
@@ -810,3 +775,3 @@ ```ts | ||
##### Correct example | ||
#### ✅ Correct | ||
@@ -825,44 +790,94 @@ ```ts | ||
### Sorting alphabetically within member groups | ||
### Sorting Options | ||
It is possible to sort all members within a group alphabetically. | ||
#### Sorting Alphabetically Within Member Groups | ||
#### Configuration: `{ "default": { "memberTypes": <Default Order>, "order": "alphabetically" } }` | ||
This config specifies that within each `memberTypes` group, members are in an alphabetic case-sensitive order. | ||
You can copy and paste the default order from [Default Configuration](#default-configuration). | ||
This will apply the default order (see above) and enforce an alphabetic order within each group. | ||
```jsonc | ||
// .eslintrc.json | ||
{ | ||
"rules": { | ||
"@typescript-eslint/member-ordering": [ | ||
"error", | ||
{ | ||
"default": { | ||
"memberTypes": [ | ||
/* <Default Order> */ | ||
], | ||
"order": "alphabetically" | ||
} | ||
} | ||
] | ||
} | ||
} | ||
``` | ||
##### Incorrect examples | ||
<!--tabs--> | ||
##### ❌ Incorrect | ||
```ts | ||
interface Foo { | ||
a: x; | ||
b: x; | ||
B: x; | ||
c: x; | ||
new (): Bar; | ||
(): Baz; | ||
B(): void; | ||
c(): void; | ||
a(): void; | ||
} | ||
``` | ||
##### ✅ Correct | ||
```ts | ||
interface Foo { | ||
B: x; | ||
a: x; | ||
c: x; | ||
B(): void; | ||
a(): void; | ||
b(): void; | ||
c(): void; | ||
} | ||
``` | ||
// Wrong group order, should be placed before all field definitions | ||
[a: string]: number; | ||
#### Sorting Alphabetically Case Insensitive Within Member Groups | ||
This config specifies that within each `memberTypes` group, members are in an alphabetic case-sensitive order. | ||
You can copy and paste the default order from [Default Configuration](#default-configuration). | ||
```jsonc | ||
// .eslintrc.json | ||
{ | ||
"rules": { | ||
"@typescript-eslint/member-ordering": [ | ||
"error", | ||
{ | ||
"default": { | ||
"memberTypes": [ | ||
/* <Default Order> */ | ||
], | ||
"order": "alphabetically-case-insensitive" | ||
} | ||
} | ||
] | ||
} | ||
} | ||
``` | ||
<!--tabs--> | ||
##### ❌ Incorrect | ||
```ts | ||
interface Foo { | ||
[a: string]: number; | ||
B: x; | ||
a: x; | ||
b: x; | ||
c: x; | ||
new (): Bar; | ||
(): Baz; | ||
// Wrong alphabetic order within group | ||
B(): void; | ||
c(): void; | ||
b(): void; | ||
a(): void; | ||
@@ -872,14 +887,42 @@ } | ||
### Sorting alphabetically while ignoring member groups | ||
##### ✅ Correct | ||
It is also possible to sort all members and ignore the member groups completely. | ||
```ts | ||
interface Foo { | ||
a: x; | ||
B: x; | ||
c: x; | ||
#### Configuration: `{ "default": { "memberTypes": "never", "order": "alphabetically" } }` | ||
a(): void; | ||
B(): void; | ||
c(): void; | ||
} | ||
``` | ||
##### Incorrect example | ||
#### Sorting Alphabetically Ignoring Member Groups | ||
This config specifies that members are all sorted in an alphabetic case-sensitive order. | ||
It ignores any member group types completely by specifying `"never"` for `memberTypes`. | ||
```jsonc | ||
// .eslintrc.json | ||
{ | ||
"rules": { | ||
"@typescript-eslint/member-ordering": [ | ||
"error", | ||
{ "default": { "memberTypes": "never", "order": "alphabetically" } } | ||
] | ||
} | ||
} | ||
``` | ||
<!--tabs--> | ||
##### ❌ Incorrect | ||
```ts | ||
interface Foo { | ||
static c = 0; | ||
b(): void; | ||
a: b; | ||
a: boolean; | ||
@@ -892,10 +935,460 @@ [a: string]: number; // Order doesn't matter (no sortable identifier) | ||
Note: Wrong alphabetic order `b(): void` should come after `a: b`. | ||
##### ✅ Correct | ||
## When Not To Use It | ||
```ts | ||
interface Foo { | ||
a: boolean; | ||
b(): void; | ||
static c = 0; | ||
If you don't care about the general structure of your classes and interfaces, then you will not need this rule. | ||
[a: string]: number; // Order doesn't matter (no sortable identifier) | ||
new (): Bar; // Order doesn't matter (no sortable identifier) | ||
(): Baz; // Order doesn't matter (no sortable identifier) | ||
} | ||
``` | ||
## Compatibility | ||
#### Sorting Optional Members First or Last | ||
- TSLint: [member-ordering](https://palantir.github.io/tslint/rules/member-ordering/) | ||
The `optionalityOrder` option may be enabled to place all optional members in a group at the beginning or end of that group. | ||
This config places all optional members before all required members: | ||
```jsonc | ||
// .eslintrc.json | ||
{ | ||
"rules": { | ||
"@typescript-eslint/member-ordering": [ | ||
"error", | ||
{ | ||
"default": { | ||
"optionalityOrder": "optional-first", | ||
"order": "alphabetically" | ||
} | ||
} | ||
] | ||
} | ||
} | ||
``` | ||
<!--tabs--> | ||
##### ❌ Incorrect | ||
```ts | ||
interface Foo { | ||
a: boolean; | ||
b?: number; | ||
c: string; | ||
} | ||
``` | ||
##### ✅ Correct | ||
```ts | ||
interface Foo { | ||
b?: number; | ||
a: boolean; | ||
c: string; | ||
} | ||
``` | ||
<!--/tabs--> | ||
This config places all required members before all optional members: | ||
```jsonc | ||
// .eslintrc.json | ||
{ | ||
"rules": { | ||
"@typescript-eslint/member-ordering": [ | ||
"error", | ||
{ | ||
"default": { | ||
"optionalityOrder": "required-first", | ||
"order": "alphabetically" | ||
} | ||
} | ||
] | ||
} | ||
} | ||
``` | ||
<!--tabs--> | ||
##### ❌ Incorrect | ||
```ts | ||
interface Foo { | ||
a: boolean; | ||
b?: number; | ||
c: string; | ||
} | ||
``` | ||
##### ✅ Correct | ||
```ts | ||
interface Foo { | ||
a: boolean; | ||
c: string; | ||
b?: number; | ||
} | ||
``` | ||
<!--/tabs--> | ||
## All Supported Options | ||
### Member Types (Granular Form) | ||
There are multiple ways to specify the member types. | ||
The most explicit and granular form is the following: | ||
```jsonc | ||
[ | ||
// Index signature | ||
"signature", | ||
"readonly-signature", | ||
// Fields | ||
"public-static-field", | ||
"public-static-readonly-field", | ||
"protected-static-field", | ||
"protected-static-readonly-field", | ||
"private-static-field", | ||
"private-static-readonly-field", | ||
"#private-static-field", | ||
"#private-static-readonly-field", | ||
"public-decorated-field", | ||
"public-decorated-readonly-field", | ||
"protected-decorated-field", | ||
"protected-decorated-readonly-field", | ||
"private-decorated-field", | ||
"private-decorated-readonly-field", | ||
"public-instance-field", | ||
"public-instance-readonly-field", | ||
"protected-instance-field", | ||
"protected-instance-readonly-field", | ||
"private-instance-field", | ||
"private-instance-readonly-field", | ||
"#private-instance-field", | ||
"#private-instance-readonly-field", | ||
"public-abstract-field", | ||
"public-abstract-readonly-field", | ||
"protected-abstract-field", | ||
"protected-abstract-readonly-field", | ||
"public-field", | ||
"public-readonly-field", | ||
"protected-field", | ||
"protected-readonly-field", | ||
"private-field", | ||
"private-readonly-field" | ||
"#private-field", | ||
"#private-readonly-field" | ||
"static-field", | ||
"static-readonly-field", | ||
"instance-field", | ||
"instance-readonly-field" | ||
"abstract-field", | ||
"abstract-readonly-field", | ||
"decorated-field", | ||
"decorated-readonly-field", | ||
"field", | ||
"readonly-field", | ||
// Static initialization | ||
"static-initialization", | ||
// Constructors | ||
"public-constructor", | ||
"protected-constructor", | ||
"private-constructor", | ||
// Getters | ||
"public-static-get", | ||
"protected-static-get", | ||
"private-static-get", | ||
"#private-static-get", | ||
"public-decorated-get", | ||
"protected-decorated-get", | ||
"private-decorated-get", | ||
"public-instance-get", | ||
"protected-instance-get", | ||
"private-instance-get", | ||
"#private-instance-get", | ||
"public-abstract-get", | ||
"protected-abstract-get", | ||
"public-get", | ||
"protected-get", | ||
"private-get", | ||
"#private-get", | ||
"static-get", | ||
"instance-get", | ||
"abstract-get", | ||
"decorated-get", | ||
"get", | ||
// Setters | ||
"public-static-set", | ||
"protected-static-set", | ||
"private-static-set", | ||
"#private-static-set", | ||
"public-decorated-set", | ||
"protected-decorated-set", | ||
"private-decorated-set", | ||
"public-instance-set", | ||
"protected-instance-set", | ||
"private-instance-set", | ||
"#private-instance-set", | ||
"public-abstract-set", | ||
"protected-abstract-set", | ||
"public-set", | ||
"protected-set", | ||
"private-set", | ||
"static-set", | ||
"instance-set", | ||
"abstract-set", | ||
"decorated-set", | ||
"set", | ||
// Methods | ||
"public-static-method", | ||
"protected-static-method", | ||
"private-static-method", | ||
"#private-static-method", | ||
"public-decorated-method", | ||
"protected-decorated-method", | ||
"private-decorated-method", | ||
"public-instance-method", | ||
"protected-instance-method", | ||
"private-instance-method", | ||
"#private-instance-method", | ||
"public-abstract-method", | ||
"protected-abstract-method" | ||
] | ||
``` | ||
:::note | ||
If you only specify some of the possible types, the non-specified ones can have any particular order. | ||
This means that they can be placed before, within or after the specified types and the linter won't complain about it. | ||
::: | ||
### Member Group Types (With Accessibility, Ignoring Scope) | ||
It is also possible to group member types by their accessibility (`static`, `instance`, `abstract`), ignoring their scope. | ||
```jsonc | ||
[ | ||
// Index signature | ||
// No accessibility for index signature. | ||
// Fields | ||
"public-field", // = ["public-static-field", "public-instance-field"] | ||
"protected-field", // = ["protected-static-field", "protected-instance-field"] | ||
"private-field", // = ["private-static-field", "private-instance-field"] | ||
// Static initialization | ||
// No accessibility for static initialization. | ||
// Constructors | ||
// Only the accessibility of constructors is configurable. See below. | ||
// Getters | ||
"public-get", // = ["public-static-get", "public-instance-get"] | ||
"protected-get", // = ["protected-static-get", "protected-instance-get"] | ||
"private-get", // = ["private-static-get", "private-instance-get"] | ||
// Setters | ||
"public-set", // = ["public-static-set", "public-instance-set"] | ||
"protected-set", // = ["protected-static-set", "protected-instance-set"] | ||
"private-set", // = ["private-static-set", "private-instance-set"] | ||
// Methods | ||
"public-method", // = ["public-static-method", "public-instance-method"] | ||
"protected-method", // = ["protected-static-method", "protected-instance-method"] | ||
"private-method" // = ["private-static-method", "private-instance-method"] | ||
] | ||
``` | ||
### Member Group Types (With Accessibility and a Decorator) | ||
It is also possible to group methods or fields with a decorator separately, optionally specifying | ||
their accessibility. | ||
```jsonc | ||
[ | ||
// Index signature | ||
// No decorators for index signature. | ||
// Fields | ||
"public-decorated-field", | ||
"protected-decorated-field", | ||
"private-decorated-field", | ||
"decorated-field", // = ["public-decorated-field", "protected-decorated-field", "private-decorated-field"] | ||
// Static initialization | ||
// No decorators for static initialization. | ||
// Constructors | ||
// There are no decorators for constructors. | ||
// Getters | ||
"public-decorated-get", | ||
"protected-decorated-get", | ||
"private-decorated-get", | ||
"decorated-get", // = ["public-decorated-get", "protected-decorated-get", "private-decorated-get"] | ||
// Setters | ||
"public-decorated-set", | ||
"protected-decorated-set", | ||
"private-decorated-set", | ||
"decorated-set", // = ["public-decorated-set", "protected-decorated-set", "private-decorated-set"] | ||
// Methods | ||
"public-decorated-method", | ||
"protected-decorated-method", | ||
"private-decorated-method", | ||
"decorated-method" // = ["public-decorated-method", "protected-decorated-method", "private-decorated-method"] | ||
] | ||
``` | ||
### Member Group Types (With Scope, Ignoring Accessibility) | ||
Another option is to group the member types by their scope (`public`, `protected`, `private`), ignoring their accessibility. | ||
```jsonc | ||
[ | ||
// Index signature | ||
// No scope for index signature. | ||
// Fields | ||
"static-field", // = ["public-static-field", "protected-static-field", "private-static-field"] | ||
"instance-field", // = ["public-instance-field", "protected-instance-field", "private-instance-field"] | ||
"abstract-field", // = ["public-abstract-field", "protected-abstract-field"] | ||
// Static initialization | ||
// No scope for static initialization. | ||
// Constructors | ||
"constructor", // = ["public-constructor", "protected-constructor", "private-constructor"] | ||
// Getters | ||
"static-get", // = ["public-static-get", "protected-static-get", "private-static-get"] | ||
"instance-get", // = ["public-instance-get", "protected-instance-get", "private-instance-get"] | ||
"abstract-get", // = ["public-abstract-get", "protected-abstract-get"] | ||
// Setters | ||
"static-set", // = ["public-static-set", "protected-static-set", "private-static-set"] | ||
"instance-set", // = ["public-instance-set", "protected-instance-set", "private-instance-set"] | ||
"abstract-set", // = ["public-abstract-set", "protected-abstract-set"] | ||
// Methods | ||
"static-method", // = ["public-static-method", "protected-static-method", "private-static-method"] | ||
"instance-method", // = ["public-instance-method", "protected-instance-method", "private-instance-method"] | ||
"abstract-method" // = ["public-abstract-method", "protected-abstract-method"] | ||
] | ||
``` | ||
### Member Group Types (With Scope and Accessibility) | ||
The third grouping option is to ignore both scope and accessibility. | ||
```jsonc | ||
[ | ||
// Index signature | ||
// No grouping for index signature. | ||
// Fields | ||
"field", // = ["public-static-field", "protected-static-field", "private-static-field", "public-instance-field", "protected-instance-field", "private-instance-field", | ||
// "public-abstract-field", "protected-abstract-field"] | ||
// Static initialization | ||
// No grouping for static initialization. | ||
// Constructors | ||
// Only the accessibility of constructors is configurable. | ||
// Getters | ||
"get", // = ["public-static-get", "protected-static-get", "private-static-get", "public-instance-get", "protected-instance-get", "private-instance-get", | ||
// "public-abstract-get", "protected-abstract-get"] | ||
// Setters | ||
"set", // = ["public-static-set", "protected-static-set", "private-static-set", "public-instance-set", "protected-instance-set", "private-instance-set", | ||
// "public-abstract-set", "protected-abstract-set"] | ||
// Methods | ||
"method" // = ["public-static-method", "protected-static-method", "private-static-method", "public-instance-method", "protected-instance-method", "private-instance-method", | ||
// "public-abstract-method", "protected-abstract-method"] | ||
] | ||
``` | ||
### Member Group Types (Readonly Fields) | ||
It is possible to group fields by their `readonly` modifiers. | ||
```jsonc | ||
[ | ||
// Index signature | ||
"readonly-signature", | ||
"signature", | ||
// Fields | ||
"readonly-field", // = ["public-static-readonly-field", "protected-static-readonly-field", "private-static-readonly-field", "public-instance-readonly-field", "protected-instance-readonly-field", "private-instance-readonly-field", "public-abstract-readonly-field", "protected-abstract-readonly-field"] | ||
"field" // = ["public-static-field", "protected-static-field", "private-static-field", "public-instance-field", "protected-instance-field", "private-instance-field", "public-abstract-field", "protected-abstract-field"] | ||
] | ||
``` | ||
### Grouping Different Member Types at the Same Rank | ||
It is also possible to group different member types at the same rank. | ||
```jsonc | ||
[ | ||
// Index signature | ||
"signature", | ||
// Fields | ||
"field", | ||
// Static initialization | ||
"static-initialization", | ||
// Constructors | ||
"constructor", | ||
// Getters and Setters at the same rank | ||
["get", "set"], | ||
// Methods | ||
"method" | ||
] | ||
``` | ||
## When Not To Use It | ||
If you don't care about the general order of your members, then you will not need this rule. |
@@ -1,13 +0,17 @@ | ||
# Enforces using a particular method signature syntax. (`method-signature-style`) | ||
--- | ||
description: 'Enforce using a particular method signature syntax.' | ||
--- | ||
There are two ways to define an object/interface function property. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/method-signature-style** for documentation. | ||
TypeScript provides two ways to define an object/interface function property: | ||
```ts | ||
// method shorthand syntax | ||
interface T1 { | ||
interface Example { | ||
// method shorthand syntax | ||
func(arg: string): number; | ||
} | ||
// regular property with function type | ||
interface T2 { | ||
// regular property with function type | ||
func: (arg: string) => number; | ||
@@ -17,2 +21,4 @@ } | ||
The two are very similar; most of the time it doesn't matter which one you use. | ||
A good practice is to use the TypeScript's `strict` option (which implies `strictFunctionTypes`) which enables correct typechecking for function properties only (method signatures get old behavior). | ||
@@ -36,6 +42,10 @@ | ||
## Rule Details | ||
### `property` | ||
Examples of **incorrect** code with `property` option. | ||
Examples of code with `property` option. | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -55,3 +65,3 @@ interface T1 { | ||
Examples of **correct** code with `property` option. | ||
#### ✅ Correct | ||
@@ -73,4 +83,10 @@ ```ts | ||
Examples of **incorrect** code with `method` option. | ||
### `method` | ||
Examples of code with `method` option. | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -85,3 +101,3 @@ interface T1 { | ||
Examples of **correct** code with `method` option. | ||
#### ✅ Correct | ||
@@ -88,0 +104,0 @@ ```ts |
@@ -1,14 +0,22 @@ | ||
# Enforces naming conventions for everything across a codebase (`naming-convention`) | ||
--- | ||
description: 'Enforce naming conventions for everything across a codebase.' | ||
--- | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/naming-convention** for documentation. | ||
Enforcing naming conventions helps keep the codebase consistent, and reduces overhead when thinking about how to name a variable. | ||
Additionally, a well-designed style guide can help communicate intent, such as by enforcing all private properties begin with an `_`, and all global-level constants are written in `UPPER_CASE`. | ||
There are many different rules that have existed over time, but they have had the problem of not having enough granularity, meaning it was hard to have a well defined style guide, and most of the time you needed 3 or more rules at once to enforce different conventions, hoping they didn't conflict. | ||
## Examples | ||
## Rule Details | ||
This rule allows you to enforce conventions for any identifier, using granular selectors to create a fine-grained style guide. | ||
### Note - this rule only needs type information in specific cases, detailed below | ||
:::note | ||
This rule only needs type information in specific cases, detailed below. | ||
::: | ||
## Options | ||
@@ -99,6 +107,6 @@ | ||
- `camelCase` - standard camelCase format - no underscores are allowed between characters, and consecutive capitals are allowed (i.e. both `myID` and `myId` are valid). | ||
- `PascalCase` - same as `camelCase`, except the first character must be upper-case. | ||
- `snake_case` - standard snake_case format - all characters must be lower-case, and underscores are allowed. | ||
- `strictCamelCase` - same as `camelCase`, but consecutive capitals are not allowed (i.e. `myId` is valid, but `myID` is not). | ||
- `PascalCase` - same as `camelCase`, except the first character must be upper-case. | ||
- `StrictPascalCase` - same as `strictCamelCase`, except the first character must be upper-case. | ||
- `snake_case` - standard snake_case format - all characters must be lower-case, and underscores are allowed. | ||
- `UPPER_CASE` - same as `snake_case`, except all characters must be upper-case. | ||
@@ -109,3 +117,3 @@ | ||
### `custom` | ||
#### `custom` | ||
@@ -115,6 +123,6 @@ The `custom` option defines a custom regex that the identifier must (or must not) match. This option allows you to have a bit more finer-grained control over identifiers, letting you ban (or force) certain patterns and substrings. | ||
- `regex` - accepts a regular expression (anything accepted into `new RegExp(regex)`). | ||
- `match` - true if the identifier _must_ match the `regex`, false if the identifier _must not_ match the `regex`. | ||
- `regex` - a string that is then passed into RegExp to create a new regular expression: `new RegExp(regex)` | ||
### `filter` | ||
#### `filter` | ||
@@ -127,4 +135,4 @@ The `filter` option operates similar to `custom`, accepting the same shaped object, except that it controls if the rest of the configuration should or should not be applied to an identifier. | ||
- `regex` - accepts a regular expression (anything accepted into `new RegExp(regex)`). | ||
- `match` - true if the identifier _must_ match the `regex`, false if the identifier _must not_ match the `regex`. | ||
- `regex` - a string that is then passed into RegExp to create a new regular expression: `new RegExp(regex)` | ||
@@ -137,8 +145,8 @@ Alternatively, `filter` accepts a regular expression (anything accepted into `new RegExp(filter)`). In this case, it's treated as if you had passed an object with the regex and `match: true`. | ||
- `allow` - existence of a single leading/trailing underscore is not explicitly enforced. | ||
- `allowDouble` - existence of a double leading/trailing underscore is not explicitly enforced. | ||
- `allowSingleOrDouble` - existence of a single or a double leading/trailing underscore is not explicitly enforced. | ||
- `forbid` - a leading/trailing underscore is not allowed at all. | ||
- `require` - a single leading/trailing underscore must be included. | ||
- `requireDouble` - two leading/trailing underscores must be included. | ||
- `allow` - existence of a single leading/trailing underscore is not explicitly enforced. | ||
- `allowDouble` - existence of a double leading/trailing underscore is not explicitly enforced. | ||
- `allowSingleOrDouble` - existence of a single or a double leading/trailing underscore is not explicitly enforced. | ||
@@ -149,3 +157,3 @@ #### `prefix` / `suffix` | ||
If these are provided, the identifier must start with one of the provided values. For example, if you provide `{ prefix: ['IFace', 'Class', 'Type'] }`, then the following names are valid: `IFaceFoo`, `ClassBar`, `TypeBaz`, but the name `Bang` is not valid, as it contains none of the prefixes. | ||
If these are provided, the identifier must start with one of the provided values. For example, if you provide `{ prefix: ['Class', 'IFace', 'Type'] }`, then the following names are valid: `ClassBar`, `IFaceFoo`, `TypeBaz`, but the name `Bang` is not valid, as it contains none of the prefixes. | ||
@@ -158,18 +166,20 @@ **Note:** As [documented above](#format-options), the prefix is trimmed before format is validated, therefore PascalCase must be used to allow variables such as `isEnabled` using the prefix `is`. | ||
- Accepts one or array of selectors to define an option block that applies to one or multiple selectors. | ||
- For example, if you provide `{ selector: ['variable', 'function'] }`, then it will apply the same option to variable and function nodes. | ||
- For example, if you provide `{ selector: ['function', 'variable'] }`, then it will apply the same option to variable and function nodes. | ||
- See [Allowed Selectors, Modifiers and Types](#allowed-selectors-modifiers-and-types) below for the complete list of allowed selectors. | ||
- `modifiers` allows you to specify which modifiers to granularly apply to, such as the accessibility (`private`/`public`/`protected`), or if the thing is `static`, etc. | ||
- `modifiers` allows you to specify which modifiers to granularly apply to, such as the accessibility (`#private`/`private`/`protected`/`public`), or if the thing is `static`, etc. | ||
- The name must match _all_ of the modifiers. | ||
- For example, if you provide `{ modifiers: ['private', 'static', 'readonly'] }`, then it will only match something that is `private static readonly`, and something that is just `private` will not match. | ||
- For example, if you provide `{ modifiers: ['private','readonly','static'] }`, then it will only match something that is `private static readonly`, and something that is just `private` will not match. | ||
- The following `modifiers` are allowed: | ||
- `abstract`,`override`,`private`,`protected`,`readonly`,`static` - matches any member explicitly declared with the given modifier. | ||
- `async` - matches any method, function, or function variable which is async via the `async` keyword (e.g. does not match functions that return promises without using `async` keyword) | ||
- `const` - matches a variable declared as being `const` (`const x = 1`). | ||
- `destructured` - matches a variable declared via an object destructuring pattern (`const {x, z = 2}`). | ||
- Note that this does not match renamed destructured properties (`const {x: y, a: b = 2}`). | ||
- `exported` - matches anything that is exported from the module. | ||
- `global` - matches a variable/function declared in the top-level scope. | ||
- `exported` - matches anything that is exported from the module. | ||
- `#private` - matches any member with a private identifier (an identifier that starts with `#`) | ||
- `public` - matches any member that is either explicitly declared as `public`, or has no visibility modifier (i.e. implicitly public). | ||
- `requiresQuotes` - matches any name that requires quotes as it is not a valid identifier (i.e. has a space, a dash, etc in it). | ||
- `unused` - matches anything that is not used. | ||
- `requiresQuotes` - matches any name that requires quotes as it is not a valid identifier (i.e. has a space, a dash, etc in it). | ||
- `public` - matches any member that is either explicitly declared as `public`, or has no visibility modifier (i.e. implicitly public). | ||
- `readonly`, `static`, `abstract`, `protected`, `private` - matches any member explicitly declared with the given modifier. | ||
- `types` allows you to specify which types to match. This option supports simple, primitive types only (`boolean`, `string`, `number`, `array`, `function`). | ||
- `types` allows you to specify which types to match. This option supports simple, primitive types only (`array`,`boolean`,`function`,`number`,`string`). | ||
- The name must match _one_ of the types. | ||
@@ -179,7 +189,7 @@ - **_NOTE - Using this option will require that you lint with type information._** | ||
- The following `types` are allowed: | ||
- `array` matches any type assignable to `Array<unknown> | null | undefined` | ||
- `boolean` matches any type assignable to `boolean | null | undefined` | ||
- `function` matches any type assignable to `Function | null | undefined` | ||
- `number` matches any type assignable to `number | null | undefined` | ||
- `string` matches any type assignable to `string | null | undefined` | ||
- `number` matches any type assignable to `number | null | undefined` | ||
- `array` matches any type assignable to `Array<unknown> | null | undefined` | ||
- `function` matches any type assignable to `Function | null | undefined` | ||
@@ -196,40 +206,22 @@ The ordering of selectors does not matter. The implementation will automatically sort the selectors to ensure they match from most-specific to least specific. It will keep checking selectors in that order until it finds one that matches the name. See ["How does the rule automatically order selectors?"](#how-does-the-rule-automatically-order-selectors) | ||
- `variable` - matches any `var` / `let` / `const` variable name. | ||
- Allowed `modifiers`: `const`, `destructured`, `global`, `exported`, `unused`. | ||
- Allowed `types`: `boolean`, `string`, `number`, `function`, `array`. | ||
- `function` - matches any named function declaration or named function expression. | ||
- Allowed `modifiers`: `global`, `exported`, `unused`. | ||
- `accessor` - matches any accessor. | ||
- Allowed `modifiers`: `abstract`, `override`, `private`, `protected`, `public`, `requiresQuotes`, `static`. | ||
- Allowed `types`: `array`, `boolean`, `function`, `number`, `string`. | ||
- `class` - matches any class declaration. | ||
- Allowed `modifiers`: `abstract`, `exported`, `unused`. | ||
- Allowed `types`: none. | ||
- `parameter` - matches any function parameter. Does not match parameter properties. | ||
- Allowed `modifiers`: `destructured`, `unused`. | ||
- Allowed `types`: `boolean`, `string`, `number`, `function`, `array`. | ||
- `classProperty` - matches any class property. Does not match properties that have direct function expression or arrow function expression values. | ||
- Allowed `modifiers`: `abstract`, `private`, `protected`, `public`, `readonly`, `requiresQuotes`, `static`. | ||
- Allowed `types`: `boolean`, `string`, `number`, `function`, `array`. | ||
- `objectLiteralProperty` - matches any object literal property. Does not match properties that have direct function expression or arrow function expression values. | ||
- Allowed `modifiers`: `public`, `requiresQuotes`. | ||
- Allowed `types`: `boolean`, `string`, `number`, `function`, `array`. | ||
- `typeProperty` - matches any object type property. Does not match properties that have direct function expression or arrow function expression values. | ||
- Allowed `modifiers`: `public`, `readonly`, `requiresQuotes`. | ||
- Allowed `types`: `boolean`, `string`, `number`, `function`, `array`. | ||
- `parameterProperty` - matches any parameter property. | ||
- Allowed `modifiers`: `private`, `protected`, `public`, `readonly`. | ||
- Allowed `types`: `boolean`, `string`, `number`, `function`, `array`. | ||
- `classMethod` - matches any class method. Also matches properties that have direct function expression or arrow function expression values. Does not match accessors. | ||
- Allowed `modifiers`: `abstract`, `private`, `protected`, `public`, `requiresQuotes`, `static`. | ||
- Allowed `modifiers`: `abstract`, `async`, `override`, `#private`, `private`, `protected`, `public`, `requiresQuotes`, `static`. | ||
- Allowed `types`: none. | ||
- `objectLiteralMethod` - matches any object literal method. Also matches properties that have direct function expression or arrow function expression values. Does not match accessors. | ||
- Allowed `modifiers`: `public`, `requiresQuotes`. | ||
- `classProperty` - matches any class property. Does not match properties that have direct function expression or arrow function expression values. | ||
- Allowed `modifiers`: `abstract`, `override`, `#private`, `private`, `protected`, `public`, `readonly`, `requiresQuotes`, `static`. | ||
- Allowed `types`: `array`, `boolean`, `function`, `number`, `string`. | ||
- `enum` - matches any enum declaration. | ||
- Allowed `modifiers`: `exported`, `unused`. | ||
- Allowed `types`: none. | ||
- `typeMethod` - matches any object type method. Also matches properties that have direct function expression or arrow function expression values. Does not match accessors. | ||
- Allowed `modifiers`: `public`, `requiresQuotes`. | ||
- Allowed `types`: none. | ||
- `accessor` - matches any accessor. | ||
- Allowed `modifiers`: `abstract`, `private`, `protected`, `public`, `requiresQuotes`, `static`. | ||
- Allowed `types`: `boolean`, `string`, `number`, `function`, `array`. | ||
- `enumMember` - matches any enum member. | ||
- Allowed `modifiers`: `requiresQuotes`. | ||
- Allowed `types`: none. | ||
- `class` - matches any class declaration. | ||
- Allowed `modifiers`: `abstract`, `exported`, `unused`. | ||
- `function` - matches any named function declaration or named function expression. | ||
- Allowed `modifiers`: `async`, `exported`, `global`, `unused`. | ||
- Allowed `types`: none. | ||
@@ -239,7 +231,19 @@ - `interface` - matches any interface declaration. | ||
- Allowed `types`: none. | ||
- `objectLiteralMethod` - matches any object literal method. Also matches properties that have direct function expression or arrow function expression values. Does not match accessors. | ||
- Allowed `modifiers`: `async`, `public`, `requiresQuotes`. | ||
- Allowed `types`: none. | ||
- `objectLiteralProperty` - matches any object literal property. Does not match properties that have direct function expression or arrow function expression values. | ||
- Allowed `modifiers`: `public`, `requiresQuotes`. | ||
- Allowed `types`: `array`, `boolean`, `function`, `number`, `string`. | ||
- `parameter` - matches any function parameter. Does not match parameter properties. | ||
- Allowed `modifiers`: `destructured`, `unused`. | ||
- Allowed `types`: `array`, `boolean`, `function`, `number`, `string`. | ||
- `parameterProperty` - matches any parameter property. | ||
- Allowed `modifiers`: `private`, `protected`, `public`, `readonly`. | ||
- Allowed `types`: `array`, `boolean`, `function`, `number`, `string`. | ||
- `typeAlias` - matches any type alias declaration. | ||
- Allowed `modifiers`: `exported`, `unused`. | ||
- Allowed `types`: none. | ||
- `enum` - matches any enum declaration. | ||
- Allowed `modifiers`: `exported`, `unused`. | ||
- `typeMethod` - matches any object type method. Also matches properties that have direct function expression or arrow function expression values. Does not match accessors. | ||
- Allowed `modifiers`: `public`, `requiresQuotes`. | ||
- Allowed `types`: none. | ||
@@ -249,2 +253,8 @@ - `typeParameter` - matches any generic type parameter declaration. | ||
- Allowed `types`: none. | ||
- `typeProperty` - matches any object type property. Does not match properties that have direct function expression or arrow function expression values. | ||
- Allowed `modifiers`: `public`, `readonly`, `requiresQuotes`. | ||
- Allowed `types`: `array`, `boolean`, `function`, `number`, `string`. | ||
- `variable` - matches any `const` / `let` / `var` variable name. | ||
- Allowed `modifiers`: `async`, `const`, `destructured`, `exported`, `global`, `unused`. | ||
- Allowed `types`: `array`, `boolean`, `function`, `number`, `string`. | ||
@@ -258,16 +268,16 @@ ##### Group Selectors | ||
- Allowed `types`: none. | ||
- `variableLike` - matches the same as `variable`, `function` and `parameter`. | ||
- Allowed `modifiers`: `unused`. | ||
- `memberLike` - matches the same as `accessor`, `enumMember`, `method`, `parameterProperty`, `property`. | ||
- Allowed `modifiers`: `abstract`, `async`, `override`, `#private`, `private`, `protected`, `public`, `readonly`, `requiresQuotes`, `static`. | ||
- Allowed `types`: none. | ||
- `memberLike` - matches the same as `property`, `parameterProperty`, `method`, `accessor`, `enumMember`. | ||
- Allowed `modifiers`: `private`, `protected`, `public`, `static`, `readonly`, `abstract`, `requiresQuotes`. | ||
- `method` - matches the same as `classMethod`, `objectLiteralMethod`, `typeMethod`. | ||
- Allowed `modifiers`: `abstract`, `async`, `override`, `#private`, `private`, `protected`, `public`, `readonly`, `requiresQuotes`, `static`. | ||
- Allowed `types`: none. | ||
- `typeLike` - matches the same as `class`, `interface`, `typeAlias`, `enum`, `typeParameter`. | ||
- `property` - matches the same as `classProperty`, `objectLiteralProperty`, `typeProperty`. | ||
- Allowed `modifiers`: `abstract`, `async`, `override`, `#private`, `private`, `protected`, `public`, `readonly`, `requiresQuotes`, `static`. | ||
- Allowed `types`: `array`, `boolean`, `function`, `number`, `string`. | ||
- `typeLike` - matches the same as `class`, `enum`, `interface`, `typeAlias`, `typeParameter`. | ||
- Allowed `modifiers`: `abstract`, `unused`. | ||
- Allowed `types`: none. | ||
- `property` - matches the same as `classProperty`, `objectLiteralProperty`, `typeProperty`. | ||
- Allowed `modifiers`: `private`, `protected`, `public`, `static`, `readonly`, `abstract`, `requiresQuotes`. | ||
- Allowed `types`: `boolean`, `string`, `number`, `function`, `array`. | ||
- `method` - matches the same as `classMethod`, `objectLiteralMethod`, `typeMethod`. | ||
- Allowed `modifiers`: `private`, `protected`, `public`, `static`, `readonly`, `abstract`, `requiresQuotes`. | ||
- `variableLike` - matches the same as `function`, `parameter` and `variable`. | ||
- Allowed `modifiers`: `async`, `unused`. | ||
- Allowed `types`: none. | ||
@@ -283,14 +293,14 @@ | ||
1. check the `selector` | ||
1. if `selector` is one individual selector → the name's type must be of that type. | ||
1. if `selector` is a group selector → the name's type must be one of the grouped types. | ||
1. if `selector` is an array of selectors → apply the above for each selector in the array. | ||
1. check the `filter` | ||
1. if `filter` is omitted → skip this step. | ||
1. if the name matches the `filter` → continue evaluating this selector. | ||
1. if the name does not match the `filter` → skip this selector and continue to the next selector. | ||
1. check the `types` | ||
2. if the name matches the `filter` → continue evaluating this selector. | ||
3. if the name does not match the `filter` → skip this selector and continue to the next selector. | ||
2. check the `selector` | ||
1. if `selector` is one individual selector → the name's type must be of that type. | ||
2. if `selector` is a group selector → the name's type must be one of the grouped types. | ||
3. if `selector` is an array of selectors → apply the above for each selector in the array. | ||
3. check the `types` | ||
1. if `types` is omitted → skip this step. | ||
1. if the name has a type in `types` → continue evaluating this selector. | ||
1. if the name does not have a type in `types` → skip this selector and continue to the next selector. | ||
2. if the name has a type in `types` → continue evaluating this selector. | ||
3. if the name does not have a type in `types` → skip this selector and continue to the next selector. | ||
@@ -336,4 +346,4 @@ A name is considered to pass the config if it: | ||
- (2) is tested next because it is an individual selector. | ||
- (1) is tested next as it is a grouped selector. | ||
- (4) is tested last as it is the base default selector. | ||
- (4) is tested next as it is a grouped selector. | ||
- (1) is tested last as it is the base default selector. | ||
@@ -340,0 +350,0 @@ Its worth noting that whilst this order is applied, all selectors may not run on a name. |
@@ -1,10 +0,18 @@ | ||
# Disallow generic `Array` constructors (`no-array-constructor`) | ||
--- | ||
description: 'Disallow generic `Array` constructors.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-array-constructor** for documentation. | ||
## Examples | ||
This rule extends the base [`eslint/no-array-constructor`](https://eslint.org/docs/rules/no-array-constructor) rule. | ||
It adds support for the generically typed `Array` constructor (`new Array<Foo>()`). | ||
Examples of **incorrect** code for this rule: | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -17,3 +25,3 @@ /*eslint no-array-constructor: "error"*/ | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -29,17 +37,1 @@ ```ts | ||
``` | ||
## How to use | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"no-array-constructor": "off", | ||
"@typescript-eslint/no-array-constructor": ["error"] | ||
} | ||
``` | ||
## Options | ||
See [`eslint/no-array-constructor` options](https://eslint.org/docs/rules/no-array-constructor#options). | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-array-constructor.md)</sup> |
@@ -1,18 +0,22 @@ | ||
# Requires that `.toString()` is only called on objects which provide useful information when stringified (`no-base-to-string`) | ||
--- | ||
description: 'Require `.toString()` to only be called on objects which provide useful information when stringified.' | ||
--- | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-base-to-string** for documentation. | ||
JavaScript will call `toString()` on an object when it is converted to a string, such as when `+` adding to a string or in `${}` template literals. | ||
The default Object `.toString()` returns `"[object Object]"`, which is often not what was intended. | ||
This rule reports on stringified values that aren't primitives and don't define a more useful `.toString()` method. | ||
The default Object `.toString()` returns `"[object Object]"`, so this rule requires stringified objects define a more useful `.toString()` method. | ||
> Note that `Function` provides its own `.toString()` that returns the function's code. | ||
> Functions are not flagged by this rule. | ||
Note that `Function` provides its own `.toString()` that returns the function's code. | ||
Functions are not flagged by this rule. | ||
## Examples | ||
This rule has some overlap with [`restrict-plus-operands`](./restrict-plus-operands.md) and [`restrict-template-expressions`](./restrict-template-expressions.md). | ||
<!--tabs--> | ||
## Rule Details | ||
### ❌ Incorrect | ||
This rule prevents accidentally defaulting to the base Object `.toString()` method. | ||
Examples of **incorrect** code for this rule: | ||
```ts | ||
@@ -31,3 +35,3 @@ // Passing an object or class instance to string concatenation: | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -58,12 +62,2 @@ ```ts | ||
```ts | ||
type Options = { | ||
ignoredTypeNames?: string[]; | ||
}; | ||
const defaultOptions: Options = { | ||
ignoredTypeNames: ['RegExp'], | ||
}; | ||
``` | ||
### `ignoredTypeNames` | ||
@@ -89,2 +83,9 @@ | ||
## Related To | ||
- [`restrict-plus-operands`](./restrict-plus-operands.md) | ||
- [`restrict-template-expressions`](./restrict-template-expressions.md) | ||
## Further Reading | ||
- [`Object.prototype.toString()` MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString) |
@@ -1,4 +0,8 @@ | ||
# Disallow non-null assertion in locations that may be confusing (`no-confusing-non-null-assertion`) | ||
--- | ||
description: 'Disallow non-null assertion in locations that may be confusing.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-confusing-non-null-assertion** for documentation. | ||
@@ -13,4 +17,10 @@ Using a non-null assertion (`!`) next to an assign or equals check (`=` or `==` or `===`) creates code that is confusing as it looks similar to a not equals check (`!=` `!==`). | ||
Examples of **incorrect** code for this rule: | ||
This rule flags confusing `!` assertions and suggests either removing them or wrapping the asserted expression in `()` parenthesis. | ||
## Examples | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -27,3 +37,3 @@ interface Foo { | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -30,0 +40,0 @@ <!-- prettier-ignore --> |
@@ -1,14 +0,21 @@ | ||
# Requires expressions of type void to appear in statement position (`no-confusing-void-expression`) | ||
--- | ||
description: 'Require expressions of type void to appear in statement position.' | ||
--- | ||
Returning the results of an expression whose type is void can be misleading. | ||
Attempting to do so is likely a symptom of expecting a different return type from a function. | ||
Even if used correctly, it can be misleading for other developers, | ||
who don't know what a particular function does and if its result matters. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-confusing-void-expression** for documentation. | ||
This rule provides automatic fixes for most common cases. | ||
`void` in TypeScript refers to a function return that is meant to be ignored. | ||
Attempting to use a `void`-typed value, such as storing the result of a called function in a variable, is often a sign of a programmer error. | ||
`void` can also be misleading for other developers even if used correctly. | ||
This rule prevents `void` type expressions from being used in misleading locations such as being assigned to a variable, provided as a function argument, or returned from a function. | ||
## Examples | ||
Examples of **incorrect** code for this rule: | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -32,3 +39,3 @@ // somebody forgot that `alert` doesn't return anything | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -66,29 +73,4 @@ ```ts | ||
An object option can be specified. Each boolean flag makes the rule less strict. | ||
```ts | ||
type Options = { | ||
ignoreArrowShorthand?: boolean; | ||
ignoreVoidOperator?: boolean; | ||
}; | ||
const defaults: Options = { | ||
ignoreArrowShorthand: false, | ||
ignoreVoidOperator: false, | ||
}; | ||
``` | ||
### `ignoreArrowShorthand` | ||
`false` by default. | ||
```json | ||
{ | ||
"@typescript-eslint/no-confusing-void-expression": [ | ||
"error", | ||
{ "ignoreArrowShorthand": true } | ||
] | ||
} | ||
``` | ||
It might be undesirable to wrap every arrow function shorthand expression with braces. | ||
@@ -105,13 +87,2 @@ Especially when using Prettier formatter, which spreads such code across 3 lines instead of 1. | ||
`false` by default. | ||
```json | ||
{ | ||
"@typescript-eslint/no-confusing-void-expression": [ | ||
"error", | ||
{ "ignoreVoidOperator": true } | ||
] | ||
} | ||
``` | ||
It might be preferable to only use some distinct syntax | ||
@@ -149,5 +120,1 @@ to explicitly mark the confusing but valid usage of void expressions. | ||
Also, if you prefer concise coding style then also don't use it. | ||
## Related to | ||
- TSLint: ['no-void-expression'](https://palantir.github.io/tslint/rules/no-void-expression/) |
@@ -1,22 +0,12 @@ | ||
# Disallow duplicate class members (`no-dupe-class-members`) | ||
--- | ||
description: 'Disallow duplicate class members.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-dupe-class-members** for documentation. | ||
## Examples | ||
This rule extends the base [`eslint/no-dupe-class-members`](https://eslint.org/docs/rules/no-dupe-class-members) rule. | ||
It adds support for TypeScript's method overload definitions. | ||
## How to use | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"no-dupe-class-members": "off", | ||
"@typescript-eslint/no-dupe-class-members": ["error"] | ||
} | ||
``` | ||
## Options | ||
See [`eslint/no-dupe-class-members` options](https://eslint.org/docs/rules/no-dupe-class-members#options). | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-dupe-class-members.md)</sup> |
@@ -1,22 +0,12 @@ | ||
# Disallow duplicate imports (`no-duplicate-imports`) | ||
--- | ||
description: 'Disallow duplicate imports.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-duplicate-imports** for documentation. | ||
This rule extends the base [`eslint/no-duplicate-imports`](https://eslint.org/docs/rules/no-duplicate-imports) rule. | ||
This version adds support for type-only import and export. | ||
:::danger Deprecated | ||
## How to use | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"no-duplicate-imports": "off", | ||
"@typescript-eslint/no-duplicate-imports": ["error"] | ||
} | ||
``` | ||
## Options | ||
See [`eslint/no-duplicate-imports` options](https://eslint.org/docs/rules/no-duplicate-imports#options). | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-duplicate-imports.md)</sup> | ||
This rule has been deprecated in favour of the [`import/no-duplicates`](https://github.com/import-js/eslint-plugin-import/blob/HEAD/docs/rules/no-duplicates.md) rule. | ||
::: |
@@ -1,28 +0,21 @@ | ||
# Disallow the delete operator with computed key expressions (`no-dynamic-delete`) | ||
--- | ||
description: 'Disallow using the `delete` operator on computed key expressions.' | ||
--- | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-dynamic-delete** for documentation. | ||
Deleting dynamically computed keys can be dangerous and in some cases not well optimized. | ||
## Rule Details | ||
Using the `delete` operator on keys that aren't runtime constants could be a sign that you're using the wrong data structures. | ||
Using `Object`s with added and removed keys can cause occasional edge case bugs, such as if a key is named `"hasOwnProperty"`. | ||
Consider using a `Map` or `Set` if you’re storing collections of objects. | ||
Examples of **correct** code with this rule: | ||
> Consider using a `Map` or `Set` if you’re storing collections of objects. | ||
```ts | ||
const container: { [i: string]: number } = { | ||
/* ... */ | ||
}; | ||
## Examples | ||
// Constant runtime lookups by string index | ||
delete container.aaa; | ||
<!--tabs--> | ||
// Constants that must be accessed by [] | ||
delete container[7]; | ||
delete container['-Infinity']; | ||
``` | ||
### ❌ Incorrect | ||
Examples of **incorrect** code with this rule: | ||
```ts | ||
@@ -39,2 +32,17 @@ // Can be replaced with the constant equivalents, such as container.aaa | ||
### ✅ Correct | ||
```ts | ||
const container: { [i: string]: number } = { | ||
/* ... */ | ||
}; | ||
// Constant runtime lookups by string index | ||
delete container.aaa; | ||
// Constants that must be accessed by [] | ||
delete container[7]; | ||
delete container['-Infinity']; | ||
``` | ||
## When Not To Use It | ||
@@ -47,5 +55,1 @@ | ||
Even repeated minor performance slowdowns likely do not significantly affect your application's general perceived speed. | ||
## Related to | ||
- TSLint: [no-dynamic-delete](https://palantir.github.io/tslint/rules/no-dynamic-delete) |
@@ -1,5 +0,11 @@ | ||
# Disallow empty functions (`no-empty-function`) | ||
--- | ||
description: 'Disallow empty functions.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-empty-function** for documentation. | ||
## Examples | ||
This rule extends the base [`eslint/no-empty-function`](https://eslint.org/docs/rules/no-empty-function) rule. | ||
@@ -10,15 +16,4 @@ It adds support for handling TypeScript specific code that would otherwise trigger the rule. | ||
## How to use | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"no-empty-function": "off", | ||
"@typescript-eslint/no-empty-function": ["error"] | ||
} | ||
``` | ||
## Options | ||
See [`eslint/no-empty-function` options](https://eslint.org/docs/rules/no-empty-function#options). | ||
This rule adds the following options: | ||
@@ -30,3 +25,4 @@ | ||
| 'protected-constructors' | ||
| 'decoratedFunctions'; | ||
| 'decoratedFunctions' | ||
| 'overrideMethods'; | ||
@@ -80,14 +76,16 @@ type AllowOptionEntries = | ||
## How to use | ||
### allow: `overrideMethods` | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"no-empty-function": "off", | ||
"@typescript-eslint/no-empty-function": ["error"] | ||
Examples of correct code for the `{ "allow": ["overrideMethods"] }` option: | ||
```ts | ||
abstract class Base { | ||
protected greet(): void { | ||
console.log('Hello!'); | ||
} | ||
} | ||
class Foo extends Base { | ||
protected override greet(): void {} | ||
} | ||
``` | ||
--- | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-empty-function.md)</sup> |
@@ -1,12 +0,20 @@ | ||
# Disallow the declaration of empty interfaces (`no-empty-interface`) | ||
--- | ||
description: 'Disallow the declaration of empty interfaces.' | ||
--- | ||
An empty interface is equivalent to its supertype. If the interface does not implement a supertype, then | ||
the interface is equivalent to an empty object (`{}`). In both cases it can be omitted. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-empty-interface** for documentation. | ||
## Rule Details | ||
An empty interface in TypeScript does very little: any non-nullable value is assignable to `{}`. | ||
Using an empty interface is often a sign of programmer error, such as misunderstanding the concept of `{}` or forgetting to fill in fields. | ||
This rule aims to ensure that only meaningful interfaces are declared in the code. | ||
The following patterns are considered warnings: | ||
## Examples | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -23,3 +31,3 @@ // an empty interface | ||
The following patterns are not warnings: | ||
### ✅ Correct | ||
@@ -38,8 +46,10 @@ ```ts | ||
// an interface with more than one supertype | ||
// in this case the interface can be used as a replacement of a union type. | ||
// in this case the interface can be used as a replacement of an intersection type. | ||
interface Baz extends Foo, Bar {} | ||
``` | ||
### Options | ||
<!--/tabs--> | ||
## Options | ||
This rule accepts a single object option with the following default configuration: | ||
@@ -63,5 +73,1 @@ | ||
If you don't care about having empty/meaningless interfaces, then you will not need this rule. | ||
## Compatibility | ||
- TSLint: [no-empty-interface](https://palantir.github.io/tslint/rules/no-empty-interface/) |
@@ -1,16 +0,21 @@ | ||
# Disallow usage of the `any` type (`no-explicit-any`) | ||
--- | ||
description: 'Disallow the `any` type.' | ||
--- | ||
Using the `any` type defeats the purpose of using TypeScript. | ||
When `any` is used, all compiler type checks around that value are ignored. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-explicit-any** for documentation. | ||
## Rule Details | ||
The `any` type in TypeScript is a dangerous "escape hatch" from the type system. | ||
Using `any` disables many type checking rules and is generally best used only as a last resort or when prototyping code. | ||
This rule reports on explicit uses of the `any` keyword as a type annotation. | ||
This rule doesn't allow `any` types to be defined. | ||
It aims to keep TypeScript maximally useful. | ||
TypeScript has a compiler flag for `--noImplicitAny` that will prevent | ||
an `any` type from being implied by the compiler, but doesn't prevent | ||
`any` from being explicitly used. | ||
> TypeScript's `--noImplicitAny` compiler option prevents an implied `any`, but doesn't prevent `any` from being explicitly used the way this rule does. | ||
The following patterns are considered warnings: | ||
## Examples | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -52,3 +57,3 @@ const age: any = 'seventeen'; | ||
The following patterns are not warnings: | ||
### ✅ Correct | ||
@@ -93,18 +98,2 @@ ```ts | ||
The rule accepts an options object with the following properties: | ||
```ts | ||
type Options = { | ||
// if true, auto-fixing will be made available in which the "any" type is converted to an "unknown" type | ||
fixToUnknown: boolean; | ||
// specify if arrays from the rest operator are considered okay | ||
ignoreRestArgs: boolean; | ||
}; | ||
const defaults = { | ||
fixToUnknown: false, | ||
ignoreRestArgs: false, | ||
}; | ||
``` | ||
### `ignoreRestArgs` | ||
@@ -177,8 +166,12 @@ | ||
## Related To | ||
- [`no-unsafe-argument`](./no-unsafe-argument.md) | ||
- [`no-unsafe-assignment`](./no-unsafe-assignment.md) | ||
- [`no-unsafe-call`](./no-unsafe-call.md) | ||
- [`no-unsafe-member-access`](./no-unsafe-member-access.md) | ||
- [`no-unsafe-return`](./no-unsafe-return.md) | ||
## Further Reading | ||
- TypeScript [any type](https://www.typescriptlang.org/docs/handbook/basic-types.html#any) | ||
## Compatibility | ||
- TSLint: [no-any](https://palantir.github.io/tslint/rules/no-any/) |
@@ -1,7 +0,18 @@ | ||
# Disallow extra non-null assertion (`no-extra-non-null-assertion`) | ||
--- | ||
description: 'Disallow extra non-null assertions.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-extra-non-null-assertion** for documentation. | ||
Examples of **incorrect** code for this rule: | ||
The `!` non-null assertion operator in TypeScript is used to assert that a value's type does not include `null` or `undefined`. | ||
Using the operator any more than once on a single value does nothing. | ||
## Examples | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -24,3 +35,3 @@ const foo: { bar: number } | null = null; | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -43,9 +54,1 @@ ```ts | ||
``` | ||
## How to use | ||
```json | ||
{ | ||
"@typescript-eslint/no-extra-non-null-assertion": ["error"] | ||
} | ||
``` |
@@ -1,22 +0,12 @@ | ||
# Disallow unnecessary parentheses (`no-extra-parens`) | ||
--- | ||
description: 'Disallow unnecessary parentheses.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-extra-parens** for documentation. | ||
## Examples | ||
This rule extends the base [`eslint/no-extra-parens`](https://eslint.org/docs/rules/no-extra-parens) rule. | ||
It adds support for TypeScript type assertions. | ||
## How to use | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"no-extra-parens": "off", | ||
"@typescript-eslint/no-extra-parens": ["error"] | ||
} | ||
``` | ||
## Options | ||
See [`eslint/no-extra-parens` options](https://eslint.org/docs/rules/no-extra-parens#options). | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-extra-parens.md)</sup> |
@@ -1,22 +0,12 @@ | ||
# Disallow unnecessary semicolons (`no-extra-semi`) | ||
--- | ||
description: 'Disallow unnecessary semicolons.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-extra-semi** for documentation. | ||
## Examples | ||
This rule extends the base [`eslint/no-extra-semi`](https://eslint.org/docs/rules/no-extra-semi) rule. | ||
It adds support for class properties. | ||
## How to use | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"no-extra-semi": "off", | ||
"@typescript-eslint/no-extra-semi": ["error"] | ||
} | ||
``` | ||
## Options | ||
See [`eslint/no-extra-semi` options](https://eslint.org/docs/rules/no-extra-semi#options). | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-extra-semi.md)</sup> |
@@ -1,80 +0,294 @@ | ||
# Forbids the use of classes as namespaces (`no-extraneous-class`) | ||
--- | ||
description: 'Disallow classes used as namespaces.' | ||
--- | ||
This rule warns when a class is accidentally used as a namespace. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-extraneous-class** for documentation. | ||
## Rule Details | ||
This rule reports when a class has no non-static members, such as for a class used exclusively as a static namespace. | ||
From TSLint’s docs: | ||
Users who come from a [OOP](https://en.wikipedia.org/wiki/Object-oriented_programming) paradigm may wrap their utility functions in an extra class, instead of putting them at the top level of an ECMAScript module. | ||
Doing so is generally unnecessary in JavaScript and TypeScript projects. | ||
> Users who come from a Java-style OO language may wrap their utility functions in an extra class, | ||
> instead of putting them at the top level. | ||
- Wrapper classes add extra cognitive complexity to code without adding any structural improvements | ||
- Whatever would be put on them, such as utility functions, are already organized by virtue of being in a module. | ||
- As an alternative, you can `import * as ...` the module to get all of them in a single object. | ||
- IDEs can't provide as good suggestions for static class or namespace imported properties when you start typing property names | ||
- It's more difficult to statically analyze code for unused variables, etc. when they're all on the class (see: [Finding dead code (and dead types) in TypeScript](https://effectivetypescript.com/2020/10/20/tsprune)). | ||
Examples of **incorrect** code for this rule: | ||
This rule also reports classes that have only a constructor and no fields. | ||
Those classes can generally be replaced with a standalone function. | ||
## Examples | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
class EmptyClass {} | ||
class StaticConstants { | ||
static readonly version = 42; | ||
class ConstructorOnly { | ||
static isProduction() { | ||
return process.env.NODE_ENV === 'production'; | ||
} | ||
} | ||
class HelloWorldLogger { | ||
constructor() { | ||
foo(); | ||
console.log('Hello, world!'); | ||
} | ||
} | ||
``` | ||
// Use an object instead: | ||
class StaticOnly { | ||
static version = 42; | ||
static hello() { | ||
### ✅ Correct | ||
```ts | ||
export const version = 42; | ||
export function isProduction() { | ||
return process.env.NODE_ENV === 'production'; | ||
} | ||
function logHelloWorld() { | ||
console.log('Hello, world!'); | ||
} | ||
``` | ||
## Alternatives | ||
### Individual Exports (Recommended) | ||
Instead of using a static utility class we recommend you individually export the utilities from your module. | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
export class Utilities { | ||
static util1() { | ||
return Utilities.util3(); | ||
} | ||
static util2() { | ||
/* ... */ | ||
} | ||
static util3() { | ||
/* ... */ | ||
} | ||
} | ||
``` | ||
#### ✅ Correct | ||
```ts | ||
export function util1() { | ||
return util3(); | ||
} | ||
export function util2() { | ||
/* ... */ | ||
} | ||
export function util3() { | ||
/* ... */ | ||
} | ||
``` | ||
### Namespace Imports (Not Recommended) | ||
If you strongly prefer to have all constructs from a module available as properties of a single object, you can `import * as` the module. | ||
This is known as a "namespace import". | ||
Namespace imports are sometimes preferable because they keep all properties nested and don't need to be changed as you start or stop using various properties from the module. | ||
However, namespace imports are impacted by these downsides: | ||
- They also don't play as well with tree shaking in modern bundlers | ||
- They require a name prefix before each property's usage | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
// utilities.ts | ||
export class Utilities { | ||
static sayHello() { | ||
console.log('Hello, world!'); | ||
} | ||
} | ||
// consumers.ts | ||
import { Utilities } from './utilities'; | ||
Utilities.sayHello(); | ||
``` | ||
Examples of **correct** code for this rule: | ||
#### ⚠️ Namespace Imports | ||
```ts | ||
class EmptyClass extends SuperClass {} | ||
// utilities.ts | ||
export function sayHello() { | ||
console.log('Hello, world!'); | ||
} | ||
class ParameterProperties { | ||
constructor(public name: string) {} | ||
// consumers.ts | ||
import * as utilities from './utilities'; | ||
utilities.sayHello(); | ||
``` | ||
#### ✅ Standalone Imports | ||
```ts | ||
// utilities.ts | ||
export function sayHello() { | ||
console.log('Hello, world!'); | ||
} | ||
const StaticOnly = { | ||
version: 42, | ||
hello() { | ||
// consumers.ts | ||
import { sayHello } from './utilities'; | ||
sayHello(); | ||
``` | ||
### Notes on Mutating Variables | ||
One case you need to be careful of is exporting mutable variables. | ||
While class properties can be mutated externally, exported variables are always constant. | ||
This means that importers can only ever read the first value they are assigned and cannot write to the variables. | ||
Needing to write to an exported variable is very rare and is generally considered a code smell. | ||
If you do need it you can accomplish it using getter and setter functions: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
export class Utilities { | ||
static mutableCount = 1; | ||
static incrementCount() { | ||
Utilities.mutableCount += 1; | ||
} | ||
} | ||
``` | ||
#### ✅ Correct | ||
```ts | ||
let mutableCount = 1; | ||
export function getMutableCount() { | ||
return mutableField; | ||
} | ||
export function incrementCount() { | ||
mutableField += 1; | ||
} | ||
``` | ||
## Options | ||
This rule normally bans classes that are empty (have no constructor or fields). | ||
The rule's options each add an exemption for a specific type of class. | ||
### `allowConstructorOnly` | ||
`allowConstructorOnly` adds an exemption for classes that have only a constructor and no fields. | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
class NoFields {} | ||
``` | ||
#### ✅ Correct | ||
```ts | ||
class NoFields { | ||
constructor() { | ||
console.log('Hello, world!'); | ||
}, | ||
}; | ||
} | ||
} | ||
``` | ||
### Options | ||
### `allowEmpty` | ||
This rule accepts a single object option. | ||
The `allowEmpty` option adds an exemption for classes that are entirely empty. | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
type Options = { | ||
// allow extraneous classes if they only contain a constructor | ||
allowConstructorOnly?: boolean; | ||
// allow extraneous classes if they have no body (i.e. are empty) | ||
allowEmpty?: boolean; | ||
// allow extraneous classes if they only contain static members | ||
allowStaticOnly?: boolean; | ||
// allow extraneous classes if they have a decorator | ||
allowWithDecorator?: boolean; | ||
}; | ||
class NoFields { | ||
constructor() { | ||
console.log('Hello, world!'); | ||
} | ||
} | ||
``` | ||
const defaultOptions: Options = { | ||
allowConstructorOnly: false, | ||
allowEmpty: false, | ||
allowStaticOnly: false, | ||
allowWithDecorator: false, | ||
}; | ||
#### ✅ Correct | ||
```ts | ||
class NoFields {} | ||
``` | ||
## When Not To Use It | ||
### `allowStaticOnly` | ||
You can disable this rule if you don’t have anyone who would make these kinds of mistakes on your | ||
team or if you use classes as namespaces. | ||
The `allowStaticOnly` option adds an exemption for classes that only contain static members. | ||
## Compatibility | ||
:::caution | ||
We strongly recommend against the `allowStaticOnly` exemption. | ||
It works against this rule's primary purpose of discouraging classes used only for static members. | ||
::: | ||
[`no-unnecessary-class`](https://palantir.github.io/tslint/rules/no-unnecessary-class/) from TSLint | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
class EmptyClass {} | ||
``` | ||
#### ✅ Correct | ||
```ts | ||
class NotEmptyClass { | ||
static version = 42; | ||
} | ||
``` | ||
### `allowWithDecorator` | ||
The `allowWithDecorator` option adds an exemption for classes that contain a member decorated with a `@` decorator. | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
class Constants { | ||
static readonly version = 42; | ||
} | ||
``` | ||
#### ✅ Correct | ||
```ts | ||
class Constants { | ||
@logOnRead() | ||
static readonly version = 42; | ||
} | ||
``` | ||
## When Not To Use It | ||
You can disable this rule if you are unable -or unwilling- to switch off using classes as namespaces. |
@@ -1,13 +0,31 @@ | ||
# Requires Promise-like values to be handled appropriately (`no-floating-promises`) | ||
--- | ||
description: 'Require Promise-like statements to be handled appropriately.' | ||
--- | ||
This rule forbids usage of Promise-like values in statements without handling | ||
their errors appropriately. Unhandled promises can cause several issues, such | ||
as improperly sequenced operations, ignored Promise rejections and more. Valid | ||
ways of handling a Promise-valued statement include `await`ing, returning, and | ||
either calling `.then()` with two arguments or `.catch()` with one argument. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-floating-promises** for documentation. | ||
## Rule Details | ||
A "floating" Promise is one that is created without any code set up to handle any errors it might throw. | ||
Floating Promises can cause several issues, such as improperly sequenced operations, ignored Promise rejections, and more. | ||
Examples of **incorrect** code for this rule: | ||
This rule reports when a Promise is created and not properly handled. | ||
Valid ways of handling a Promise-valued statement include: | ||
- `await`ing it | ||
- `return`ing it | ||
- Calling its `.then()` with two arguments | ||
- Calling its `.catch()` with one argument | ||
:::tip | ||
`no-floating-promises` only detects unhandled Promise _statements_. | ||
See [`no-misused-promises`](./no-misused-promises.md) for detecting code that provides Promises to _logical_ locations such as if statements. | ||
::: | ||
## Examples | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -27,3 +45,3 @@ const promise = new Promise((resolve, reject) => resolve('value')); | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -49,18 +67,2 @@ ```ts | ||
The rule accepts an options object with the following properties: | ||
```ts | ||
type Options = { | ||
// if true, checking void expressions will be skipped | ||
ignoreVoid?: boolean; | ||
// if true, checking for async iife will be skipped | ||
ignoreIIFE?: boolean; | ||
}; | ||
const defaults = { | ||
ignoreVoid: true, | ||
ignoreIIFE: false, | ||
}; | ||
``` | ||
### `ignoreVoid` | ||
@@ -82,7 +84,7 @@ | ||
With this option set to `true`, and if you are using `no-void`, you should turn on the [`allowAsAStatement`](https://eslint.org/docs/rules/no-void#allowasstatement) option. | ||
With this option set to `true`, and if you are using `no-void`, you should turn on the [`allowAsStatement`](https://eslint.org/docs/rules/no-void#allowasstatement) option. | ||
### `ignoreIIFE` | ||
This allows you to skip checking of async iife | ||
This allows you to skip checking of async IIFEs (Immediately Invocated function Expressions). | ||
@@ -105,4 +107,4 @@ Examples of **correct** code for this rule with `{ ignoreIIFE: true }`: | ||
## Related to | ||
## Related To | ||
- TSLint: ['no-floating-promises'](https://palantir.github.io/tslint/rules/no-floating-promises/) | ||
- [`no-misused-promises`](./no-misused-promises.md) |
@@ -1,36 +0,52 @@ | ||
# Disallow iterating over an array with a for-in loop (`no-for-in-array`) | ||
--- | ||
description: 'Disallow iterating over an array with a for-in loop.' | ||
--- | ||
This rule prohibits iterating over an array with a for-in loop. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-for-in-array** for documentation. | ||
## Rule Details | ||
Rationale from TSLint: | ||
A for-in loop (`for (var k in o)`) iterates over the properties of an Object. | ||
A for-in loop (`for (var i in o)`) iterates over the properties of an Object. | ||
While it is legal to use for-in loops with array types, it is not common. | ||
for-in will iterate over the indices of the array as strings, omitting any "holes" in | ||
the array. | ||
More common is to use for-of, which iterates over the values of an array. | ||
If you want to iterate over the indices, alternatives include: | ||
```js | ||
array.forEach((value, index) => { ... }); | ||
for (const [index, value] of array.entries()) { ... } | ||
for (let i = 0; i < array.length; i++) { ... } | ||
``` | ||
## Examples | ||
Examples of **incorrect** code for this rule: | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```js | ||
for (const x in [3, 4, 5]) { | ||
console.log(x); | ||
declare const array: string[]; | ||
for (const i in array) { | ||
console.log(array[i]); | ||
} | ||
for (const i in array) { | ||
console.log(i, array[i]); | ||
} | ||
``` | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
```js | ||
for (const x in { a: 3, b: 4, c: 5 }) { | ||
console.log(x); | ||
declare const array: string[]; | ||
for (const value of array) { | ||
console.log(value); | ||
} | ||
for (let i = 0; i < array.length; i += 1) { | ||
console.log(i, array[i]); | ||
} | ||
array.forEach((value, i) => { | ||
console.log(i, value); | ||
}) | ||
for (const [i, value] of array.entries()) { | ||
console.log(i, value); | ||
} | ||
``` | ||
@@ -41,5 +57,1 @@ | ||
If you want to iterate through a loop using the indices in an array as strings, you can turn off this rule. | ||
## Related to | ||
- TSLint: ['no-for-in-array'](https://palantir.github.io/tslint/rules/no-for-in-array/) |
@@ -1,3 +0,14 @@ | ||
# Disallow usage of the implicit `any` type in catch clauses (`no-implicit-any-catch`) | ||
--- | ||
description: 'Disallow usage of the implicit `any` type in catch clauses.' | ||
--- | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-implicit-any-catch** for documentation. | ||
:::danger Deprecated | ||
This rule has been deprecated as TypeScript versions >=4 includes a `useUnknownInCatchVariables` compiler option with the same check. | ||
::: | ||
TypeScript 4.0 added support for adding an explicit `any` or `unknown` type annotation on a catch clause variable. | ||
@@ -7,10 +18,14 @@ | ||
The `noImplicitAny` flag in TypeScript does not cover this for backwards compatibility reasons. | ||
The `noImplicitAny` flag in TypeScript does not cover this for backwards compatibility reasons, however you can use `useUnknownInCatchVariables` (part of `strict`) instead of this rule. | ||
## Rule Details | ||
## DEPRECATED | ||
## Examples | ||
This rule requires an explicit type to be declared on a catch clause variable. | ||
The following pattern is considered a warning: | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -24,3 +39,3 @@ try { | ||
The following pattern is **_not_** considered a warning: | ||
### ✅ Correct | ||
@@ -42,15 +57,2 @@ <!-- TODO: prettier currently removes the type annotations, re-enable this once prettier is updated --> | ||
The rule accepts an options object with the following properties: | ||
```ts | ||
type Options = { | ||
// if false, disallow specifying `: any` as the error type as well. See also `no-explicit-any` | ||
allowExplicitAny: boolean; | ||
}; | ||
const defaults = { | ||
allowExplicitAny: false, | ||
}; | ||
``` | ||
### `allowExplicitAny` | ||
@@ -60,5 +62,2 @@ | ||
<!-- TODO: prettier currently removes the type annotations, re-enable this once prettier is updated --> | ||
<!-- prettier-ignore-start --> | ||
```ts | ||
@@ -72,4 +71,2 @@ try { | ||
<!-- prettier-ignore-end --> | ||
## When Not To Use It | ||
@@ -81,2 +78,2 @@ | ||
- [TypeScript 4.0 Beta Release Notes](https://devblogs.microsoft.com/typescript/announcing-typescript-4-0-beta/#unknown-on-catch) | ||
- [TypeScript 4.0 Release Notes](https://devblogs.microsoft.com/typescript/announcing-typescript-4-0/#unknown-on-catch) |
@@ -1,3 +0,9 @@ | ||
# Disallow the use of `eval()`-like methods (`no-implied-eval`) | ||
--- | ||
description: 'Disallow the use of `eval()`-like methods.' | ||
--- | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-implied-eval** for documentation. | ||
It's considered a good practice to avoid using `eval()`. There are security and performance implications involved with doing so, which is why many linters recommend disallowing `eval()`. However, there are some other ways to pass a string and have it interpreted as JavaScript code that have similar concerns. | ||
@@ -22,8 +28,10 @@ | ||
## Rule Details | ||
## Examples | ||
This rule aims to eliminate implied `eval()` through the use of `new Function()`, `setTimeout()`, `setInterval()`, `setImmediate()` or `execScript()`. | ||
Examples of **incorrect** code for this rule: | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -55,3 +63,3 @@ /* eslint @typescript-eslint/no-implied-eval: "error" */ | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -93,16 +101,4 @@ ```ts | ||
## How to use | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"no-implied-eval": "off", | ||
"@typescript-eslint/no-implied-eval": ["error"] | ||
} | ||
``` | ||
## When Not To Use It | ||
If you want to allow `new Function()` or `setTimeout()`, `setInterval()`, `setImmediate()` and `execScript()` with string arguments, then you can safely disable this rule. | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-implied-eval.md)</sup> |
@@ -1,60 +0,60 @@ | ||
# Disallows explicit type declarations for variables or parameters initialized to a number, string, or boolean (`no-inferrable-types`) | ||
--- | ||
description: 'Disallow explicit type declarations for variables or parameters initialized to a number, string, or boolean.' | ||
--- | ||
Explicit types where they can be easily inferred may add unnecessary verbosity. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-inferrable-types** for documentation. | ||
## Rule Details | ||
TypeScript is able to infer the types of parameters, properties, and variables from their default or initial values. | ||
There is no need to use an explicit `:` type annotation on one of those constructs initialized to a boolean, number, or string. | ||
Doing so adds unnecessary verbosity to code -making it harder to read- and in some cases can prevent TypeScript from inferring a more specific literal type (e.g. `10`) instead of the more general primitive type (e.g. `number`) | ||
This rule disallows explicit type declarations on parameters, variables | ||
and properties where the type can be easily inferred from its value. | ||
## Examples | ||
## Options | ||
<!--tabs--> | ||
This rule accepts the following options: | ||
### ❌ Incorrect | ||
```ts | ||
interface Options { | ||
ignoreParameters?: boolean; | ||
ignoreProperties?: boolean; | ||
const a: bigint = 10n; | ||
const a: bigint = BigInt(10); | ||
const a: boolean = !0; | ||
const a: boolean = Boolean(null); | ||
const a: boolean = true; | ||
const a: null = null; | ||
const a: number = 10; | ||
const a: number = Infinity; | ||
const a: number = NaN; | ||
const a: number = Number('1'); | ||
const a: RegExp = /a/; | ||
const a: RegExp = new RegExp('a'); | ||
const a: string = `str`; | ||
const a: string = String(1); | ||
const a: symbol = Symbol('a'); | ||
const a: undefined = undefined; | ||
const a: undefined = void someValue; | ||
class Foo { | ||
prop: number = 5; | ||
} | ||
``` | ||
### Default | ||
The default options are: | ||
```JSON | ||
{ | ||
"ignoreParameters": false, | ||
"ignoreProperties": false, | ||
} | ||
function fn(a: number = 5, b: boolean = true) {} | ||
``` | ||
With these options, the following patterns are valid: | ||
### ✅ Correct | ||
```ts | ||
const a = 10n; | ||
const a = -10n; | ||
const a = BigInt(10); | ||
const a = -BigInt(10); | ||
const a = false; | ||
const a = !0; | ||
const a = Boolean(null); | ||
const a = true; | ||
const a = Boolean(null); | ||
const a = !0; | ||
const a = null; | ||
const a = 10; | ||
const a = +10; | ||
const a = -10; | ||
const a = Number('1'); | ||
const a = +Number('1'); | ||
const a = -Number('1'); | ||
const a = Infinity; | ||
const a = +Infinity; | ||
const a = -Infinity; | ||
const a = NaN; | ||
const a = +NaN; | ||
const a = -NaN; | ||
const a = null; | ||
const a = Number('1'); | ||
const a = /a/; | ||
const a = RegExp('a'); | ||
const a = new RegExp('a'); | ||
const a = 'str'; | ||
const a = `str`; | ||
@@ -71,47 +71,8 @@ const a = String(1); | ||
function fn(a = 5, b = true) {} | ||
function fn(a: number, b: boolean, c: string) {} | ||
``` | ||
The following are invalid: | ||
<!--/tabs--> | ||
```ts | ||
const a: bigint = 10n; | ||
const a: bigint = -10n; | ||
const a: bigint = BigInt(10); | ||
const a: bigint = -BigInt(10); | ||
const a: boolean = false; | ||
const a: boolean = true; | ||
const a: boolean = Boolean(null); | ||
const a: boolean = !0; | ||
const a: number = 10; | ||
const a: number = +10; | ||
const a: number = -10; | ||
const a: number = Number('1'); | ||
const a: number = +Number('1'); | ||
const a: number = -Number('1'); | ||
const a: number = Infinity; | ||
const a: number = +Infinity; | ||
const a: number = -Infinity; | ||
const a: number = NaN; | ||
const a: number = +NaN; | ||
const a: number = -NaN; | ||
const a: null = null; | ||
const a: RegExp = /a/; | ||
const a: RegExp = RegExp('a'); | ||
const a: RegExp = new RegExp('a'); | ||
const a: string = 'str'; | ||
const a: string = `str`; | ||
const a: string = String(1); | ||
const a: symbol = Symbol('a'); | ||
const a: undefined = undefined; | ||
const a: undefined = void someValue; | ||
## Options | ||
class Foo { | ||
prop: number = 5; | ||
} | ||
function fn(a: number = 5, b: boolean = true) {} | ||
``` | ||
### `ignoreParameters` | ||
@@ -144,5 +105,1 @@ | ||
TypeScript [Inference](https://www.typescriptlang.org/docs/handbook/type-inference.html) | ||
## Compatibility | ||
TSLint: [no-inferrable-types](https://palantir.github.io/tslint/rules/no-inferrable-types/) |
@@ -1,26 +0,12 @@ | ||
# Disallow `this` keywords outside of classes or class-like objects (`no-invalid-this`) | ||
--- | ||
description: 'Disallow `this` keywords outside of classes or class-like objects.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-invalid-this** for documentation. | ||
## Examples | ||
This rule extends the base [`eslint/no-invalid-this`](https://eslint.org/docs/rules/no-invalid-this) rule. | ||
It adds support for TypeScript's `this` parameters. | ||
## How to use | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"no-invalid-this": "off", | ||
"@typescript-eslint/no-invalid-this": ["error"] | ||
} | ||
``` | ||
## Options | ||
See [`eslint/no-invalid-this` options](https://eslint.org/docs/rules/no-invalid-this#options). | ||
## When Not To Use It | ||
When you are indifferent as to how your variables are initialized. | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-invalid-this.md)</sup> |
@@ -1,18 +0,21 @@ | ||
# Disallows usage of `void` type outside of generic or return types (`no-invalid-void-type`) | ||
--- | ||
description: 'Disallow `void` type outside of generic or return types.' | ||
--- | ||
Disallows usage of `void` type outside of return types or generic type arguments. | ||
If `void` is used as return type, it shouldn’t be a part of intersection/union type with most other types. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-invalid-void-type** for documentation. | ||
## Rationale | ||
`void` in TypeScript refers to a function return that is meant to be ignored. | ||
Attempting to use a `void` type outside of a return type or generic type argument is often a sign of programmer error. | ||
`void` can also be misleading for other developers even if used correctly. | ||
The `void` type means “nothing” or that a function does not return any value, | ||
in contrast with implicit `undefined` type which means that a function returns a value `undefined`. | ||
So “nothing” cannot be mixed with any other types, other than `never`, which accepts all types. | ||
If you need this - use the `undefined` type instead. | ||
> The `void` type means cannot be mixed with any other types, other than `never`, which accepts all types. | ||
> If you think you need this then you probably want the `undefined` type instead. | ||
## Rule Details | ||
## Examples | ||
This rule aims to ensure that the `void` type is only used in valid places. | ||
<!--tabs--> | ||
The following patterns are considered warnings: | ||
### ❌ Incorrect | ||
@@ -38,3 +41,3 @@ ```ts | ||
The following patterns are not considered warnings: | ||
### ✅ Correct | ||
@@ -53,18 +56,6 @@ ```ts | ||
### Options | ||
## Options | ||
```ts | ||
interface Options { | ||
allowInGenericTypeArguments?: boolean | string[]; | ||
allowAsThisParameter?: boolean; | ||
} | ||
### `allowInGenericTypeArguments` | ||
const defaultOptions: Options = { | ||
allowInGenericTypeArguments: true, | ||
allowAsThisParameter: false, | ||
}; | ||
``` | ||
#### `allowInGenericTypeArguments` | ||
This option lets you control if `void` can be used as a valid value for generic type parameters. | ||
@@ -104,3 +95,3 @@ | ||
#### `allowAsThisParameter` | ||
### `allowAsThisParameter` | ||
@@ -126,5 +117,1 @@ This option allows specifying a `this` parameter of a function to be `void` when set to `true`. | ||
or in invalid places, then you don't need this rule. | ||
## Compatibility | ||
- TSLint: [invalid-void](https://palantir.github.io/tslint/rules/invalid-void/) |
@@ -1,22 +0,12 @@ | ||
# Disallow function declarations that contain unsafe references inside loop statements (`no-loop-func`) | ||
--- | ||
description: 'Disallow function declarations that contain unsafe references inside loop statements.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-loop-func** for documentation. | ||
## Examples | ||
This rule extends the base [`eslint/no-loop-func`](https://eslint.org/docs/rules/no-loop-func) rule. | ||
It adds support for TypeScript types. | ||
## How to use | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"no-loop-func": "off", | ||
"@typescript-eslint/no-loop-func": ["error"] | ||
} | ||
``` | ||
## Options | ||
See [`eslint/no-loop-func` options](https://eslint.org/docs/rules/no-loop-func#options). | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-loop-func.md)</sup> |
@@ -1,19 +0,12 @@ | ||
# Disallow literal numbers that lose precision (`no-loss-of-precision`) | ||
--- | ||
description: 'Disallow literal numbers that lose precision.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-loss-of-precision** for documentation. | ||
## Examples | ||
This rule extends the base [`eslint/no-loss-of-precision`](https://eslint.org/docs/rules/no-loss-of-precision) rule. | ||
It adds support for [numeric separators](https://github.com/tc39/proposal-numeric-separator). | ||
Note that this rule requires ESLint v7. | ||
## How to use | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"no-loss-of-precision": "off", | ||
"@typescript-eslint/no-loss-of-precision": ["error"] | ||
} | ||
``` | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-loss-of-precision.md)</sup> |
@@ -1,5 +0,11 @@ | ||
# Disallow magic numbers (`no-magic-numbers`) | ||
--- | ||
description: 'Disallow magic numbers.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-magic-numbers** for documentation. | ||
## Examples | ||
This rule extends the base [`eslint/no-magic-numbers`](https://eslint.org/docs/rules/no-magic-numbers) rule. | ||
@@ -12,20 +18,4 @@ It adds support for: | ||
## How to use | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"no-magic-numbers": "off", | ||
"@typescript-eslint/no-magic-numbers": [ | ||
"error", | ||
{ | ||
/* options */ | ||
} | ||
] | ||
} | ||
``` | ||
## Options | ||
See [`eslint/no-magic-numbers` options](https://eslint.org/docs/rules/no-magic-numbers#options). | ||
This rule adds the following options: | ||
@@ -38,2 +28,3 @@ | ||
ignoreReadonlyClassProperties?: boolean; | ||
ignoreTypeIndexes?: boolean; | ||
} | ||
@@ -46,2 +37,3 @@ | ||
ignoreReadonlyClassProperties: false, | ||
ignoreTypeIndexes: false, | ||
}; | ||
@@ -59,4 +51,4 @@ ``` | ||
enum foo = { | ||
SECOND = 1000, | ||
enum foo { | ||
SECOND = 1000, | ||
} | ||
@@ -70,4 +62,4 @@ ``` | ||
enum foo = { | ||
SECOND = 1000, | ||
enum foo { | ||
SECOND = 1000, | ||
} | ||
@@ -124,2 +116,22 @@ ``` | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-magic-numbers.md)</sup> | ||
### `ignoreTypeIndexes` | ||
A boolean to specify if numbers used to index types are okay. `false` by default. | ||
Examples of **incorrect** code for the `{ "ignoreTypeIndexes": false }` option: | ||
```ts | ||
/*eslint @typescript-eslint/no-magic-numbers: ["error", { "ignoreTypeIndexes": false }]*/ | ||
type Foo = Bar[0]; | ||
type Baz = Parameters<Foo>[2]; | ||
``` | ||
Examples of **correct** code for the `{ "ignoreTypeIndexes": true }` option: | ||
```ts | ||
/*eslint @typescript-eslint/no-magic-numbers: ["error", { "ignoreTypeIndexes": true }]*/ | ||
type Foo = Bar[0]; | ||
type Baz = Parameters<Foo>[2]; | ||
``` |
@@ -1,11 +0,23 @@ | ||
# Enforce valid definition of `new` and `constructor` (`no-misused-new`) | ||
--- | ||
description: 'Enforce valid definition of `new` and `constructor`.' | ||
--- | ||
Warns on apparent attempts to define constructors for interfaces or `new` for classes. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-misused-new** for documentation. | ||
## Rule Details | ||
JavaScript classes may define a `constructor` method that runs when a class instance is newly created. | ||
TypeScript allows interfaces that describe a static class object to define a `new()` method (though this is rarely used in real world code). | ||
Developers new to JavaScript classes and/or TypeScript interfaces may sometimes confuse when to use `constructor` or `new`. | ||
Examples of **incorrect** code for this rule. | ||
This rule reports when a class defines a method named `new` or an interface defines a method named `constructor`. | ||
## Examples | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
class C { | ||
declare class C { | ||
new(): C; | ||
@@ -20,8 +32,9 @@ } | ||
Examples of **correct** code for this rule. | ||
### ✅ Correct | ||
```ts | ||
class C { | ||
constructor() {} | ||
declare class C { | ||
constructor(); | ||
} | ||
interface I { | ||
@@ -32,12 +45,4 @@ new (): C; | ||
## Options | ||
## When Not To Use It | ||
```json | ||
{ | ||
"@typescript-eslint/no-misused-new": "error" | ||
} | ||
``` | ||
## Compatibility | ||
- TSLint: [no-misused-new](https://palantir.github.io/tslint/rules/no-misused-new/) | ||
If you intentionally want a class with a `new` method, and you're confident nobody working in your code will mistake it with a constructor. |
@@ -1,12 +0,43 @@ | ||
# Avoid using promises in places not designed to handle them (`no-misused-promises`) | ||
--- | ||
description: 'Disallow Promises in places not designed to handle them.' | ||
--- | ||
This rule forbids using promises in places where the TypeScript compiler | ||
allows them but they are not handled properly. These situations can often arise | ||
due to a missing `await` keyword or just a misunderstanding of the way async | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-misused-promises** for documentation. | ||
This rule forbids providing Promises to logical locations such as if statements in places where the TypeScript compiler allows them but they are not handled properly. | ||
These situations can often arise due to a missing `await` keyword or just a misunderstanding of the way async | ||
functions are handled/awaited. | ||
## Rule Details | ||
:::tip | ||
`no-misused-promises` only detects code that provides Promises to incorrect _logical_ locations. | ||
See [`no-floating-promises`](./no-floating-promises.md) for detecting unhandled Promise _statements_. | ||
::: | ||
Examples of **incorrect** code for this rule with `checksConditionals: true`: | ||
## Options | ||
### `"checksConditionals"` | ||
If you don't want to check conditionals, you can configure the rule with `"checksConditionals": false`: | ||
```json | ||
{ | ||
"@typescript-eslint/no-misused-promises": [ | ||
"error", | ||
{ | ||
"checksConditionals": false | ||
} | ||
] | ||
} | ||
``` | ||
Doing so prevents the rule from looking at code like `if (somePromise)`. | ||
Examples of code for this rule with `checksConditionals: true`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -26,5 +57,69 @@ const promise = Promise.resolve('value'); | ||
Examples of **incorrect** code for this rule with `checksVoidReturn: true`: | ||
#### ✅ Correct | ||
```ts | ||
const promise = Promise.resolve('value'); | ||
// Always `await` the Promise in a conditional | ||
if (await promise) { | ||
// Do something | ||
} | ||
const val = (await promise) ? 123 : 456; | ||
while (await promise) { | ||
// Do something | ||
} | ||
``` | ||
<!--/tabs--> | ||
### `"checksVoidReturn"` | ||
Likewise, if you don't want functions that return promises where a void return is | ||
expected to be checked, your configuration will look like this: | ||
```json | ||
{ | ||
"@typescript-eslint/no-misused-promises": [ | ||
"error", | ||
{ | ||
"checksVoidReturn": false | ||
} | ||
] | ||
} | ||
``` | ||
You can disable selective parts of the `checksVoidReturn` option by providing an object that disables specific checks. | ||
The following options are supported: | ||
- `arguments`: Disables checking an asynchronous function passed as argument where the parameter type expects a function that returns `void` | ||
- `attributes`: Disables checking an asynchronous function passed as a JSX attribute expected to be a function that returns `void` | ||
- `properties`: Disables checking an asynchronous function passed as an object property expected to be a function that returns `void` | ||
- `returns`: Disables checking an asynchronous function returned in a function whose return type is a function that returns `void` | ||
- `variables`: Disables checking an asynchronous function used as a variable whose return type is a function that returns `void` | ||
For example, if you don't mind that passing a `() => Promise<void>` to a `() => void` parameter or JSX attribute can lead to a floating unhandled Promise: | ||
```json | ||
{ | ||
"@typescript-eslint/no-misused-promises": [ | ||
"error", | ||
{ | ||
"checksVoidReturn": { | ||
"arguments": false, | ||
"attributes": false | ||
} | ||
} | ||
] | ||
} | ||
``` | ||
Examples of code for this rule with `checksVoidReturn: true`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
[1, 2, 3].forEach(async value => { | ||
@@ -41,21 +136,12 @@ await doSomething(value); | ||
eventEmitter.on('some-event', async () => { | ||
synchronousCall(); | ||
await doSomething(); | ||
otherSynchronousCall(); | ||
}); | ||
``` | ||
Examples of **correct** code for this rule: | ||
#### ✅ Correct | ||
```ts | ||
const promise = Promise.resolve('value'); | ||
if (await promise) { | ||
// Do something | ||
} | ||
const val = (await promise) ? 123 : 456; | ||
while (await promise) { | ||
// Do something | ||
} | ||
// for-of puts `await` in outer context | ||
for (const value of [1, 2, 3]) { | ||
@@ -65,21 +151,41 @@ await doSomething(value); | ||
// If outer context is not `async`, handle error explicitly | ||
Promise.all( | ||
[1, 2, 3].map(async value => { | ||
await doSomething(value); | ||
}), | ||
).catch(handleError); | ||
// Use an async IIFE wrapper | ||
new Promise((resolve, reject) => { | ||
// Do something | ||
resolve(); | ||
// combine with `void` keyword to tell `no-floating-promises` rule to ignore unhandled rejection | ||
void (async () => { | ||
await doSomething(); | ||
resolve(); | ||
})(); | ||
}); | ||
// Name the async wrapper to call it later | ||
const eventEmitter = new EventEmitter(); | ||
eventEmitter.on('some-event', () => { | ||
doSomething(); | ||
const handler = async () => { | ||
await doSomething(); | ||
otherSynchronousCall(); | ||
}; | ||
try { | ||
synchronousCall(); | ||
} catch (err) { | ||
handleSpecificError(err); | ||
} | ||
handler().catch(handleError); | ||
}); | ||
``` | ||
## Options | ||
<!--/tabs--> | ||
This rule accepts a single option which is an object with `checksConditionals` | ||
and `checksVoidReturn` properties indicating which types of misuse to flag. | ||
Both are enabled by default | ||
### `"checksSpreads"` | ||
If you don't want functions that return promises where a void return is | ||
expected to be checked, your configuration will look like this: | ||
If you don't want to check object spreads, you can add this configuration: | ||
@@ -91,3 +197,3 @@ ```json | ||
{ | ||
"checksVoidReturn": false | ||
"checksSpreads": false | ||
} | ||
@@ -98,16 +204,36 @@ ] | ||
Likewise, if you don't want to check conditionals, you can configure the rule | ||
like this: | ||
Examples of code for this rule with `checksSpreads: true`: | ||
```json | ||
{ | ||
"@typescript-eslint/no-misused-promises": [ | ||
"error", | ||
{ | ||
"checksConditionals": false | ||
} | ||
] | ||
} | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
const getData = () => someAsyncOperation({ myArg: 'foo' }); | ||
return { foo: 42, ...getData() }; | ||
const getData2 = async () => { | ||
await someAsyncOperation({ myArg: 'foo' }); | ||
}; | ||
return { foo: 42, ...getData2() }; | ||
``` | ||
#### ✅ Correct | ||
```ts | ||
const getData = () => someAsyncOperation({ myArg: 'foo' }); | ||
return { foo: 42, ...(await getData()) }; | ||
const getData2 = async () => { | ||
await someAsyncOperation({ myArg: 'foo' }); | ||
}; | ||
return { foo: 42, ...(await getData2()) }; | ||
``` | ||
<!--tabs--> | ||
## When Not To Use It | ||
@@ -118,8 +244,8 @@ | ||
## Related to | ||
## Further Reading | ||
- [`no-floating-promises`](./no-floating-promises.md) | ||
- [TypeScript void function assignability](https://github.com/Microsoft/TypeScript/wiki/FAQ#why-are-functions-returning-non-void-assignable-to-function-returning-void) | ||
## Further Reading | ||
## Related To | ||
- [TypeScript void function assignability](https://github.com/Microsoft/TypeScript/wiki/FAQ#why-are-functions-returning-non-void-assignable-to-function-returning-void) | ||
- [`no-floating-promises`](./no-floating-promises.md) |
@@ -1,23 +0,23 @@ | ||
# Disallow the use of custom TypeScript modules and namespaces (`no-namespace`) | ||
--- | ||
description: 'Disallow TypeScript namespaces.' | ||
--- | ||
Custom TypeScript modules (`module foo {}`) and namespaces (`namespace foo {}`) are considered outdated | ||
ways to organize TypeScript code. ES2015 module syntax is now preferred (`import`/`export`). | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-namespace** for documentation. | ||
This rule still allows the use of TypeScript module declarations to describe external APIs (`declare module 'foo' {}`). | ||
TypeScript historically allowed a form of code organization called "custom modules" (`module Example {}`), later renamed to "namespaces" (`namespace Example`). | ||
Namespaces are an outdated way to organize TypeScript code. | ||
ES2015 module syntax is now preferred (`import`/`export`). | ||
## Rule Details | ||
> This rule does not report on the use of TypeScript module declarations to describe external APIs (`declare module 'foo' {}`). | ||
This rule aims to standardize the way modules are declared. | ||
## Examples | ||
## Options | ||
Examples of code with the default options: | ||
This rule, in its default state, does not require any argument. If you would like to enable one | ||
or more of the following you may pass an object with the options set as follows: | ||
<!--tabs--> | ||
- `allowDeclarations` set to `true` will allow you to `declare` custom TypeScript modules and namespaces (Default: `false`). | ||
- `allowDefinitionFiles` set to `true` will allow you to `declare` and use custom TypeScript modules and namespaces | ||
inside definition files (Default: `true`). | ||
### ❌ Incorrect | ||
Examples of **incorrect** code for the default `{ "allowDeclarations": false, "allowDefinitionFiles": true }` options: | ||
```ts | ||
@@ -31,3 +31,3 @@ module foo {} | ||
Examples of **correct** code for the default `{ "allowDeclarations": false, "allowDefinitionFiles": true }` options: | ||
### ✅ Correct | ||
@@ -40,6 +40,14 @@ ```ts | ||
<!--/tabs--> | ||
## Options | ||
### `allowDeclarations` | ||
Examples of **incorrect** code for the `{ "allowDeclarations": true }` option: | ||
Examples of code with the `{ "allowDeclarations": true }` option: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -50,3 +58,3 @@ module foo {} | ||
Examples of **correct** code for the `{ "allowDeclarations": true }` option: | ||
#### ✅ Correct | ||
@@ -67,4 +75,10 @@ ```ts | ||
Examples of **incorrect** code for the `{ "allowDeclarations": false }` option: | ||
<!--/tabs--> | ||
Examples of code for the `{ "allowDeclarations": false }` option: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -77,3 +91,3 @@ module foo {} | ||
Examples of **correct** code for the `{ "allowDeclarations": false }` option: | ||
#### ✅ Correct | ||
@@ -86,4 +100,8 @@ ```ts | ||
Examples of **incorrect** code for the `{ "allowDefinitionFiles": true }` option: | ||
Examples of code for the `{ "allowDefinitionFiles": true }` option: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -101,3 +119,3 @@ // if outside a d.ts file | ||
Examples of **correct** code for the `{ "allowDefinitionFiles": true }` option: | ||
#### ✅ Correct | ||
@@ -119,5 +137,1 @@ ```ts | ||
- [Namespaces and Modules](https://www.typescriptlang.org/docs/handbook/namespaces-and-modules.html) | ||
## Compatibility | ||
- TSLint: [no-namespace](https://palantir.github.io/tslint/rules/no-namespace/) |
@@ -1,45 +0,32 @@ | ||
# Disallows using a non-null assertion after an optional chain expression (`no-non-null-asserted-optional-chain`) | ||
--- | ||
description: 'Disallow non-null assertions after an optional chain expression.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-non-null-asserted-optional-chain** for documentation. | ||
Optional chain expressions are designed to return `undefined` if the optional property is nullish. | ||
Using non-null assertions after an optional chain expression is wrong, and introduces a serious type safety hole into your code. | ||
`?.` optional chain expressions provide `undefined` if an object is `null` or `undefined`. | ||
Using a `!` non-null assertion to assert the result of an `?.` optional chain expression is non-nullable is likely wrong. | ||
Examples of **incorrect** code for this rule: | ||
> Most of the time, either the object was not nullable and did not need the `?.` for its property lookup, or the `!` is incorrect and introducing a type safety hole. | ||
## Examples | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
/* eslint @typescript-eslint/no-non-null-asserted-optional-chain: "error" */ | ||
foo?.bar!; | ||
foo?.bar()!; | ||
// Prior to TS3.9, foo?.bar!.baz meant (foo?.bar).baz - i.e. the non-null assertion is applied to the entire chain so far. | ||
// For TS3.9 and greater, the non-null assertion is only applied to the property itself, so it's safe. | ||
// The following is incorrect code if you're using less than TS3.9 | ||
foo?.bar!.baz; | ||
foo?.bar!(); | ||
foo?.bar!().baz; | ||
``` | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
```ts | ||
/* eslint @typescript-eslint/no-non-null-asserted-optional-chain: "error" */ | ||
foo?.bar; | ||
(foo?.bar).baz; | ||
foo?.bar(); | ||
foo?.bar(); | ||
foo?.bar().baz; | ||
// The following is correct code if you're using TS3.9 or greater | ||
foo?.bar!.baz; | ||
foo?.bar!(); | ||
foo?.bar!().baz; | ||
``` | ||
## When Not To Use It | ||
If you are not using TypeScript 3.7 (or greater), then you will not need to use this rule, as the operator is not supported. | ||
## Further Reading | ||
@@ -46,0 +33,0 @@ |
@@ -1,27 +0,37 @@ | ||
# Disallows non-null assertions using the `!` postfix operator (`no-non-null-assertion`) | ||
--- | ||
description: 'Disallow non-null assertions using the `!` postfix operator.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-non-null-assertion** for documentation. | ||
Using non-null assertions cancels the benefits of the strict null-checking mode. | ||
TypeScript's `!` non-null assertion operator asserts to the type system that an expression is non-nullable, as in not `null` or `undefined`. | ||
Using assertions to tell the type system new information is often a sign that code is not fully type-safe. | ||
It's generally better to structure program logic so that TypeScript understands when values may be nullable. | ||
Examples of **incorrect** code for this rule: | ||
## Examples | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
interface Foo { | ||
bar?: string; | ||
interface Example { | ||
property?: string; | ||
} | ||
const foo: Foo = getFoo(); | ||
const includesBaz: boolean = foo.bar!.includes('baz'); | ||
declare const example: Example; | ||
const includesBaz = example.property!.includes('baz'); | ||
``` | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
```ts | ||
interface Foo { | ||
bar?: string; | ||
interface Example { | ||
property?: string; | ||
} | ||
const foo: Foo = getFoo(); | ||
const includesBaz: boolean = foo.bar?.includes('baz') ?? false; | ||
declare const example: Example; | ||
const includesBaz = example.property?.includes('baz') ?? false; | ||
``` | ||
@@ -31,6 +41,3 @@ | ||
If you don't care about strict null-checking, then you will not need this rule. | ||
## Further Reading | ||
- [`no-non-null-assertion`](https://palantir.github.io/tslint/rules/no-non-null-assertion/) in [TSLint](https://palantir.github.io/tslint/) | ||
If your project does not use the `strictNullChecks` compiler option, this rule is likely useless to you. | ||
If your code is often wildly incorrect with respect to strict null-checking, your code may not yet be ready for this rule. |
@@ -1,7 +0,18 @@ | ||
# Disallow the use of parameter properties in class constructors (`no-parameter-properties`) | ||
--- | ||
description: 'Disallow the use of parameter properties in class constructors.' | ||
--- | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-parameter-properties** for documentation. | ||
:::danger Deprecated | ||
This rule has been deprecated in favour of the equivalent, better named [`parameter-properties`](./parameter-properties.md) rule. | ||
::: | ||
Parameter properties can be confusing to those new to TypeScript as they are less explicit than other ways | ||
of declaring and initializing class members. | ||
## Rule Details | ||
## Examples | ||
@@ -27,4 +38,8 @@ This rule disallows the use of parameter properties in constructors, forcing the user to explicitly | ||
Examples of **incorrect** code for this rule with no options at all: | ||
Examples of code for this rule with no options at all: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -60,3 +75,3 @@ class Foo { | ||
Examples of **correct** code for this rule with no options at all: | ||
#### ✅ Correct | ||
@@ -71,4 +86,8 @@ ```ts | ||
Examples of **incorrect** code for the `{ "allows": ["readonly"] }` options: | ||
Examples of code for the `{ "allows": ["readonly"] }` options: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -100,3 +119,3 @@ class Foo { | ||
Examples of **correct** code for the `{ "allows": ["readonly"] }` options: | ||
#### ✅ Correct | ||
@@ -115,4 +134,8 @@ ```ts | ||
Examples of **incorrect** code for the `{ "allows": ["private"] }` options: | ||
Examples of code for the `{ "allows": ["private"] }` options: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -144,3 +167,3 @@ class Foo { | ||
Examples of **correct** code for the `{ "allows": ["private"] }` options: | ||
#### ✅ Correct | ||
@@ -159,4 +182,8 @@ ```ts | ||
Examples of **incorrect** code for the `{ "allows": ["protected"] }` options: | ||
Examples of code for the `{ "allows": ["protected"] }` options: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -188,3 +215,3 @@ class Foo { | ||
Examples of **correct** code for the `{ "allows": ["protected"] }` options: | ||
#### ✅ Correct | ||
@@ -203,4 +230,8 @@ ```ts | ||
Examples of **incorrect** code for the `{ "allows": ["public"] }` options: | ||
Examples of code for the `{ "allows": ["public"] }` options: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -232,3 +263,3 @@ class Foo { | ||
Examples of **correct** code for the `{ "allows": ["public"] }` options: | ||
#### ✅ Correct | ||
@@ -247,4 +278,8 @@ ```ts | ||
Examples of **incorrect** code for the `{ "allows": ["private readonly"] }` options: | ||
Examples of code for the `{ "allows": ["private readonly"] }` options: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -276,3 +311,3 @@ class Foo { | ||
Examples of **correct** code for the `{ "allows": ["private readonly"] }` options: | ||
#### ✅ Correct | ||
@@ -291,4 +326,8 @@ ```ts | ||
Examples of **incorrect** code for the `{ "allows": ["protected readonly"] }` options: | ||
Examples of code for the `{ "allows": ["protected readonly"] }` options: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -320,3 +359,3 @@ class Foo { | ||
Examples of **correct** code for the `{ "allows": ["protected readonly"] }` options: | ||
#### ✅ Correct | ||
@@ -335,4 +374,8 @@ ```ts | ||
Examples of **incorrect** code for the `{ "allows": ["public readonly"] }` options: | ||
Examples of code for the `{ "allows": ["public readonly"] }` options: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -364,3 +407,3 @@ class Foo { | ||
Examples of **correct** code for the `{ "allows": ["public readonly"] }` options: | ||
#### ✅ Correct | ||
@@ -380,5 +423,1 @@ ```ts | ||
If you don't care about the using parameter properties in constructors, then you will not need this rule. | ||
## Compatibility | ||
- TSLint: [no-parameter-properties](https://palantir.github.io/tslint/rules/no-parameter-properties/) |
@@ -1,21 +0,16 @@ | ||
# Disallow variable redeclaration (`no-redeclare`) | ||
--- | ||
description: 'Disallow variable redeclaration.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-redeclare** for documentation. | ||
## Examples | ||
This rule extends the base [`eslint/no-redeclare`](https://eslint.org/docs/rules/no-redeclare) rule. | ||
It adds support for TypeScript function overloads, and declaration merging. | ||
## How to use | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"no-redeclare": "off", | ||
"@typescript-eslint/no-redeclare": ["error"] | ||
} | ||
``` | ||
## Options | ||
See [`eslint/no-redeclare` options](https://eslint.org/docs/rules/no-redeclare#options). | ||
This rule adds the following options: | ||
@@ -44,2 +39,3 @@ | ||
- function + namespace | ||
- enum + namespace | ||
@@ -79,3 +75,1 @@ Examples of **correct** code with `{ ignoreDeclarationMerge: true }`: | ||
``` | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-redeclare.md)</sup> |
@@ -1,26 +0,29 @@ | ||
# Disallows invocation of `require()` (`no-require-imports`) | ||
--- | ||
description: 'Disallow invocation of `require()`.' | ||
--- | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-require-imports** for documentation. | ||
Prefer the newer ES6-style imports over `require()`. | ||
## Rule Details | ||
## Examples | ||
Examples of **incorrect** code for this rule: | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
var lib = require('lib'); | ||
let lib2 = require('lib2'); | ||
var lib5 = require('lib5'), | ||
lib6 = require('lib6'); | ||
import lib8 = require('lib8'); | ||
const lib1 = require('lib1'); | ||
const { lib2 } = require('lib2'); | ||
import lib3 = require('lib3'); | ||
``` | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
```ts | ||
import { l } from 'lib'; | ||
var lib3 = load('not_an_import'); | ||
var lib4 = lib2.subImport; | ||
var lib7 = 700; | ||
import lib9 = lib2.anotherSubImport; | ||
import lib10 from 'lib10'; | ||
import * as lib1 from 'lib1'; | ||
import { lib2 } from 'lib2'; | ||
import * as lib3 from 'lib3'; | ||
``` | ||
@@ -30,6 +33,6 @@ | ||
If you don't care about TypeScript module syntax, then you will not need this rule. | ||
If you don't care about using newer module syntax, then you will not need this rule. | ||
## Compatibility | ||
## Related To | ||
- TSLint: [no-require-imports](https://palantir.github.io/tslint/rules/no-require-imports/) | ||
- [`no-var-requires`](./no-var-requires.md) |
@@ -1,21 +0,16 @@ | ||
# Disallow variable declarations from shadowing variables declared in the outer scope (`no-shadow`) | ||
--- | ||
description: 'Disallow variable declarations from shadowing variables declared in the outer scope.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-shadow** for documentation. | ||
## Examples | ||
This rule extends the base [`eslint/no-shadow`](https://eslint.org/docs/rules/no-shadow) rule. | ||
It adds support for TypeScript's `this` parameters and global augmentation, and adds options for TypeScript features. | ||
## How to use | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"no-shadow": "off", | ||
"@typescript-eslint/no-shadow": ["error"] | ||
} | ||
``` | ||
## Options | ||
See [`eslint/no-shadow` options](https://eslint.org/docs/rules/no-shadow#options). | ||
This rule adds the following options: | ||
@@ -87,2 +82,21 @@ | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-shadow.md)</sup> | ||
## FAQ | ||
### Why does the rule report on enum members that share the same name as a variable in a parent scope? | ||
Reporting on this case isn't a bug - it is completely intentional and correct reporting! The rule reports due to a relatively unknown feature of enums - enum members create a variable within the enum scope so that they can be referenced within the enum without a qualifier. | ||
To illustrate this with an example: | ||
```ts | ||
const A = 2; | ||
enum Test { | ||
A = 1, | ||
B = A, | ||
} | ||
console.log(Test.B); | ||
// what should be logged? | ||
``` | ||
Naively looking at the above code, it might look like the log should output `2`, because the outer variable `A`'s value is `2` - however, the code instead outputs `1`, which is the value of `Test.A`. This is because the unqualified code `B = A` is equivalent to the fully-qualified code `B = Test.A`. Due to this behavior, the enum member has **shadowed** the outer variable declaration. |
@@ -1,60 +0,38 @@ | ||
# Disallow aliasing `this` (`no-this-alias`) | ||
--- | ||
description: 'Disallow aliasing `this`.' | ||
--- | ||
This rule prohibits assigning variables to `this`. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-this-alias** for documentation. | ||
## Rule Details | ||
Assigning a variable to `this` instead of properly using arrow lambdas may be a symptom of pre-ES6 practices | ||
or not managing scope well. | ||
Rationale from TSLint: | ||
## Examples | ||
> Assigning a variable to `this` instead of properly using arrow lambdas may be a symptom of pre-ES6 practices | ||
> or not managing scope well. | ||
> | ||
> Instead of storing a reference to `this` and using it inside a `function () {`: | ||
> | ||
> ```js | ||
> const self = this; | ||
> | ||
> setTimeout(function () { | ||
> self.doWork(); | ||
> }); | ||
> ``` | ||
> | ||
> Use `() =>` arrow lambdas, as they preserve `this` scope for you: | ||
> | ||
> ```js | ||
> setTimeout(() => { | ||
> this.doWork(); | ||
> }); | ||
> ``` | ||
<!--tabs--> | ||
Examples of **incorrect** code for this rule: | ||
### ❌ Incorrect | ||
(see the rationale above) | ||
```js | ||
const self = this; | ||
Examples of **correct** code for this rule: | ||
setTimeout(function () { | ||
self.doWork(); | ||
}); | ||
``` | ||
(see the rationale above) | ||
### ✅ Correct | ||
### Options | ||
```js | ||
setTimeout(() => { | ||
this.doWork(); | ||
}); | ||
``` | ||
You can pass an object option: | ||
## Options | ||
```jsonc | ||
{ | ||
"@typescript-eslint/no-this-alias": [ | ||
"error", | ||
{ | ||
"allowDestructuring": true, // Allow `const { props, state } = this`; false by default | ||
"allowedNames": ["self"] // Allow `const self = this`; `[]` by default | ||
} | ||
] | ||
} | ||
``` | ||
## When Not To Use It | ||
If you need to assign `this` to variables, you shouldn’t use this rule. | ||
## Related to | ||
- TSLint: [`no-this-assignment`](https://palantir.github.io/tslint/rules/no-this-assignment/) |
@@ -1,14 +0,22 @@ | ||
# Disallow throwing literals as exceptions (`no-throw-literal`) | ||
--- | ||
description: 'Disallow throwing literals as exceptions.' | ||
--- | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-throw-literal** for documentation. | ||
It is considered good practice to only `throw` the `Error` object itself or an object using the `Error` object as base objects for user-defined exceptions. | ||
The fundamental benefit of `Error` objects is that they automatically keep track of where they were built and originated. | ||
This rule restricts what can be thrown as an exception. When it was first created, it only prevented literals from being thrown (hence the name), but it has now been expanded to only allow expressions which have a possibility of being an `Error` object. | ||
This rule restricts what can be thrown as an exception. When it was first created, it only prevented literals from being thrown (hence the name), but it has now been expanded to only allow expressions which have a possibility of being an `Error` object. With the `allowThrowingAny` and `allowThrowingUnknown`, it can be configured to only allow throwing values which are guaranteed to be an instance of `Error`. | ||
## Rule Details | ||
## Examples | ||
This rule is aimed at maintaining consistency when throwing exception by disallowing to throw literals and other expressions which cannot possibly be an `Error` object. | ||
Examples of **incorrect** code for this rule: | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -45,3 +53,3 @@ /*eslint @typescript-eslint/no-throw-literal: "error"*/ | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -83,14 +91,23 @@ ```ts | ||
## How to use | ||
## Options | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"no-throw-literal": "off", | ||
"@typescript-eslint/no-throw-literal": ["error"] | ||
This rule adds the following options: | ||
```ts | ||
interface Options { | ||
/** | ||
* Whether to always allow throwing values typed as `any`. | ||
*/ | ||
allowThrowingAny?: boolean; | ||
/** | ||
* Whether to always allow throwing values typed as `unknown`. | ||
*/ | ||
allowThrowingUnknown?: boolean; | ||
} | ||
const defaultOptions: Options = { | ||
allowThrowingAny: false, | ||
allowThrowingUnknown: false, | ||
}; | ||
``` | ||
--- | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-throw-literal.md)</sup> |
@@ -1,3 +0,9 @@ | ||
# Disallow the use of type aliases (`no-type-alias`) | ||
--- | ||
description: 'Disallow type aliases.' | ||
--- | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-type-alias** for documentation. | ||
In TypeScript, type aliases serve three purposes: | ||
@@ -75,3 +81,3 @@ | ||
## Rule Details | ||
## Examples | ||
@@ -83,13 +89,2 @@ This rule disallows the use of type aliases in favor of interfaces | ||
This rule, in its default state, does not require any argument. If you would like to enable one | ||
or more of the following you may pass an object with the options set as follows: | ||
- `allowAliases` set to `"always"` will allow you to do aliasing (Defaults to `"never"`). | ||
- `allowCallbacks` set to `"always"` will allow you to use type aliases with callbacks (Defaults to `"never"`) | ||
- `allowConditionalTypes` set to `"always"` will allow you to use type aliases with conditional types (Defaults to `"never"`) | ||
- `allowConstructors` set to `"always"` will allow you to use type aliases with constructors (Defaults to `"never"`) | ||
- `allowLiterals` set to `"always"` will allow you to use type aliases with literal objects (Defaults to `"never"`) | ||
- `allowMappedTypes` set to `"always"` will allow you to use type aliases as mapping tools (Defaults to `"never"`) | ||
- `allowTupleTypes` set to `"always"` will allow you to use type aliases with tuples (Defaults to `"never"`) | ||
### `allowAliases` | ||
@@ -120,2 +115,4 @@ | ||
type Foo = `foo-${number}`; | ||
// reference types | ||
@@ -142,2 +139,4 @@ interface Bar {} | ||
type Foo = `foo-${number}`; | ||
// reference types | ||
@@ -160,2 +159,4 @@ interface Bar {} | ||
type Foo = `a-${number}` | `b-${number}`; | ||
// reference types | ||
@@ -180,2 +181,4 @@ interface Bar {} | ||
type Foo = `a-${number}` | `b-${number}`; | ||
// reference types | ||
@@ -196,2 +199,4 @@ interface Bar {} | ||
type Foo = `a-${number}` & `b-${number}`; | ||
// reference types | ||
@@ -212,2 +217,4 @@ interface Bar {} | ||
type Foo = `foo-${number}`; | ||
// reference types | ||
@@ -230,2 +237,6 @@ interface Bar {} | ||
type Foo = `a-${number}` & `b-${number}`; | ||
type Foo = `a-${number}` | `b-${number}`; | ||
// reference types | ||
@@ -417,4 +428,5 @@ interface Bar {} | ||
type Foo<T, U> = { readonly [P in keyof T]: T[P] } & | ||
{ readonly [P in keyof U]: U[P] }; | ||
type Foo<T, U> = { readonly [P in keyof T]: T[P] } & { | ||
readonly [P in keyof U]: U[P]; | ||
}; | ||
@@ -431,4 +443,5 @@ type Foo<T, U> = { [P in keyof T]?: T[P] } & { [P in keyof U]?: U[P] }; | ||
type Foo<T, U> = { readonly [P in keyof T]: T[P] } & | ||
{ readonly [P in keyof U]: U[P] }; | ||
type Foo<T, U> = { readonly [P in keyof T]: T[P] } & { | ||
readonly [P in keyof U]: U[P]; | ||
}; | ||
@@ -465,4 +478,5 @@ type Foo<T, U> = { [P in keyof T]?: T[P] } & { [P in keyof U]?: U[P] }; | ||
```ts | ||
type Foo<T, U> = { readonly [P in keyof T]: T[P] } & | ||
{ readonly [P in keyof U]: U[P] }; | ||
type Foo<T, U> = { readonly [P in keyof T]: T[P] } & { | ||
readonly [P in keyof U]: U[P]; | ||
}; | ||
@@ -489,4 +503,5 @@ type Foo<T, U> = { [P in keyof T]?: T[P] } & { [P in keyof U]?: U[P] }; | ||
type Foo<T, U> = { readonly [P in keyof T]: T[P] } & | ||
{ readonly [P in keyof U]: U[P] }; | ||
type Foo<T, U> = { readonly [P in keyof T]: T[P] } & { | ||
readonly [P in keyof U]: U[P]; | ||
}; | ||
@@ -571,2 +586,24 @@ type Foo<T, U> = { [P in keyof T]?: T[P] } & { [P in keyof U]?: U[P] }; | ||
### `allowGenerics` | ||
This applies to generic types, including TypeScript provided global utility types (`type Foo = Record<string, number>`). | ||
The setting accepts the following options: | ||
- `"always"` or `"never"` to active or deactivate the feature. | ||
Examples of **correct** code for the `{ "allowGenerics": "always" }` options: | ||
```ts | ||
type Foo = Bar<string>; | ||
type Foo = Record<string, number>; | ||
type Foo = Readonly<Bar>; | ||
type Foo = Partial<Bar>; | ||
type Foo = Omit<Bar, 'a' | 'b'>; | ||
``` | ||
## When Not To Use It | ||
@@ -579,6 +616,2 @@ | ||
- [Advance Types](https://www.typescriptlang.org/docs/handbook/advanced-types.html) | ||
## Related to | ||
- TSLint: [interface-over-type-literal](https://palantir.github.io/tslint/rules/interface-over-type-literal/) | ||
- [Advanced Types](https://www.typescriptlang.org/docs/handbook/advanced-types.html) |
@@ -1,23 +0,28 @@ | ||
# Flags unnecessary equality comparisons against boolean literals (`no-unnecessary-boolean-literal-compare`) | ||
--- | ||
description: 'Disallow unnecessary equality comparisons against boolean literals.' | ||
--- | ||
Comparing boolean values to boolean literals is unnecessary, those comparisons result in the same booleans. Using the boolean values directly, or via a unary negation (`!value`), is more concise and clearer. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-unnecessary-boolean-literal-compare** for documentation. | ||
## Rule Details | ||
Comparing boolean values to boolean literals is unnecessary: those comparisons result in the same booleans. | ||
Using the boolean values directly, or via a unary negation (`!value`), is more concise and clearer. | ||
This rule ensures that you do not include unnecessary comparisons with boolean literals. | ||
A comparison is considered unnecessary if it checks a boolean literal against any variable with just the `boolean` type. | ||
A comparison is **_not_** considered unnecessary if the type is a union of booleans (`string | boolean`, `someObject | boolean`). | ||
A comparison is **_not_** considered unnecessary if the type is a union of booleans (`string | boolean`, `SomeObject | boolean`, etc.). | ||
**Warning**: Do not use this rule when `strictNullChecks` is disabled. | ||
ESLint is not able to distinguish between `false` and `undefined` or `null` values. | ||
This can cause unintended code changes when using autofix. | ||
## Examples | ||
**Note**: Throughout this page, only strict equality (`===` and `!==`) are | ||
used in the examples. However, the implementation of the rule does not | ||
distinguish between strict and loose equality. Any example below that uses | ||
`===` would be treated the same way if `==` was used, and any example below | ||
that uses `!==` would be treated the same way if `!=` was used. | ||
:::note | ||
Throughout this page, only strict equality (`===` and `!==`) are used in the examples. | ||
However, the implementation of the rule does not distinguish between strict and loose equality. | ||
Any example below that uses `===` would be treated the same way if `==` was used, and `!==` would be treated the same way if `!=` was used. | ||
::: | ||
Examples of **incorrect** code for this rule: | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -29,3 +34,3 @@ declare const someCondition: boolean; | ||
Examples of **correct** code for this rule | ||
### ✅ Correct | ||
@@ -48,15 +53,2 @@ ```ts | ||
The rule accepts an options object with the following properties. | ||
```ts | ||
type Options = { | ||
// if false, comparisons between a nullable boolean variable to `true` will be checked and fixed | ||
allowComparingNullableBooleansToTrue?: boolean; | ||
// if false, comparisons between a nullable boolean variable to `false` will be checked and fixed | ||
allowComparingNullableBooleansToFalse?: boolean; | ||
}; | ||
``` | ||
### Defaults | ||
This rule always checks comparisons between a boolean variable and a boolean | ||
@@ -66,13 +58,10 @@ literal. Comparisons between nullable boolean variables and boolean literals | ||
```ts | ||
const defaults = { | ||
allowComparingNullableBooleansToTrue: true, | ||
allowComparingNullableBooleansToFalse: true, | ||
}; | ||
``` | ||
### `allowComparingNullableBooleansToTrue` | ||
Examples of **incorrect** code for this rule with `{ allowComparingNullableBooleansToTrue: false }`: | ||
Examples of code for this rule with `{ allowComparingNullableBooleansToTrue: false }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -88,3 +77,3 @@ declare const someUndefinedCondition: boolean | undefined; | ||
Examples of **correct** code for this rule with `{ allowComparingNullableBooleansToTrue: false }`: | ||
#### ✅ Correct | ||
@@ -103,4 +92,8 @@ ```ts | ||
Examples of **incorrect** code for this rule with `{ allowComparingNullableBooleansToFalse: false }`: | ||
Examples of code for this rule with `{ allowComparingNullableBooleansToFalse: false }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -116,3 +109,3 @@ declare const someUndefinedCondition: boolean | undefined; | ||
Examples of **correct** code for this rule with `{ allowComparingNullableBooleansToFalse: false }`: | ||
#### ✅ Correct | ||
@@ -131,15 +124,17 @@ ```ts | ||
| Comparison | Fixer Output | Notes | | ||
| :----------------------------: | ------------------------------- | ----------------------------------------------------------------------------------- | | ||
| `booleanVar === true` | `booleanLiteral` | | | ||
| `booleanVar !== true` | `!booleanLiteral` | | | ||
| `booleanVar === false` | `!booleanLiteral` | | | ||
| `booleanVar !== false` | `booleanLiteral` | | | ||
| `nullableBooleanVar === true` | `nullableBooleanVar` | Only checked/fixed if the `allowComparingNullableBooleansToTrue` option is `false` | | ||
| `nullableBooleanVar !== true` | `!nullableBooleanVar` | Only checked/fixed if the `allowComparingNullableBooleansToTrue` option is `false` | | ||
| `nullableBooleanVar === false` | `nullableBooleanVar ?? true` | Only checked/fixed if the `allowComparingNullableBooleansToFalse` option is `false` | | ||
| `nullableBooleanVar !== false` | `!(nullableBooleanVar ?? true)` | Only checked/fixed if the `allowComparingNullableBooleansToFalse` option is `false` | | ||
| Comparison | Fixer Output | Notes | | ||
| :-------------------------------: | ------------------------------- | ----------------------------------------------------------------------------------- | | ||
| `booleanVar === true` | `booleanVar` | | | ||
| `booleanVar !== true` | `!booleanVar` | | | ||
| `booleanVar === false` | `!booleanVar` | | | ||
| `booleanVar !== false` | `booleanVar` | | | ||
| `nullableBooleanVar === true` | `nullableBooleanVar` | Only checked/fixed if the `allowComparingNullableBooleansToTrue` option is `false` | | ||
| `nullableBooleanVar !== true` | `!nullableBooleanVar` | Only checked/fixed if the `allowComparingNullableBooleansToTrue` option is `false` | | ||
| `!(nullableBooleanVar === false)` | `nullableBooleanVar ?? true` | Only checked/fixed if the `allowComparingNullableBooleansToFalse` option is `false` | | ||
| `!(nullableBooleanVar !== false)` | `!(nullableBooleanVar ?? true)` | Only checked/fixed if the `allowComparingNullableBooleansToFalse` option is `false` | | ||
## Related to | ||
## Not To Use It | ||
- TSLint: [no-boolean-literal-compare](https://palantir.github.io/tslint/rules/no-boolean-literal-compare) | ||
Do not use this rule when `strictNullChecks` is disabled. | ||
ESLint is not able to distinguish between `false` and `undefined` or `null` values. | ||
This can cause unintended code changes when using autofix. |
@@ -1,5 +0,12 @@ | ||
# Prevents conditionals where the type is always truthy or always falsy (`no-unnecessary-condition`) | ||
--- | ||
description: 'Disallow conditionals where the type is always truthy or always falsy.' | ||
--- | ||
Any expression being used as a condition must be able to evaluate as truthy or falsy in order to be considered "necessary". Conversely, any expression that always evaluates to truthy or always evaluates to falsy, as determined by the type of the expression, is considered unnecessary and will be flagged by this rule. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-unnecessary-condition** for documentation. | ||
Any expression being used as a condition must be able to evaluate as truthy or falsy in order to be considered "necessary". | ||
Conversely, any expression that always evaluates to truthy or always evaluates to falsy, as determined by the type of the expression, is considered unnecessary and will be flagged by this rule. | ||
The following expressions are checked: | ||
@@ -11,4 +18,8 @@ | ||
Examples of **incorrect** code for this rule: | ||
## Examples | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -40,3 +51,3 @@ function head<T>(items: T[]) { | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -67,16 +78,2 @@ ```ts | ||
```ts | ||
type Options = { | ||
// if true, the rule will ignore constant loop conditions | ||
allowConstantLoopConditions?: boolean; | ||
// if true, the rule will not error when running with a tsconfig that has strictNullChecks turned **off** | ||
allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing?: boolean; | ||
}; | ||
const defaultOptions: Options = { | ||
allowConstantLoopConditions: false, | ||
allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: false, | ||
}; | ||
``` | ||
### `allowConstantLoopConditions` | ||
@@ -109,3 +106,2 @@ | ||
- ESLint: [no-constant-condition](https://eslint.org/docs/rules/no-constant-condition) - `no-unnecessary-condition` is essentially a stronger version of `no-constant-condition`, but requires type information. | ||
- [strict-boolean-expressions](./strict-boolean-expressions.md) - a more opinionated version of `no-unnecessary-condition`. `strict-boolean-expressions` enforces a specific code style, while `no-unnecessary-condition` is about correctness. |
@@ -1,23 +0,18 @@ | ||
# Warns when a namespace qualifier is unnecessary (`no-unnecessary-qualifier`) | ||
--- | ||
description: 'Disallow unnecessary namespace qualifiers.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-unnecessary-qualifier** for documentation. | ||
This rule aims to let users know when a namespace or enum qualifier is unnecessary, | ||
whether used for a type or for a value. | ||
Members of TypeScript enums and namespaces are generally retrieved as qualified property lookups: e.g. `Enum.member`. | ||
However, when accessed within their parent enum or namespace, the qualifier is unnecessary: e.g. just `member` instead of `Enum.member`. | ||
This rule reports when an enum or namespace qualifier is unnecessary. | ||
Examples of **incorrect** code for this rule: | ||
## Examples | ||
```ts | ||
namespace A { | ||
export type B = number; | ||
const x: A.B = 3; | ||
} | ||
``` | ||
<!--tabs--> | ||
```ts | ||
namespace A { | ||
export const x = 3; | ||
export const y = A.x; | ||
} | ||
``` | ||
### ❌ Incorrect | ||
@@ -33,39 +28,20 @@ ```ts | ||
namespace A { | ||
export namespace B { | ||
export type T = number; | ||
const x: A.B.T = 3; | ||
} | ||
export type B = number; | ||
const x: A.B = 3; | ||
} | ||
``` | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
```ts | ||
namespace X { | ||
export type T = number; | ||
} | ||
namespace Y { | ||
export const x: X.T = 3; | ||
} | ||
``` | ||
```ts | ||
enum A { | ||
X, | ||
Y, | ||
B, | ||
C = B, | ||
} | ||
enum B { | ||
Z = A.X, | ||
} | ||
``` | ||
```ts | ||
namespace X { | ||
export type T = number; | ||
namespace Y { | ||
type T = string; | ||
const x: X.T = 0; | ||
} | ||
namespace A { | ||
export type B = number; | ||
const x: B = 3; | ||
} | ||
@@ -76,6 +52,2 @@ ``` | ||
If you don't care about having unneeded namespace or enum qualifiers, then you don't need to use this rule. | ||
## Further Reading | ||
- TSLint: [no-unnecessary-qualifier](https://palantir.github.io/tslint/rules/no-unnecessary-qualifier/) | ||
If you don't care about having unneeded enum or namespace qualifiers, then you don't need to use this rule. |
@@ -1,7 +0,9 @@ | ||
# Enforces that type arguments will not be used if not required (`no-unnecessary-type-arguments`) | ||
--- | ||
description: 'Disallow type arguments that are equal to the default.' | ||
--- | ||
Warns if an explicitly specified type argument is the default for that type parameter. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-unnecessary-type-arguments** for documentation. | ||
## Rule Details | ||
Type parameters in TypeScript may specify a default value. | ||
@@ -11,21 +13,32 @@ For example: | ||
```ts | ||
function f<T = number>() {} | ||
function f<T = number>(...) {...} | ||
``` | ||
It is redundant to provide an explicit type parameter equal to that default. | ||
It is redundant to provide an explicit type parameter equal to that default: e.g. calling `f<number>(...)`. | ||
This rule reports when an explicitly specified type argument is the default for that type parameter. | ||
Examples of **incorrect** code for this rule: | ||
## Examples | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
function f<T = number>() {} | ||
f<number>(); | ||
``` | ||
```ts | ||
function g<T = number, U = string>() {} | ||
g<string, string>(); | ||
``` | ||
```ts | ||
class C<T = number> {} | ||
function h(c: C<number>) {} | ||
new C<number>(); | ||
class D extends C<number> {} | ||
``` | ||
```ts | ||
interface I<T = number> {} | ||
@@ -35,21 +48,28 @@ class Impl implements I<number> {} | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
```ts | ||
function f<T = number>() {} | ||
f(); | ||
f<string>(); | ||
``` | ||
```ts | ||
function g<T = number, U = string>() {} | ||
g<string>(); | ||
g<number, number>(); | ||
``` | ||
```ts | ||
class C<T = number> {} | ||
new C(); | ||
new C<string>(); | ||
class D extends C {} | ||
class D extends C<string> {} | ||
``` | ||
```ts | ||
interface I<T = number> {} | ||
class Impl implements I<string> {} | ||
``` | ||
## Related to | ||
- TSLint: [use-default-type-parameter](https://palantir.github.io/tslint/rules/use-default-type-parameter) |
@@ -1,11 +0,19 @@ | ||
# Warns if a type assertion does not change the type of an expression (`no-unnecessary-type-assertion`) | ||
--- | ||
description: 'Disallow type assertions that do not change the type of an expression.' | ||
--- | ||
This rule prohibits using a type assertion that does not change the type of an expression. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-unnecessary-type-assertion** for documentation. | ||
## Rule Details | ||
TypeScript can be told an expression is a different type than expected using `as` type assertions. | ||
Leaving `as` assertions in the codebase increases visual clutter and harms code readability, so it's generally best practice to remove them if they don't change the type of an expression. | ||
This rule reports when a type assertion does not change the type of an expression. | ||
This rule aims to prevent unnecessary type assertions. | ||
## Examples | ||
Examples of **incorrect** code for this rule: | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -36,3 +44,3 @@ const foo = 3; | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -57,7 +65,7 @@ ```ts | ||
### Options | ||
## Options | ||
This rule optionally takes an object with a single property `typesToIgnore`, which can be set to a list of type names to ignore. | ||
### `typesToIgnore` | ||
For example, with `@typescript-eslint/no-unnecessary-type-assertion: ["error", { typesToIgnore: ['Foo'] }]`, the following is **correct** code": | ||
With `@typescript-eslint/no-unnecessary-type-assertion: ["error", { typesToIgnore: ['Foo'] }]`, the following is **correct** code": | ||
@@ -72,5 +80,1 @@ ```ts | ||
If you don't care about having no-op type assertions in your code, then you can turn off this rule. | ||
## Related to | ||
- TSLint: ['no-unnecessary-type-assertion`](https://palantir.github.io/tslint/rules/no-unnecessary-type-assertion/) |
@@ -1,38 +0,38 @@ | ||
# Disallows unnecessary constraints on generic types (`no-unnecessary-type-constraint`) | ||
--- | ||
description: 'Disallow unnecessary constraints on generic types.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-unnecessary-type-constraint** for documentation. | ||
Type parameters (`<T>`) may be "constrained" with an `extends` keyword ([docs](https://www.typescriptlang.org/docs/handbook/generics.html#generic-constraints)). | ||
When not provided, type parameters happen to default to: | ||
Generic type parameters (`<T>`) in TypeScript may be "constrained" with an [`extends` keyword](https://www.typescriptlang.org/docs/handbook/generics.html#generic-constraints). | ||
When no `extends` is provided, type parameters default a constraint to `unknown`. | ||
It is therefore redundant to `extend` from `any` or `unknown`. | ||
- As of TypeScript 3.9: `unknown` ([docs](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-9.html#type-parameters-that-extend-any-no-longer-act-as-any)) | ||
- Before that, as of 3.5: `any` ([docs](https://devblogs.microsoft.com/typescript/announcing-typescript-3-5/#breaking-changes)) | ||
## Examples | ||
It is therefore redundant to `extend` from these types in later versions of TypeScript. | ||
<!--tabs--> | ||
Examples of **incorrect** code for this rule: | ||
### ❌ Incorrect | ||
```ts | ||
interface FooAny<T extends any> {} | ||
interface FooUnknown<T extends unknown> {} | ||
type BarAny<T extends any> = {}; | ||
type BarUnknown<T extends unknown> = {}; | ||
class BazAny<T extends any> { | ||
quxUnknown<U extends unknown>() {} | ||
quxAny<U extends any>() {} | ||
} | ||
class BazUnknown<T extends unknown> { | ||
quxUnknown<U extends unknown>() {} | ||
} | ||
const QuuxAny = <T extends any>() => {}; | ||
const QuuxUnknown = <T extends unknown>() => {}; | ||
function QuuzAny<T extends any>() {} | ||
function QuuzUnknown<T extends unknown>() {} | ||
``` | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -45,3 +45,3 @@ ```ts | ||
class Baz<T> { | ||
qux<U> { } | ||
qux<U> { } | ||
} | ||
@@ -48,0 +48,0 @@ |
@@ -1,13 +0,26 @@ | ||
# Disallows assigning any to variables and properties (`no-unsafe-assignment`) | ||
--- | ||
description: 'Disallow assigning a value with type `any` to variables and properties.' | ||
--- | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-unsafe-assignment** for documentation. | ||
The `any` type in TypeScript is a dangerous "escape hatch" from the type system. | ||
Using `any` disables many type checking rules and is generally best used only as a last resort or when prototyping code. | ||
Despite your best intentions, the `any` type can sometimes leak into your codebase. | ||
Assigning an `any` typed value to a variable can be hard to pick up on, particularly if it leaks in from an external library. Operations on the variable will not be checked at all by TypeScript, so it creates a potential safety hole, and source of bugs in your codebase. | ||
Assigning an `any` typed value to a variable can be hard to pick up on, particularly if it leaks in from an external library. | ||
## Rule Details | ||
This rule disallows assigning `any` to a variable, and assigning `any[]` to an array destructuring. | ||
This rule also compares the assigned type to the variable's type to ensure you don't assign an unsafe `any` in a generic position to a receiver that's expecting a specific type. For example, it will error if you assign `Set<any>` to a variable declared as `Set<string>`. | ||
Examples of **incorrect** code for this rule: | ||
This rule also compares generic type argument types to ensure you don't pass an unsafe `any` in a generic position to a receiver that's expecting a specific type. | ||
For example, it will error if you assign `Set<any>` to a variable declared as `Set<string>`. | ||
## Examples | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -36,3 +49,3 @@ const x = 1 as any, | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -60,5 +73,7 @@ ```ts | ||
<!--/tabs--> | ||
There are cases where the rule allows assignment of `any` to `unknown`. | ||
Example of `any` to `unknown` assignment that are allowed. | ||
Example of `any` to `unknown` assignment that are allowed: | ||
@@ -71,5 +86,4 @@ ```ts | ||
## Related to | ||
## Related To | ||
- [`no-explicit-any`](./no-explicit-any.md) | ||
- TSLint: [`no-unsafe-any`](https://palantir.github.io/tslint/rules/no-unsafe-any/) |
@@ -1,12 +0,23 @@ | ||
# Disallows calling an any type value (`no-unsafe-call`) | ||
--- | ||
description: 'Disallow calling a value with type `any`.' | ||
--- | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-unsafe-call** for documentation. | ||
The `any` type in TypeScript is a dangerous "escape hatch" from the type system. | ||
Using `any` disables many type checking rules and is generally best used only as a last resort or when prototyping code. | ||
Despite your best intentions, the `any` type can sometimes leak into your codebase. | ||
The arguments to, and return value of calling an `any` typed variable are not checked at all by TypeScript, so it creates a potential safety hole, and source of bugs in your codebase. | ||
Calling an `any`-typed value as a function creates a potential type safety hole and source of bugs in your codebase. | ||
## Rule Details | ||
This rule disallows calling any value that is typed as `any`. | ||
This rule disallows calling any variable that is typed as `any`. | ||
## Examples | ||
Examples of **incorrect** code for this rule: | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -29,3 +40,3 @@ declare const anyVar: any; | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -46,5 +57,4 @@ ```ts | ||
## Related to | ||
## Related To | ||
- [`no-explicit-any`](./no-explicit-any.md) | ||
- TSLint: [`no-unsafe-any`](https://palantir.github.io/tslint/rules/no-unsafe-any/) |
@@ -1,12 +0,23 @@ | ||
# Disallows member access on any typed variables (`no-unsafe-member-access`) | ||
--- | ||
description: 'Disallow member access on a value with type `any`.' | ||
--- | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-unsafe-member-access** for documentation. | ||
The `any` type in TypeScript is a dangerous "escape hatch" from the type system. | ||
Using `any` disables many type checking rules and is generally best used only as a last resort or when prototyping code. | ||
Despite your best intentions, the `any` type can sometimes leak into your codebase. | ||
Member access on `any` typed variables is not checked at all by TypeScript, so it creates a potential safety hole, and source of bugs in your codebase. | ||
Accessing a member of an `any`-typed value creates a potential type safety hole and source of bugs in your codebase. | ||
## Rule Details | ||
This rule disallows member access on any variable that is typed as `any`. | ||
Examples of **incorrect** code for this rule: | ||
## Examples | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -33,3 +44,3 @@ declare const anyVar: any; | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -52,5 +63,4 @@ ```ts | ||
## Related to | ||
## Related To | ||
- [`no-explicit-any`](./no-explicit-any.md) | ||
- TSLint: [`no-unsafe-any`](https://palantir.github.io/tslint/rules/no-unsafe-any/) |
@@ -1,13 +0,26 @@ | ||
# Disallows returning any from a function (`no-unsafe-return`) | ||
--- | ||
description: 'Disallow returning a value with type `any` from a function.' | ||
--- | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-unsafe-return** for documentation. | ||
The `any` type in TypeScript is a dangerous "escape hatch" from the type system. | ||
Using `any` disables many type checking rules and is generally best used only as a last resort or when prototyping code. | ||
Despite your best intentions, the `any` type can sometimes leak into your codebase. | ||
Returned `any` typed values not checked at all by TypeScript, so it creates a potential safety hole, and source of bugs in your codebase. | ||
Returning an an `any`-typed value from a function creates a potential type safety hole and source of bugs in your codebase. | ||
## Rule Details | ||
This rule disallows returning `any` or `any[]` from a function. | ||
This rule also compares the return type to the function's declared/inferred return type to ensure you don't return an unsafe `any` in a generic position to a receiver that's expecting a specific type. For example, it will error if you return `Set<any>` from a function declared as returning `Set<string>`. | ||
Examples of **incorrect** code for this rule: | ||
This rule also compares generic type argument types to ensure you don't return an unsafe `any` in a generic position to a function that's expecting a specific type. | ||
For example, it will error if you return `Set<any>` from a function declared as returning `Set<string>`. | ||
## Examples | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -52,3 +65,3 @@ function foo1() { | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -73,5 +86,7 @@ ```ts | ||
<!--/tabs--> | ||
There are cases where the rule allows to return `any` to `unknown`. | ||
Examples of `any` to `unknown` return that are allowed. | ||
Examples of `any` to `unknown` return that are allowed: | ||
@@ -88,5 +103,4 @@ ```ts | ||
## Related to | ||
## Related To | ||
- [`no-explicit-any`](./no-explicit-any.md) | ||
- TSLint: [`no-unsafe-any`](https://palantir.github.io/tslint/rules/no-unsafe-any/) |
@@ -1,22 +0,12 @@ | ||
# Disallow unused expressions (`no-unused-expressions`) | ||
--- | ||
description: 'Disallow unused expressions.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-unused-expressions** for documentation. | ||
## Examples | ||
This rule extends the base [`eslint/no-unused-expressions`](https://eslint.org/docs/rules/no-unused-expressions) rule. | ||
It adds support for optional call expressions `x?.()`, and directive in module declarations. | ||
## How to use | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"no-unused-expressions": "off", | ||
"@typescript-eslint/no-unused-expressions": ["error"] | ||
} | ||
``` | ||
## Options | ||
See [`eslint/no-unused-expressions` options](https://eslint.org/docs/rules/no-unused-expressions#options). | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-unused-expressions.md)</sup> |
@@ -1,22 +0,12 @@ | ||
# Disallow unused variables (`no-unused-vars`) | ||
--- | ||
description: 'Disallow unused variables.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-unused-vars** for documentation. | ||
## Examples | ||
This rule extends the base [`eslint/no-unused-vars`](https://eslint.org/docs/rules/no-unused-vars) rule. | ||
It adds support for TypeScript features, such as types. | ||
## How to use | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"no-unused-vars": "off", | ||
"@typescript-eslint/no-unused-vars": ["error"] | ||
} | ||
``` | ||
## Options | ||
See [`eslint/no-unused-vars` options](https://eslint.org/docs/rules/no-unused-vars#options). | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-unused-vars.md)</sup> |
@@ -1,6 +0,10 @@ | ||
# Disallow the use of variables before they are defined (`no-use-before-define`) | ||
--- | ||
description: 'Disallow the use of variables before they are defined.' | ||
--- | ||
## PLEASE READ THIS ISSUE BEFORE USING THIS RULE [#1856](https://github.com/typescript-eslint/typescript-eslint/issues/1856) | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-use-before-define** for documentation. | ||
## Rule Details | ||
## Examples | ||
@@ -10,15 +14,4 @@ This rule extends the base [`eslint/no-use-before-define`](https://eslint.org/docs/rules/no-use-before-define) rule. | ||
## How to use | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"no-use-before-define": "off", | ||
"@typescript-eslint/no-use-before-define": ["error"] | ||
} | ||
``` | ||
## Options | ||
See [`eslint/no-use-before-define` options](https://eslint.org/docs/rules/no-use-before-define#options). | ||
This rule adds the following options: | ||
@@ -46,6 +39,10 @@ | ||
Examples of **incorrect** code for the `{ "enums": false }` option: | ||
Examples of code for the `{ "enums": true }` option: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
/*eslint no-use-before-define: ["error", { "enums": false }]*/ | ||
/*eslint no-use-before-define: ["error", { "enums": true }]*/ | ||
@@ -59,3 +56,3 @@ const x = Foo.FOO; | ||
Examples of **correct** code for the `{ "enums": false }` option: | ||
#### ✅ Correct | ||
@@ -104,7 +101,1 @@ ```ts | ||
``` | ||
### Other Options | ||
See [`eslint/no-use-before-define` options](https://eslint.org/docs/rules/no-use-before-define#options). | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-use-before-define.md)</sup> |
@@ -1,5 +0,11 @@ | ||
# Disallow unnecessary constructors (`no-useless-constructor`) | ||
--- | ||
description: 'Disallow unnecessary constructors.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-useless-constructor** for documentation. | ||
## Examples | ||
This rule extends the base [`eslint/no-useless-constructor`](https://eslint.org/docs/rules/no-useless-constructor) rule. | ||
@@ -12,16 +18,5 @@ It adds support for: | ||
## How to use | ||
### Caveat | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"no-useless-constructor": "off", | ||
"@typescript-eslint/no-useless-constructor": ["error"] | ||
} | ||
``` | ||
## Options | ||
See [`eslint/no-useless-constructor` options](https://eslint.org/docs/rules/no-useless-constructor#options). | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-useless-constructor.md)</sup> | ||
This lint rule will report on constructors whose sole purpose is to change visibility of a parent constructor. | ||
See [discussion on this rule's lack of type information](https://github.com/typescript-eslint/typescript-eslint/issues/3820#issuecomment-917821240) for context. |
@@ -1,9 +0,17 @@ | ||
# Disallows the use of require statements except in import statements (`no-var-requires`) | ||
--- | ||
description: 'Disallow `require` statements except in import statements.' | ||
--- | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/no-var-requires** for documentation. | ||
In other words, the use of forms such as `var foo = require("foo")` are banned. Instead use ES6 style imports or `import foo = require("foo")` imports. | ||
## Rule Details | ||
## Examples | ||
Examples of **incorrect** code for this rule: | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -15,3 +23,3 @@ var foo = require('foo'); | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -26,6 +34,6 @@ ```ts | ||
If you don't care about TypeScript module syntax, then you will not need this rule. | ||
If you don't care about using newer module syntax, then you will not need this rule. | ||
## Compatibility | ||
## Related To | ||
- TSLint: [no-var-requires](https://palantir.github.io/tslint/rules/no-var-requires/) | ||
- [`no-require-imports`](./no-require-imports.md) |
@@ -1,9 +0,23 @@ | ||
# Prefers a non-null assertion over explicit type cast when possible (`non-nullable-type-assertion-style`) | ||
--- | ||
description: 'Enforce non-null assertions over explicit type casts.' | ||
--- | ||
This rule detects when an `as` cast is doing the same job as a `!` would, and suggests fixing the code to be an `!`. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/non-nullable-type-assertion-style** for documentation. | ||
## Rule Details | ||
There are two common ways to assert to TypeScript that a value is its type without `null` or `undefined`: | ||
Examples of **incorrect** code for this rule: | ||
- `!`: Non-null assertion | ||
- `as`: Traditional type assertion with a coincidentally equivalent type | ||
`!` non-null assertions are generally preferred for requiring less code and being harder to fall out of sync as types change. | ||
This rule reports when an `as` cast is doing the same job as a `!` would, and suggests fixing the code to be an `!`. | ||
## Examples | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -16,3 +30,3 @@ const maybe = Math.random() > 0.5 ? '' : undefined; | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -19,0 +33,0 @@ ```ts |
@@ -1,22 +0,12 @@ | ||
# Enforce consistent spacing inside braces (`object-curly-spacing`) | ||
--- | ||
description: 'Enforce consistent spacing inside braces.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/object-curly-spacing** for documentation. | ||
## Examples | ||
This rule extends the base [`eslint/object-curly-spacing`](https://eslint.org/docs/rules/object-curly-spacing) rule. | ||
It adds support for TypeScript's object types. | ||
## How to use | ||
```cjson | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"object-curly-spacing": "off", | ||
"@typescript-eslint/object-curly-spacing": ["error"] | ||
} | ||
``` | ||
## Options | ||
See [`eslint/object-curly-spacing` options](https://eslint.org/docs/rules/object-curly-spacing#options). | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/object-curly-spacing.md)</sup> |
@@ -1,9 +0,23 @@ | ||
# Prefer usage of `as const` over literal type (`prefer-as-const`) | ||
--- | ||
description: 'Enforce the use of `as const` over literal type.' | ||
--- | ||
This rule recommends usage of `const` assertion when type primitive value is equal to type. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/prefer-as-const** for documentation. | ||
## Rule Details | ||
There are two common ways to tell TypeScript that a literal value should be interpreted as its literal type (e.g. `2`) rather than general primitive type (e.g. `number`); | ||
Examples of **incorrect** code for this rule: | ||
- `as const`: telling TypeScript to infer the literal type automatically | ||
- `as` with the literal type: explicitly telling the literal type to TypeScript | ||
`as const` is generally preferred, as it doesn't require re-typing the literal value. | ||
This rule reports when an `as` with an explicit literal type can be replaced with an `as const`. | ||
## Examples | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -15,3 +29,3 @@ let bar: 2 = 2; | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -27,4 +41,6 @@ ```ts | ||
<!--/tabs--> | ||
## When Not To Use It | ||
If you are using TypeScript < 3.4 |
@@ -1,30 +0,22 @@ | ||
# Prefer initializing each enums member value (`prefer-enum-initializers`) | ||
--- | ||
description: 'Require each enum member value to be explicitly initialized.' | ||
--- | ||
This rule recommends having each `enum`s member value explicitly initialized. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/prefer-enum-initializers** for documentation. | ||
`enum`s are a practical way to organize semantically related constant values. However, by implicitly defining values, `enum`s can lead to unexpected bugs if it's modified without paying attention to the order of its items. | ||
TypeScript `enum`s are a practical way to organize semantically related constant values. | ||
Members of `enum`s that don't have explicit values are by default given sequentially increasing numbers. | ||
## Rule Details | ||
In projects where the value of `enum` members are important, allowing implicit values for enums can cause bugs if `enum`s are modified over time. | ||
`enum`s infers sequential numbers automatically when initializers are omitted: | ||
This rule recommends having each `enum` member value explicitly initialized. | ||
```ts | ||
enum Status { | ||
Open, // infer 0 | ||
Closed, // infer 1 | ||
} | ||
``` | ||
## Examples | ||
If a new member is added to the top of `Status`, both `Open` and `Closed` would have its values altered: | ||
<!--tabs--> | ||
```ts | ||
enum Status { | ||
Pending, // infer 0 | ||
Open, // infer 1 | ||
Closed, // infer 2 | ||
} | ||
``` | ||
### ❌ Incorrect | ||
Examples of **incorrect** code for this rule: | ||
```ts | ||
@@ -48,3 +40,3 @@ enum Status { | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -51,0 +43,0 @@ ```ts |
@@ -1,33 +0,42 @@ | ||
# Prefer a ‘for-of’ loop over a standard ‘for’ loop if the index is only used to access the array being iterated (`prefer-for-of`) | ||
--- | ||
description: 'Enforce the use of `for-of` loop over the standard `for` loop where possible.' | ||
--- | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/prefer-for-of** for documentation. | ||
Many developers default to writing `for (let i = 0; i < ...` loops to iterate over arrays. | ||
However, in many of those arrays, the loop iterator variable (e.g. `i`) is only used to access the respective element of the array. | ||
In those cases, a `for-of` loop is easier to read and write. | ||
This rule recommends a for-of loop when the loop index is only used to read from an array that is being iterated. | ||
## Rule Details | ||
## Examples | ||
For cases where the index is only used to read from the array being iterated, a for-of loop is easier to read and write. | ||
<!--tabs--> | ||
Examples of **incorrect** code for this rule: | ||
### ❌ Incorrect | ||
```js | ||
for (let i = 0; i < arr.length; i++) { | ||
console.log(arr[i]); | ||
declare const array: string[]; | ||
for (let i = 0; i < array.length; i++) { | ||
console.log(array[i]); | ||
} | ||
``` | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
```js | ||
for (const x of arr) { | ||
declare const array: string[]; | ||
for (const x of array) { | ||
console.log(x); | ||
} | ||
for (let i = 0; i < arr.length; i++) { | ||
// i is used to write to arr, so for-of could not be used. | ||
arr[i] = 0; | ||
for (let i = 0; i < array.length; i++) { | ||
// i is used, so for-of could not be used. | ||
console.log(i, array[i]); | ||
} | ||
for (let i = 0; i < arr.length; i++) { | ||
// i is used independent of arr, so for-of could not be used. | ||
console.log(i, arr[i]); | ||
} | ||
``` | ||
@@ -38,5 +47,1 @@ | ||
If you transpile for browsers that do not support for-of loops, you may wish to use traditional for loops that produce more compact code. | ||
## Related to | ||
- TSLint: ['prefer-for-of'](https://palantir.github.io/tslint/rules/prefer-for-of/) |
@@ -1,11 +0,26 @@ | ||
# Use function types instead of interfaces with call signatures (`prefer-function-type`) | ||
--- | ||
description: 'Enforce using function types instead of interfaces with call signatures.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/prefer-function-type** for documentation. | ||
TypeScript allows for two common ways to declare a type for a function: | ||
- Function type: `() => string` | ||
- Object type with a signature: `{ (): string }` | ||
The function type form is generally preferred when possible for being more succinct. | ||
This rule suggests using a function type instead of an interface or object type literal with a single call signature. | ||
Examples of **incorrect** code for this rule: | ||
## Examples | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
interface Foo { | ||
interface Example { | ||
(): string; | ||
@@ -16,4 +31,4 @@ } | ||
```ts | ||
function foo(bar: { (): number }): number { | ||
return bar(); | ||
function foo(example: { (): number }): number { | ||
return example(); | ||
} | ||
@@ -23,9 +38,3 @@ ``` | ||
```ts | ||
interface Foo extends Function { | ||
(): void; | ||
} | ||
``` | ||
```ts | ||
interface MixinMethod { | ||
interface ReturnsSelf { | ||
// returns the function itself, not the `this` argument. | ||
@@ -36,8 +45,11 @@ (arg: string): this; | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
```ts | ||
interface Foo { | ||
(): void; | ||
bar: number; | ||
type Example = () => string; | ||
``` | ||
```ts | ||
function foo(example: () => number): number { | ||
return bar(); | ||
} | ||
@@ -47,2 +59,7 @@ ``` | ||
```ts | ||
// returns the function itself, not the `this` argument. | ||
type ReturnsSelf = (arg: string) => ReturnsSelf; | ||
``` | ||
```ts | ||
function foo(bar: { (): string; baz: number }): string { | ||
@@ -63,9 +80,2 @@ return bar(); | ||
```ts | ||
// returns the `this` argument of function, retaining it's type. | ||
type MixinMethod = <TSelf>(this: TSelf, arg: string) => TSelf; | ||
// a function that returns itself is much clearer in this form. | ||
type ReturnsSelf = (arg: string) => ReturnsSelf; | ||
``` | ||
```ts | ||
// multiple call signatures (overloads) is allowed: | ||
@@ -84,4 +94,5 @@ interface Overloaded { | ||
## Further Reading | ||
- TSLint: [`callable-types`](https://palantir.github.io/tslint/rules/callable-types/) | ||
This rule has a known edge case of sometimes triggering on global augmentations such as `interface Function`. | ||
These edge cases are rare and often symptomatic of odd code. | ||
We recommend you use an [inline ESLint disable comment](https://eslint.org/docs/latest/use/configure/rules#using-configuration-comments-1). | ||
See [#454](https://github.com/typescript-eslint/typescript-eslint/issues/454) for details. |
@@ -1,26 +0,31 @@ | ||
# Enforce `includes` method over `indexOf` method (`prefer-includes`) | ||
--- | ||
description: 'Enforce `includes` method over `indexOf` method.' | ||
--- | ||
Until ES5, we were using `String#indexOf` method to check whether a string contains an arbitrary substring or not. | ||
Until ES2015, we were using `Array#indexOf` method to check whether an array contains an arbitrary value or not. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/prefer-includes** for documentation. | ||
ES2015 has added `String#includes` and ES2016 has added `Array#includes`. | ||
It makes code more understandable if we use those `includes` methods for the purpose. | ||
Prior to ES2015, `Array#indexOf` and `String#indexOf` comparisons against `-1` were the standard ways to check whether a value exists in an array or string, respectively. | ||
Alternatives that are easier to read and write now exist: ES2015 added `String#includes` and ES2016 added `Array#includes`. | ||
## Rule Details | ||
This rule reports when an `.indexOf` call can be replaced with an `.includes`. | ||
Additionally, this rule reports the tests of simple regular expressions in favor of `String#includes`. | ||
This rule is aimed at suggesting `includes` method if `indexOf` method was used to check whether an object contains an arbitrary value or not. | ||
> This rule will report on any receiver object of an `indexOf` method call that has an `includes` method where the two methods have the same parameters. | ||
> Matching types include: `String`, `Array`, `ReadonlyArray`, and typed arrays. | ||
If the receiver object of the `indexOf` method call has `includes` method and the two methods have the same parameters, this rule does suggestion. | ||
There are such types: `String`, `Array`, `ReadonlyArray`, and typed arrays. | ||
## Examples | ||
Additionally, this rule reports the tests of simple regular expressions in favor of `String#includes`. | ||
<!--tabs--> | ||
Examples of **incorrect** code for this rule: | ||
### ❌ Incorrect | ||
```ts | ||
let str: string; | ||
let array: any[]; | ||
let readonlyArray: ReadonlyArray<any>; | ||
let typedArray: UInt8Array; | ||
let userDefined: { | ||
const str: string; | ||
const array: any[]; | ||
const readonlyArray: ReadonlyArray<any>; | ||
const typedArray: UInt8Array; | ||
const maybe: string; | ||
const userDefined: { | ||
indexOf(x: any): number; | ||
@@ -34,39 +39,40 @@ includes(x: any): boolean; | ||
typedArray.indexOf(value) > -1; | ||
maybe?.indexOf('') !== -1; | ||
userDefined.indexOf(value) >= 0; | ||
// simple RegExp test | ||
/foo/.test(str); | ||
/example/.test(str); | ||
``` | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
```ts | ||
let array: any[]; | ||
let readonlyArray: ReadonlyArray<any>; | ||
let typedArray: UInt8Array; | ||
let userDefined: { | ||
const str: string; | ||
const array: any[]; | ||
const readonlyArray: ReadonlyArray<any>; | ||
const typedArray: UInt8Array; | ||
const maybe: string; | ||
const userDefined: { | ||
indexOf(x: any): number; | ||
includes(x: any): boolean; | ||
}; | ||
let mismatchExample: { | ||
indexOf(x: any, fromIndex?: number): number; | ||
includes(x: any): boolean; | ||
}; | ||
str.includes(value); | ||
array.includes(value); | ||
readonlyArray.includes(value); | ||
!readonlyArray.includes(value); | ||
typedArray.includes(value); | ||
maybe?.includes(''); | ||
userDefined.includes(value); | ||
// the two methods have different parameters. | ||
str.includes('example'); | ||
// The two methods have different parameters. | ||
declare const mismatchExample: { | ||
indexOf(x: unknown, fromIndex?: number): number; | ||
includes(x: unknown): boolean; | ||
}; | ||
mismatchExample.indexOf(value) >= 0; | ||
``` | ||
## Options | ||
There are no options. | ||
## When Not To Use It | ||
If you don't want to suggest `includes`, you can safely turn this rule off. |
@@ -1,5 +0,13 @@ | ||
# Require that all enum members be literal values to prevent unintended enum member name shadow issues (`prefer-literal-enum-member`) | ||
--- | ||
description: 'Require all enum members to be literal values.' | ||
--- | ||
TypeScript allows the value of an enum member to be many different kinds of valid JavaScript expressions. However, because enums create their own scope whereby each enum member becomes a variable in that scope, unexpected values could be used at runtime. Example: | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/prefer-literal-enum-member** for documentation. | ||
TypeScript allows the value of an enum member to be many different kinds of valid JavaScript expressions. | ||
However, because enums create their own scope whereby each enum member becomes a variable in that scope, developers are often surprised at the resultant values. | ||
For example: | ||
```ts | ||
@@ -18,10 +26,13 @@ const imOutside = 2; | ||
The answer is that `Foo.c` will be `1` at runtime. The [playground](https://www.typescriptlang.org/play/#src=const%20imOutside%20%3D%202%3B%0D%0Aconst%20b%20%3D%202%3B%0D%0Aenum%20Foo%20%7B%0D%0A%20%20%20%20outer%20%3D%20imOutside%2C%0D%0A%20%20%20%20a%20%3D%201%2C%0D%0A%20%20%20%20b%20%3D%20a%2C%0D%0A%20%20%20%20c%20%3D%20b%2C%0D%0A%20%20%20%20%2F%2F%20does%20c%20%3D%3D%20Foo.b%20%3D%3D%20Foo.c%20%3D%3D%201%3F%0D%0A%20%20%20%20%2F%2F%20or%20does%20c%20%3D%3D%20b%20%3D%3D%202%3F%0D%0A%7D) illustrates this quite nicely. | ||
> The answer is that `Foo.c` will be `1` at runtime [[TypeScript playground](https://www.typescriptlang.org/play/#src=const%20imOutside%20%3D%202%3B%0D%0Aconst%20b%20%3D%202%3B%0D%0Aenum%20Foo%20%7B%0D%0A%20%20%20%20outer%20%3D%20imOutside%2C%0D%0A%20%20%20%20a%20%3D%201%2C%0D%0A%20%20%20%20b%20%3D%20a%2C%0D%0A%20%20%20%20c%20%3D%20b%2C%0D%0A%20%20%20%20%2F%2F%20does%20c%20%3D%3D%20Foo.b%20%3D%3D%20Foo.c%20%3D%3D%201%3F%0D%0A%20%20%20%20%2F%2F%20or%20does%20c%20%3D%3D%20b%20%3D%3D%202%3F%0D%0A%7D)]. | ||
## Rule Details | ||
Therefore, it's often better to prevent unexpected results in code by requiring the use of literal values as enum members. | ||
This rule reports when an enum member is given a value that is not a literal. | ||
This rule is meant to prevent unexpected results in code by requiring the use of literal values as enum members to prevent unexpected runtime behavior. Template literals, arrays, objects, constructors, and all other expression types can end up using a variable from its scope or the parent scope, which can result in the same unexpected behavior at runtime. | ||
## Examples | ||
Examples of **incorrect** code for this rule: | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -38,3 +49,3 @@ const str = 'Test'; | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -51,4 +62,43 @@ ```ts | ||
<!--/tabs--> | ||
## Options | ||
- `allowBitwiseExpressions` set to `true` will allow you to use bitwise expressions in enum initializer (Default: `false`). | ||
Examples of code for the `{ "allowBitwiseExpressions": true }` option: | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
const x = 1; | ||
enum Foo { | ||
A = x << 0, | ||
B = x >> 0, | ||
C = x >>> 0, | ||
D = x | 0, | ||
E = x & 0, | ||
F = x ^ 0, | ||
G = ~x, | ||
} | ||
``` | ||
### ✅ Correct | ||
```ts | ||
enum Foo { | ||
A = 1 << 0, | ||
B = 1 >> 0, | ||
C = 1 >>> 0, | ||
D = 1 | 0, | ||
E = 1 & 0, | ||
F = 1 ^ 0, | ||
G = ~1, | ||
} | ||
``` | ||
## When Not To Use It | ||
If you want use anything other than simple literals as an enum value. |
@@ -1,10 +0,39 @@ | ||
# Require the use of the `namespace` keyword instead of the `module` keyword to declare custom TypeScript modules (`prefer-namespace-keyword`) | ||
--- | ||
description: 'Require using `namespace` keyword over `module` keyword to declare custom TypeScript modules.' | ||
--- | ||
In an effort to prevent further confusion between custom TypeScript modules and the new ES2015 modules, starting | ||
with TypeScript `v1.5` the keyword `namespace` is now the preferred way to declare custom TypeScript modules. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/prefer-namespace-keyword** for documentation. | ||
## Rule Details | ||
TypeScript historically allowed a form of code organization called "custom modules" (`module Example {}`), later renamed to "namespaces" (`namespace Example`). | ||
This rule aims to standardize the way modules are declared. | ||
Namespaces are an outdated way to organize TypeScript code. | ||
ES2015 module syntax is now preferred (`import`/`export`). | ||
For projects still using custom modules / namespaces, it's preferred to refer to them as namespaces. | ||
This rule reports when the `module` keyword is used instead of `namespace`. | ||
> This rule does not report on the use of TypeScript module declarations to describe external APIs (`declare module 'foo' {}`). | ||
## Examples | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
module Example {} | ||
``` | ||
### ✅ Correct | ||
```ts | ||
namespace Example {} | ||
declare module 'foo' {} | ||
``` | ||
<!--/tabs--> | ||
## When Not To Use It | ||
@@ -19,5 +48,1 @@ | ||
- [Namespaces and Modules](https://www.typescriptlang.org/docs/handbook/namespaces-and-modules.html) | ||
## Compatibility | ||
- TSLint: [no-internal-module](https://palantir.github.io/tslint/rules/no-internal-module/) |
@@ -1,58 +0,58 @@ | ||
# Enforce the usage of the nullish coalescing operator instead of logical chaining (`prefer-nullish-coalescing`) | ||
--- | ||
description: 'Enforce using the nullish coalescing operator instead of logical chaining.' | ||
--- | ||
TypeScript 3.7 added support for the nullish coalescing operator. | ||
This operator allows you to safely cascade a value when dealing with `null` or `undefined`. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/prefer-nullish-coalescing** for documentation. | ||
```ts | ||
function myFunc(foo: string | null) { | ||
return foo ?? 'a string'; | ||
} | ||
The `??` nullish coalescing runtime operator allows providing a default value when dealing with `null` or `undefined`. | ||
Because the nullish coalescing operator _only_ coalesces when the original value is `null` or `undefined`, it is much safer than relying upon logical OR operator chaining `||`, which coalesces on any _falsy_ value. | ||
// is equivalent to | ||
This rule reports when an `||` operator can be safely replaced with a `??`. | ||
function myFunc(foo: string | null) { | ||
return foo !== null && foo !== undefined ? foo : 'a string'; | ||
} | ||
``` | ||
:::caution | ||
This rule will not work as expected if [`strictNullChecks`](https://www.typescriptlang.org/tsconfig#strictNullChecks) is not enabled. | ||
::: | ||
Because the nullish coalescing operator _only_ coalesces when the original value is `null` or `undefined`, it is much safer than relying upon logical OR operator chaining `||`; which coalesces on any _falsy_ value: | ||
## Options | ||
```ts | ||
const emptyString = ''; | ||
### `ignoreTernaryTests` | ||
const nullish1 = emptyString ?? 'unsafe'; | ||
const logical1 = emptyString || 'unsafe'; | ||
Setting this option to `true` (the default) will cause the rule to ignore any ternary expressions that could be simplified by using the nullish coalescing operator. | ||
// nullish1 === '' | ||
// logical1 === 'unsafe' | ||
Incorrect code for `ignoreTernaryTests: false`, and correct code for `ignoreTernaryTests: true`: | ||
declare const nullString: string | null; | ||
```ts | ||
const foo: any = 'bar'; | ||
foo !== undefined && foo !== null ? foo : 'a string'; | ||
foo === undefined || foo === null ? 'a string' : foo; | ||
foo == undefined ? 'a string' : foo; | ||
foo == null ? 'a string' : foo; | ||
const nullish2 = nullString ?? 'safe'; | ||
const logical2 = nullString || 'safe'; | ||
const foo: string | undefined = 'bar'; | ||
foo !== undefined ? foo : 'a string'; | ||
foo === undefined ? 'a string' : foo; | ||
// nullish2 === 'safe' | ||
// logical2 === 'safe' | ||
const foo: string | null = 'bar'; | ||
foo !== null ? foo : 'a string'; | ||
foo === null ? 'a string' : foo; | ||
``` | ||
## Rule Details | ||
Correct code for `ignoreTernaryTests: false`: | ||
This rule aims enforce the usage of the safer operator. | ||
```ts | ||
const foo: any = 'bar'; | ||
foo ?? 'a string'; | ||
foo ?? 'a string'; | ||
foo ?? 'a string'; | ||
foo ?? 'a string'; | ||
## Options | ||
const foo: string | undefined = 'bar'; | ||
foo ?? 'a string'; | ||
foo ?? 'a string'; | ||
```ts | ||
type Options = [ | ||
{ | ||
ignoreConditionalTests?: boolean; | ||
ignoreMixedLogicalExpressions?: boolean; | ||
}, | ||
]; | ||
const defaultOptions = [ | ||
{ | ||
ignoreConditionalTests: true, | ||
ignoreMixedLogicalExpressions: true, | ||
}, | ||
]; | ||
const foo: string | null = 'bar'; | ||
foo ?? 'a string'; | ||
foo ?? 'a string'; | ||
``` | ||
@@ -134,2 +134,25 @@ | ||
### `ignorePrimitives` | ||
If you would like to ignore certain primitive types that can be falsy then you may pass an object containing a boolean value for each primitive: | ||
- `string: true`, ignores `null` or `undefined` unions with `string` (default: false). | ||
- `number: true`, ignores `null` or `undefined` unions with `number` (default: false). | ||
- `bigint: true`, ignores `null` or `undefined` unions with `bigint` (default: false). | ||
- `boolean: true`, ignores `null` or `undefined` unions with `boolean` (default: false). | ||
Incorrect code for `ignorePrimitives: { string: true }`, and correct code for `ignorePrimitives: { string: false }`: | ||
```ts | ||
const foo: string | undefined = 'bar'; | ||
foo || 'a string'; | ||
``` | ||
Correct code for `ignorePrimitives: { string: true }`: | ||
```ts | ||
const foo: string | undefined = 'bar'; | ||
foo ?? 'a string'; | ||
``` | ||
## When Not To Use It | ||
@@ -136,0 +159,0 @@ |
@@ -1,49 +0,21 @@ | ||
# Prefer using concise optional chain expressions instead of chained logical ands (`prefer-optional-chain`) | ||
--- | ||
description: 'Enforce using concise optional chain expressions instead of chained logical ands, negated logical ors, or empty objects.' | ||
--- | ||
TypeScript 3.7 added support for the optional chain operator. | ||
This operator allows you to safely access properties and methods on objects when they are potentially `null` or `undefined`. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/prefer-optional-chain** for documentation. | ||
```ts | ||
type T = { | ||
a?: { | ||
b?: { | ||
c: string; | ||
method?: () => void; | ||
}; | ||
}; | ||
}; | ||
`?.` optional chain expressions provide `undefined` if an object is `null` or `undefined`. | ||
Because the optional chain operator _only_ chains when the property value is `null` or `undefined`, it is much safer than relying upon logical AND operator chaining `&&`; which chains on any _truthy_ value. | ||
It is also often less code to use `?.` optional chaining than `&&` truthiness checks. | ||
function myFunc(foo: T | null) { | ||
return foo?.a?.b?.c; | ||
} | ||
// is roughly equivalent to | ||
function myFunc(foo: T | null) { | ||
return foo && foo.a && foo.a.b && foo.a.b.c; | ||
} | ||
This rule reports on code where an `&&` operator can be safely replaced with `?.` optional chaining. | ||
function myFunc(foo: T | null) { | ||
return foo?.['a']?.b?.c; | ||
} | ||
// is roughly equivalent to | ||
function myFunc(foo: T | null) { | ||
return foo && foo['a'] && foo['a'].b && foo['a'].b.c; | ||
} | ||
## Examples | ||
function myFunc(foo: T | null) { | ||
return foo?.a?.b?.method?.(); | ||
} | ||
// is roughly equivalent to | ||
function myFunc(foo: T | null) { | ||
return foo && foo.a && foo.a.b && foo.a.b.method && foo.a.b.method(); | ||
} | ||
``` | ||
<!--tabs--> | ||
Because the optional chain operator _only_ chains when the property value is `null` or `undefined`, it is much safer than relying upon logical AND operator chaining `&&`; which chains on any _truthy_ value. | ||
### ❌ Incorrect | ||
## Rule Details | ||
This rule aims enforce the usage of the safer operator. | ||
Examples of **incorrect** code for this rule: | ||
```ts | ||
@@ -54,2 +26,11 @@ foo && foo.a && foo.a.b && foo.a.b.c; | ||
// With empty objects | ||
(((foo || {}).a || {}).b || {}).c; | ||
(((foo || {})['a'] || {}).b || {}).c; | ||
// With negated `or`s | ||
!foo || !foo.bar; | ||
!foo || !foo[bar]; | ||
!foo || !foo.bar || !foo.bar.baz || !foo.bar.baz(); | ||
// this rule also supports converting chained strict nullish checks: | ||
@@ -64,3 +45,3 @@ foo && | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -73,9 +54,17 @@ ```ts | ||
foo?.a?.b?.c?.d?.e; | ||
!foo?.bar; | ||
!foo?.[bar]; | ||
!foo?.bar?.baz?.(); | ||
``` | ||
**Note:** there are a few edge cases where this rule will false positive. Use your best judgement when evaluating reported errors. | ||
<!--/tabs--> | ||
:::note | ||
There are a few edge cases where this rule will false positive. Use your best judgement when evaluating reported errors. | ||
::: | ||
## When Not To Use It | ||
If you are not using TypeScript 3.7 (or greater), then you will not be able to use this rule, as the operator is not supported. | ||
If you don't mind using more explicit `&&`s, you don't need this rule. | ||
@@ -82,0 +71,0 @@ ## Further Reading |
@@ -1,3 +0,9 @@ | ||
# Requires that function parameters are typed as readonly to prevent accidental mutation of inputs (`prefer-readonly-parameter-types`) | ||
--- | ||
description: 'Require function parameters to be typed as `readonly` to prevent accidental mutation of inputs.' | ||
--- | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/prefer-readonly-parameter-types** for documentation. | ||
Mutating function arguments can lead to confusing, hard to debug behavior. | ||
@@ -7,4 +13,2 @@ Whilst it's easy to implicitly remember to not modify function arguments, explicitly typing arguments as readonly provides clear contract to consumers. | ||
## Rule Details | ||
This rule allows you to enforce that function parameters resolve to readonly types. | ||
@@ -19,4 +23,8 @@ A type is considered readonly if: | ||
Examples of **incorrect** code for this rule: | ||
## Examples | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -62,3 +70,3 @@ function array1(arg: string[]) {} // array is not readonly | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -127,14 +135,2 @@ ```ts | ||
```ts | ||
interface Options { | ||
checkParameterProperties?: boolean; | ||
ignoreInferredTypes?: boolean; | ||
} | ||
const defaultOptions: Options = { | ||
checkParameterProperties: true, | ||
ignoreInferredTypes: false, | ||
}; | ||
``` | ||
### `checkParameterProperties` | ||
@@ -145,4 +141,8 @@ | ||
Examples of **incorrect** code for this rule with `{checkParameterProperties: true}`: | ||
Examples of code for this rule with `{checkParameterProperties: true}`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -154,3 +154,3 @@ class Foo { | ||
Examples of **correct** code for this rule with `{checkParameterProperties: true}`: | ||
#### ✅ Correct | ||
@@ -163,2 +163,4 @@ ```ts | ||
<!--/tabs--> | ||
Examples of **correct** code for this rule with `{checkParameterProperties: false}`: | ||
@@ -179,8 +181,12 @@ | ||
Examples of **incorrect** code for this rule with `{ignoreInferredTypes: true}`: | ||
Examples of code for this rule with `{ignoreInferredTypes: true}`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
import { acceptsCallback, CallbackOptions } from 'external-dependency'; | ||
acceceptsCallback((options: CallbackOptions) => {}); | ||
acceptsCallback((options: CallbackOptions) => {}); | ||
``` | ||
@@ -203,3 +209,3 @@ | ||
Examples of **correct** code for this rule with `{ignoreInferredTypes: true}`: | ||
#### ✅ Correct | ||
@@ -209,3 +215,3 @@ ```ts | ||
acceceptsCallback(options => {}); | ||
acceptsCallback(options => {}); | ||
``` | ||
@@ -227,1 +233,47 @@ | ||
</details> | ||
### `treatMethodsAsReadonly` | ||
This option allows you to treat all mutable methods as though they were readonly. This may be desirable when you are never reassigning methods. | ||
Examples of code for this rule with `{treatMethodsAsReadonly: false}`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
type MyType = { | ||
readonly prop: string; | ||
method(): string; // note: this method is mutable | ||
}; | ||
function foo(arg: MyType) {} | ||
``` | ||
#### ✅ Correct | ||
```ts | ||
type MyType = Readonly<{ | ||
prop: string; | ||
method(): string; | ||
}>; | ||
function foo(arg: MyType) {} | ||
type MyOtherType = { | ||
readonly prop: string; | ||
readonly method: () => string; | ||
}; | ||
function bar(arg: MyOtherType) {} | ||
``` | ||
<!--/tabs--> | ||
Examples of **correct** code for this rule with `{treatMethodsAsReadonly: true}`: | ||
```ts | ||
type MyType = { | ||
readonly prop: string; | ||
method(): string; // note: this method is mutable | ||
}; | ||
function foo(arg: MyType) {} | ||
``` |
@@ -1,12 +0,20 @@ | ||
# Requires that private members are marked as `readonly` if they're never modified outside of the constructor (`prefer-readonly`) | ||
--- | ||
description: "Require private members to be marked as `readonly` if they're never modified outside of the constructor." | ||
--- | ||
This rule enforces that private members are marked as `readonly` if they're never modified outside of the constructor. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/prefer-readonly** for documentation. | ||
## Rule Details | ||
Member variables with the privacy `private` are never permitted to be modified outside of their declaring class. | ||
If that class never modifies their value, they may safely be marked as `readonly`. | ||
Examples of **incorrect** code for this rule: | ||
This rule reports on private members are marked as `readonly` if they're never modified outside of the constructor. | ||
## Examples | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -28,3 +36,3 @@ class Container { | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -50,4 +58,2 @@ ```ts | ||
This rule, in its default state, does not require any argument. | ||
### `onlyInlineLambdas` | ||
@@ -63,11 +69,7 @@ | ||
Example of **correct** code for the `{ "onlyInlineLambdas": true }` options: | ||
Example of code for the `{ "onlyInlineLambdas": true }` options: | ||
```ts | ||
class Container { | ||
private neverModifiedPrivate = 'unchanged'; | ||
} | ||
``` | ||
<!--tabs--> | ||
Example of **incorrect** code for the `{ "onlyInlineLambdas": true }` options: | ||
#### ❌ Incorrect | ||
@@ -82,4 +84,8 @@ ```ts | ||
## Related to | ||
#### ✅ Correct | ||
- TSLint: ['prefer-readonly'](https://palantir.github.io/tslint/rules/prefer-readonly) | ||
```ts | ||
class Container { | ||
private neverModifiedPrivate = 'unchanged'; | ||
} | ||
``` |
@@ -1,3 +0,9 @@ | ||
# Prefer using type parameter when calling `Array#reduce` instead of casting (`prefer-reduce-type-parameter`) | ||
--- | ||
description: 'Enforce using type parameter when calling `Array#reduce` instead of casting.' | ||
--- | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/prefer-reduce-type-parameter** for documentation. | ||
It's common to call `Array#reduce` with a generic type, such as an array or object, as the initial value. | ||
@@ -9,15 +15,17 @@ Since these values are empty, their types are not usable: | ||
A common solution to this problem is to cast the initial value. While this will work, it's not the most optimal | ||
solution as casting has subtle effects on the underlying types that can allow bugs to slip in. | ||
A common solution to this problem is to use an `as` assertion on the initial value. | ||
While this will work, it's not the most optimal solution as type assertions have subtle effects on the underlying types that can allow bugs to slip in. | ||
A better (and lesser known) solution is to pass the type in as a generic parameter to `Array#reduce` explicitly. | ||
A better solution is to pass the type in as a generic type argument to `Array#reduce` explicitly. | ||
This means that TypeScript doesn't have to try to infer the type, and avoids the common pitfalls that come with casting. | ||
## Rule Details | ||
This rule looks for calls to `Array#reduce`, and reports if an initial value is being passed & asserted. | ||
It will suggest instead pass the asserted type to `Array#reduce` as a generic type argument. | ||
This rule looks for calls to `Array#reduce`, and warns if an initial value is being passed & casted, | ||
suggesting instead to pass the cast type to `Array#reduce` as its generic parameter. | ||
## Examples | ||
Examples of **incorrect** code for this rule: | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -35,3 +43,3 @@ [1, 2, 3].reduce((arr, num) => arr.concat(num * 2), [] as number[]); | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -50,8 +58,4 @@ ```ts | ||
## Options | ||
There are no options. | ||
## When Not To Use It | ||
If you don't want to use typechecking in your linting, you can't use this rule. |
@@ -1,19 +0,22 @@ | ||
# Enforce that `RegExp#exec` is used instead of `String#match` if no global flag is provided (`prefer-regexp-exec`) | ||
--- | ||
description: 'Enforce `RegExp#exec` over `String#match` if no global flag is provided.' | ||
--- | ||
`RegExp#exec` is faster than `String#match` and both work the same when not using the `/g` flag. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/prefer-regexp-exec** for documentation. | ||
## Rule Details | ||
`String#match` is defined to work the same as `RegExp#exec` when the regular expression does not include the `g` flag. | ||
Keeping to consistently using one of the two can help improve code readability. | ||
This rule is aimed at enforcing the more performant way of applying regular expressions on strings. | ||
This rule reports when a `String#match` call can be replaced with an equivalent `RegExp#exec`. | ||
From [`String#match` on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match): | ||
> `RegExp#exec` may also be slightly faster than `String#match`; this is the reason to choose it as the preferred usage. | ||
> If the regular expression does not include the g flag, returns the same result as `RegExp.exec()`. | ||
## Examples | ||
From [Stack Overflow](https://stackoverflow.com/questions/9214754/what-is-the-difference-between-regexp-s-exec-function-and-string-s-match-fun) | ||
<!--tabs--> | ||
> `RegExp.prototype.exec` is a lot faster than `String.prototype.match`, but that’s because they are not exactly the same thing, they are different. | ||
### ❌ Incorrect | ||
Examples of **incorrect** code for this rule: | ||
```ts | ||
@@ -29,3 +32,3 @@ 'something'.match(/thing/); | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -42,14 +45,4 @@ ```ts | ||
## Options | ||
There are no options. | ||
```json | ||
{ | ||
"@typescript-eslint/prefer-regexp-exec": "error" | ||
} | ||
``` | ||
## When Not To Use It | ||
If you prefer consistent use of `String#match` for both, with `g` flag and without it, you can turn this rule off. | ||
If you prefer consistent use of `String#match` for both with `g` flag and without it, you can turn this rule off. |
@@ -1,15 +0,23 @@ | ||
# Enforce the use of `String#startsWith` and `String#endsWith` instead of other equivalent methods of checking substrings (`prefer-string-starts-ends-with`) | ||
--- | ||
description: 'Enforce using `String#startsWith` and `String#endsWith` over other equivalent methods of checking substrings.' | ||
--- | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/prefer-string-starts-ends-with** for documentation. | ||
There are multiple ways to verify if a string starts or ends with a specific string, such as `foo.indexOf('bar') === 0`. | ||
As of ES2015, the most common way in JavaScript is to use `String#startsWith` and `String#endsWith`. | ||
Keeping to those methods consistently helps with code readability. | ||
Since ES2015 has added `String#startsWith` and `String#endsWith`, this rule reports other ways to be consistent. | ||
This rule reports when a string method can be replaced safely with `String#startsWith` or `String#endsWith`. | ||
## Rule Details | ||
## Examples | ||
This rule is aimed at enforcing a consistent way to check whether a string starts or ends with a specific string. | ||
<!--tabs--> | ||
Examples of **incorrect** code for this rule: | ||
### ❌ Incorrect | ||
```ts | ||
let foo: string; | ||
declare const foo: string; | ||
@@ -35,21 +43,16 @@ // starts with | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
```ts | ||
declare const foo: string; | ||
// starts with | ||
foo.startsWith('bar'); | ||
// ends with | ||
foo.endsWith('bar'); | ||
``` | ||
## Options | ||
There are no options. | ||
```JSON | ||
{ | ||
"@typescript-eslint/prefer-string-starts-ends-with": "error" | ||
} | ||
``` | ||
## When Not To Use It | ||
If you don't mind that style, you can turn this rule off safely. |
@@ -1,19 +0,23 @@ | ||
# Recommends using `@ts-expect-error` over `@ts-ignore` (`prefer-ts-expect-error`) | ||
--- | ||
description: 'Enforce using `@ts-expect-error` over `@ts-ignore`.' | ||
--- | ||
TypeScript allows you to suppress all errors on a line by placing a single-line comment or a comment block line starting with `@ts-ignore` immediately before the erroring line. | ||
While powerful, there is no way to know if a `@ts-ignore` is actually suppressing an error without manually investigating what happens when the `@ts-ignore` is removed. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/prefer-ts-expect-error** for documentation. | ||
TypeScript allows you to suppress all errors on a line by placing a comment starting with `@ts-ignore` or `@ts-expect-error` immediately before the erroring line. | ||
The two directives work the same, except `@ts-expect-error` causes a type error if placed before a line that's not erroring in the first place. | ||
This means its easy for `@ts-ignore`s to be forgotten about, and remain in code even after the error they were suppressing is fixed. | ||
This is dangerous, as if a new error arises on that line it'll be suppressed by the forgotten about `@ts-ignore`, and so be missed. | ||
To address this, TS3.9 ships with a new single-line comment directive: `// @ts-expect-error`. | ||
## Examples | ||
This directive operates in the same manner as `@ts-ignore`, but will error if the line it's meant to be suppressing doesn't actually contain an error, making it a lot safer. | ||
This rule reports any usage of `@ts-ignore`, including a fixer to replace with `@ts-expect-error`. | ||
## Rule Details | ||
<!--tabs--> | ||
This rule looks for usages of `@ts-ignore`, and flags them to be replaced with `@ts-expect-error`. | ||
### ❌ Incorrect | ||
Examples of **incorrect** code for this rule: | ||
```ts | ||
@@ -38,3 +42,3 @@ // @ts-ignore | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -62,3 +66,3 @@ ```ts | ||
If you are **NOT** using TypeScript 3.9 (or greater), then you will not be able to use this rule, as the directive is not supported | ||
If you are compiling against multiple versions of TypeScript and using `@ts-ignore` to ignore version-specific type errors, this rule might get in your way. | ||
@@ -65,0 +69,0 @@ ## Further Reading |
@@ -1,4 +0,9 @@ | ||
# Requires any function or method that returns a Promise to be marked async (`promise-function-async`) | ||
--- | ||
description: 'Require any function or method that returns a Promise to be marked async.' | ||
--- | ||
Requires any function or method that returns a Promise to be marked async. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/promise-function-async** for documentation. | ||
Ensures that each function is only capable of: | ||
@@ -9,10 +14,16 @@ | ||
In contrast, non-`async` `Promise` - returning functions are technically capable of either. | ||
In contrast, non-`async`, `Promise`-returning functions are technically capable of either. | ||
Code that handles the results of those functions will often need to handle both cases, which can get complex. | ||
This rule's practice removes a requirement for creating code to handle both cases. | ||
## Rule Details | ||
> When functions return unions of `Promise` and non-`Promise` types implicitly, it is usually a mistake—this rule flags those cases. If it is intentional, make the return type explicitly to allow the rule to pass. | ||
Examples of **incorrect** code for this rule | ||
## Examples | ||
Examples of code for this rule | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -24,5 +35,9 @@ const arrowFunctionReturnsPromise = () => Promise.resolve('value'); | ||
} | ||
function functionReturnsUnionWithPromiseImplicitly(p: boolean) { | ||
return p ? 'value' : Promise.resolve('value'); | ||
} | ||
``` | ||
Examples of **correct** code for this rule | ||
### ✅ Correct | ||
@@ -35,35 +50,13 @@ ```ts | ||
} | ||
``` | ||
## Options | ||
// An explicit return type that is not Promise means this function cannot be made async, so it is ignored by the rule | ||
function functionReturnsUnionWithPromiseExplicitly( | ||
p: boolean, | ||
): string | Promise<string> { | ||
return p ? 'value' : Promise.resolve('value'); | ||
} | ||
Options may be provided as an object with: | ||
- `allowAny` to indicate that `any` or `unknown` shouldn't be considered Promises (`true` by default). | ||
- `allowedPromiseNames` to indicate any extra names of classes or interfaces to be considered Promises when returned. | ||
In addition, each of the following properties may be provided, and default to `true`: | ||
- `checkArrowFunctions` | ||
- `checkFunctionDeclarations` | ||
- `checkFunctionExpressions` | ||
- `checkMethodDeclarations` | ||
```json | ||
{ | ||
"@typescript-eslint/promise-function-async": [ | ||
"error", | ||
{ | ||
"allowedPromiseNames": ["Thenable"], | ||
"checkArrowFunctions": true, | ||
"checkFunctionDeclarations": true, | ||
"checkFunctionExpressions": true, | ||
"checkMethodDeclarations": true | ||
} | ||
] | ||
async function functionReturnsUnionWithPromiseImplicitly(p: boolean) { | ||
return p ? 'value' : Promise.resolve('value'); | ||
} | ||
``` | ||
## Related To | ||
- TSLint: [promise-function-async](https://palantir.github.io/tslint/rules/promise-function-async) |
@@ -1,22 +0,12 @@ | ||
# Enforce the consistent use of either backticks, double, or single quotes (`quotes`) | ||
--- | ||
description: 'Enforce the consistent use of either backticks, double, or single quotes.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/quotes** for documentation. | ||
## Examples | ||
This rule extends the base [`eslint/quotes`](https://eslint.org/docs/rules/quotes) rule. | ||
It adds support for TypeScript features which allow quoted names, but not backtick quoted names. | ||
## How to use | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"quotes": "off", | ||
"@typescript-eslint/quotes": ["error"] | ||
} | ||
``` | ||
## Options | ||
See [`eslint/quotes` options](https://eslint.org/docs/rules/quotes#options). | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/quotes.md)</sup> |
@@ -1,9 +0,13 @@ | ||
# Requires `Array#sort` calls to always provide a `compareFunction` (`require-array-sort-compare`) | ||
--- | ||
description: 'Require `Array#sort` calls to always provide a `compareFunction`.' | ||
--- | ||
This rule prevents invoking the `Array#sort()` method without providing a `compare` argument. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/require-array-sort-compare** for documentation. | ||
When called without a compare function, `Array#sort()` converts all non-undefined array elements into strings and then compares said strings based off their UTF-16 code units. | ||
When called without a compare function, `Array#sort()` converts all non-undefined array elements into strings and then compares said strings based off their UTF-16 code units [[ECMA specification](https://www.ecma-international.org/ecma-262/9.0/#sec-sortcompare)]. | ||
The result is that elements are sorted alphabetically, regardless of their type. | ||
When sorting numbers, this results in the classic "10 before 2" order: | ||
For example, when sorting numbers, this results in a "10 before 2" order: | ||
@@ -14,12 +18,12 @@ ```ts | ||
This also means that `Array#sort` does not always sort consistently, as elements may have custom `#toString` implementations that are not deterministic; this trap is noted in the noted in the language specification thusly: | ||
This rule reports on any call to the `Array#sort()` method that doesn't provide a `compare` argument. | ||
> NOTE 2: Method calls performed by the `ToString` abstract operations in steps 5 and 7 have the potential to cause `SortCompare` to not behave as a consistent comparison function.<br> > https://www.ecma-international.org/ecma-262/9.0/#sec-sortcompare | ||
## Examples | ||
## Rule Details | ||
This rule aims to ensure all calls of the native `Array#sort` method provide a `compareFunction`, while ignoring calls to user-defined `sort` methods. | ||
Examples of **incorrect** code for this rule: | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -35,3 +39,3 @@ const array: any[]; | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -50,21 +54,10 @@ ```ts | ||
The rule accepts an options object with the following properties: | ||
### `ignoreStringArrays` | ||
```ts | ||
type Options = { | ||
/** | ||
* If true, an array which all elements are string is ignored. | ||
*/ | ||
ignoreStringArrays?: boolean; | ||
}; | ||
Examples of code for this rule with `{ ignoreStringArrays: true }`: | ||
const defaults = { | ||
ignoreStringArrays: false, | ||
}; | ||
``` | ||
<!--tabs--> | ||
### `ignoreStringArrays` | ||
#### ❌ Incorrect | ||
Examples of **incorrect** code for this rule with `{ ignoreStringArrays: true }`: | ||
```ts | ||
@@ -77,3 +70,3 @@ const one = 1; | ||
Examples of **correct** code for this rule with `{ ignoreStringArrays: true }`: | ||
#### ✅ Correct | ||
@@ -89,2 +82,2 @@ ```ts | ||
If you understand the language specification enough, you can turn this rule off safely. | ||
If you understand the language specification enough, and/or only ever sort arrays in a string-like manner, you can turn this rule off safely. |
@@ -1,5 +0,11 @@ | ||
# Disallow async functions which have no `await` expression (`require-await`) | ||
--- | ||
description: 'Disallow async functions which have no `await` expression.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/require-await** for documentation. | ||
## Examples | ||
This rule extends the base [`eslint/require-await`](https://eslint.org/docs/rules/require-await) rule. | ||
@@ -18,16 +24,2 @@ It uses type information to add support for `async` functions that return a `Promise`. | ||
## How to use | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"require-await": "off", | ||
"@typescript-eslint/require-await": "error" | ||
} | ||
``` | ||
## Options | ||
See [`eslint/require-await` options](https://eslint.org/docs/rules/require-await#options). | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/require-await.md)</sup> | ||
## How to Use |
@@ -1,15 +0,30 @@ | ||
# When adding two variables, operands must both be of type number or of type string (`restrict-plus-operands`) | ||
--- | ||
description: 'Require both operands of addition to be the same type and be `bigint`, `number`, or `string`.' | ||
--- | ||
Examples of **correct** code: | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/restrict-plus-operands** for documentation. | ||
TypeScript allows `+` adding together two values of any type(s). | ||
However, adding values that are not the same type and/or are not the same primitive type is often a sign of programmer error. | ||
This rule reports when a `+` operation combines two values of different types, or a type that is not `bigint`, `number`, or `string`. | ||
## Examples | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
var foo = parseInt('5.5', 10) + 10; | ||
var foo = 1n + 1n; | ||
let foo = '5.5' + 5; | ||
let foo = 1n + 1; | ||
``` | ||
Examples of **incorrect** code: | ||
### ✅ Correct | ||
```ts | ||
var foo = '5.5' + 5; | ||
var foo = 1n + 1; | ||
let foo = parseInt('5.5', 10) + 10; | ||
let foo = 1n + 1n; | ||
``` | ||
@@ -19,14 +34,148 @@ | ||
This rule has an object option: | ||
:::caution | ||
We generally recommend against using these options, as they limit which varieties of incorrect `+` usage can be checked. | ||
This in turn severely limits the validation that the rule can do to ensure that resulting strings and numbers are correct. | ||
- `"checkCompoundAssignments": false`: (default) does not check compound assignments (`+=`) | ||
- `"checkCompoundAssignments": true` | ||
Safer alternatives to using the `allow*` options include: | ||
- Using variadic forms of logging APIs to avoid needing to `+` values. | ||
```ts | ||
// Remove this line | ||
console.log('The result is ' + true); | ||
// Add this line | ||
console.log('The result is', true); | ||
``` | ||
- Using `.toFixed()` to coerce numbers to well-formed string representations: | ||
```ts | ||
const number = 1.123456789; | ||
const result = 'The number is ' + number.toFixed(2); | ||
// result === 'The number is 1.12' | ||
``` | ||
- Calling `.toString()` on other types to mark explicit and intentional string coercion: | ||
```ts | ||
const arg = '11'; | ||
const regex = /[0-9]/; | ||
const result = | ||
'The result of ' + | ||
regex.toString() + | ||
'.test("' + | ||
arg + | ||
'") is ' + | ||
regex.test(arg).toString(); | ||
// result === 'The result of /[0-9]/.test("11") is true' | ||
``` | ||
::: | ||
### `allowAny` | ||
Examples of code for this rule with `{ allowAny: true }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
let fn = (a: number, b: []) => a + b; | ||
let fn = (a: string, b: []) => a + b; | ||
``` | ||
#### ✅ Correct | ||
```ts | ||
let fn = (a: number, b: any) => a + b; | ||
let fn = (a: string, b: any) => a + b; | ||
``` | ||
### `allowBoolean` | ||
Examples of code for this rule with `{ allowBoolean: true }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
let fn = (a: number, b: unknown) => a + b; | ||
let fn = (a: string, b: unknown) => a + b; | ||
``` | ||
#### ✅ Correct | ||
```ts | ||
let fn = (a: number, b: boolean) => a + b; | ||
let fn = (a: string, b: boolean) => a + b; | ||
``` | ||
### `allowNullish` | ||
Examples of code for this rule with `{ allowNullish: true }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
let fn = (a: number, b: unknown) => a + b; | ||
let fn = (a: number, b: never) => a + b; | ||
let fn = (a: string, b: unknown) => a + b; | ||
let fn = (a: string, b: never) => a + b; | ||
``` | ||
#### ✅ Correct | ||
```ts | ||
let fn = (a: number, b: undefined) => a + b; | ||
let fn = (a: number, b: null) => a + b; | ||
let fn = (a: string, b: undefined) => a + b; | ||
let fn = (a: string, b: null) => a + b; | ||
``` | ||
### `allowNumberAndString` | ||
Examples of code for this rule with `{ allowNumberAndString: true }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
let fn = (a: number, b: unknown) => a + b; | ||
let fn = (a: number, b: never) => a + b; | ||
``` | ||
#### ✅ Correct | ||
```ts | ||
let fn = (a: number, b: string) => a + b; | ||
let fn = (a: number, b: number | string) => a + b; | ||
``` | ||
### `allowRegExp` | ||
Examples of code for this rule with `{ allowRegExp: true }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
let fn = (a: number, b: RegExp) => a + b; | ||
``` | ||
#### ✅ Correct | ||
```ts | ||
let fn = (a: string, b: RegExp) => a + b; | ||
``` | ||
### `checkCompoundAssignments` | ||
Examples of **incorrect** code for the `{ "checkCompoundAssignments": true }` option: | ||
Examples of code for this rule with `{ checkCompoundAssignments: true }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
/*eslint @typescript-eslint/restrict-plus-operands: ["error", { "checkCompoundAssignments": true }]*/ | ||
let foo: string | undefined; | ||
@@ -39,7 +188,5 @@ foo += 'some data'; | ||
Examples of **correct** code for the `{ "checkCompoundAssignments": true }` option: | ||
#### ✅ Correct | ||
```ts | ||
/*eslint @typescript-eslint/restrict-plus-operands: ["error", { "checkCompoundAssignments": true }]*/ | ||
let foo: number = 0; | ||
@@ -52,10 +199,13 @@ foo += 1; | ||
```json | ||
{ | ||
"@typescript-eslint/restrict-plus-operands": "error" | ||
} | ||
``` | ||
## When Not To Use It | ||
## Compatibility | ||
If you don't mind `"[object Object]"` in your strings, then you will not need this rule. | ||
- TSLint: [restrict-plus-operands](https://palantir.github.io/tslint/rules/restrict-plus-operands/) | ||
## Related To | ||
- [`no-base-to-string`](./no-base-to-string.md) | ||
- [`restrict-template-expressions`](./restrict-template-expressions.md) | ||
## Further Reading | ||
- [`Object.prototype.toString()` MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString) |
@@ -1,16 +0,29 @@ | ||
# Enforce template literal expressions to be of string type (`restrict-template-expressions`) | ||
--- | ||
description: 'Enforce template literal expressions to be of `string` type.' | ||
--- | ||
Examples of **correct** code: | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/restrict-template-expressions** for documentation. | ||
```ts | ||
const arg = 'foo'; | ||
const msg1 = `arg = ${arg}`; | ||
const msg2 = `arg = ${arg || 'default'}`; | ||
JavaScript automatically [converts an object to a string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String#string_coercion) in a string context, such as when concatenating it with a string using `+` or embedding it in a template literal using `${}`. | ||
The default `toString()` method of objects returns `"[object Object]"`, which is often not what was intended. | ||
This rule reports on values used in a template literal string that aren't strings, numbers, or BigInts, optionally allowing other data types that provide useful stringification results. | ||
const stringWithKindProp: string & { _kind?: 'MyString' } = 'foo'; | ||
const msg3 = `stringWithKindProp = ${stringWithKindProp}`; | ||
``` | ||
:::note | ||
Examples of **incorrect** code: | ||
This rule intentionally does not allow objects with a custom `toString()` method to be used in template literals, because the stringification result may not be user-friendly. | ||
For example, arrays have a custom [`toString()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toString) method, which only calls `join()` internally, which joins the array elements with commas. This means that (1) array elements are not necessarily stringified to useful results (2) the commas don't have spaces after them, making the result not user-friendly. The best way to format arrays is to use [`Intl.ListFormat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat), which even supports adding the "and" conjunction where necessary. | ||
You must explicitly call `object.toString()` if you want to use this object in a template literal. | ||
The [`no-base-to-string`](./no-base-to-string.md) rule can be used to guard this case against producing `"[object Object]"` by accident. | ||
::: | ||
## Examples | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -24,26 +37,15 @@ const arg1 = [1, 2]; | ||
## Options | ||
### ✅ Correct | ||
The rule accepts an options object with the following properties: | ||
```ts | ||
type Options = { | ||
// if true, also allow number type in template expressions | ||
allowNumber?: boolean; | ||
// if true, also allow boolean type in template expressions | ||
allowBoolean?: boolean; | ||
// if true, also allow any in template expressions | ||
allowAny?: boolean; | ||
// if true, also allow null and undefined in template expressions | ||
allowNullish?: boolean; | ||
}; | ||
const arg = 'foo'; | ||
const msg1 = `arg = ${arg}`; | ||
const msg2 = `arg = ${arg || 'default'}`; | ||
const defaults = { | ||
allowNumber: true, | ||
allowBoolean: false, | ||
allowAny: false, | ||
allowNullish: false, | ||
}; | ||
const stringWithKindProp: string & { _kind?: 'MyString' } = 'foo'; | ||
const msg3 = `stringWithKindProp = ${stringWithKindProp}`; | ||
``` | ||
## Options | ||
### `allowNumber` | ||
@@ -59,2 +61,4 @@ | ||
This option controls both numbers and BigInts. | ||
### `allowBoolean` | ||
@@ -88,1 +92,29 @@ | ||
``` | ||
### `allowRegExp` | ||
Examples of additional **correct** code for this rule with `{ allowRegExp: true }`: | ||
```ts | ||
const arg = new RegExp('foo'); | ||
const msg1 = `arg = ${arg}`; | ||
``` | ||
```ts | ||
const arg = /foo/; | ||
const msg1 = `arg = ${arg}`; | ||
``` | ||
### `allowNever` | ||
Examples of additional **correct** code for this rule with `{ allowNever: true }`: | ||
```ts | ||
const arg = 'something'; | ||
const msg1 = typeof arg === 'string' ? arg : `arg = ${arg}`; | ||
``` | ||
## Related To | ||
- [`no-base-to-string`](./no-base-to-string.md) | ||
- [`restrict-plus-operands`](./restrict-plus-operands.md) |
@@ -1,6 +0,12 @@ | ||
# Enforces consistent returning of awaited values (`return-await`) | ||
--- | ||
description: 'Enforce consistent returning of awaited values.' | ||
--- | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/return-await** for documentation. | ||
Returning an awaited promise can make sense for better stack trace information as well as for consistent error handling (returned promises will not be caught in an async function try/catch). | ||
## Rule Details | ||
## Examples | ||
@@ -10,12 +16,2 @@ This rule builds on top of the [`eslint/no-return-await`](https://eslint.org/docs/rules/no-return-await) rule. | ||
## How to use | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"no-return-await": "off", | ||
"@typescript-eslint/return-await": "error" | ||
} | ||
``` | ||
## Options | ||
@@ -39,4 +35,8 @@ | ||
Examples of **incorrect** code with `in-try-catch`: | ||
Examples of code with `in-try-catch`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -86,3 +86,3 @@ async function invalidInTryCatch1() { | ||
Examples of **correct** code with `in-try-catch`: | ||
#### ✅ Correct | ||
@@ -137,4 +137,8 @@ ```ts | ||
Examples of **incorrect** code with `always`: | ||
Examples of code with `always`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -156,3 +160,3 @@ async function invalidAlways1() { | ||
Examples of **correct** code with `always`: | ||
#### ✅ Correct | ||
@@ -179,4 +183,8 @@ ```ts | ||
Examples of **incorrect** code with `never`: | ||
Examples of code with `never`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -198,3 +206,3 @@ async function invalidNever1() { | ||
Examples of **correct** code with `never`: | ||
#### ✅ Correct | ||
@@ -201,0 +209,0 @@ ```ts |
@@ -1,6 +0,12 @@ | ||
# Require or disallow semicolons instead of ASI (`semi`) | ||
--- | ||
description: 'Require or disallow semicolons instead of ASI.' | ||
--- | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/semi** for documentation. | ||
This rule enforces consistent use of semicolons after statements. | ||
## Rule Details | ||
## Examples | ||
@@ -11,17 +17,1 @@ This rule extends the base [`eslint/semi`](https://eslint.org/docs/rules/semi) rule. | ||
See also the [`@typescript-eslint/member-delimiter-style`](member-delimiter-style.md) rule, which allows you to specify the delimiter for `type` and `interface` members. | ||
## How to use | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"semi": "off", | ||
"@typescript-eslint/semi": ["error"] | ||
} | ||
``` | ||
## Options | ||
See [`eslint/semi` options](https://eslint.org/docs/rules/semi#options). | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/semi.md)</sup> |
@@ -1,3 +0,14 @@ | ||
# Enforces that members of a type union/intersection are sorted alphabetically (`sort-type-union-intersection-members`) | ||
--- | ||
description: 'Enforce members of a type union/intersection to be sorted alphabetically.' | ||
--- | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/sort-type-union-intersection-members** for documentation. | ||
:::danger Deprecated | ||
This rule has been renamed to [`sort-type-constituents`](./sort-type-constituents.md). | ||
::: | ||
Sorting union (`|`) and intersection (`&`) types can help: | ||
@@ -9,21 +20,12 @@ | ||
## Rule Details | ||
This rule reports on any types that aren't sorted alphabetically. | ||
Sorting within each group is done using the following code: | ||
> Types are sorted case-insensitively and treating numbers like a human would, falling back to character code sorting in case of ties. | ||
```ts | ||
const collator = new Intl.Collator('en', { | ||
sensitivity: 'base', | ||
numeric: true, | ||
}); | ||
## Examples | ||
function compare(a, b) { | ||
return collator.compare(a, b) || (a < b ? -1 : a > b ? 1 : 0); | ||
} | ||
``` | ||
<!--tabs--> | ||
In other words, the types are sorted alphabetically, case-insensitively and treating numbers like a human would, falling back to character code sorting in case of ties. | ||
### ❌ Incorrect | ||
Examples of **incorrect** code for this rule: | ||
```ts | ||
@@ -57,3 +59,3 @@ type T1 = B | A; | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -90,45 +92,2 @@ ```ts | ||
```ts | ||
type Options = { | ||
// true to check intersection types, false otherwise | ||
checkIntersections?: boolean; | ||
// true to check union types, false otherwise | ||
checkUnions?: boolean; | ||
// the ordering of the groups | ||
groupOrder?: ( | ||
| 'conditional' | ||
| 'function' | ||
| 'import' | ||
| 'intersection' | ||
| 'keyword' | ||
| 'literal' | ||
| 'named' | ||
| 'object' | ||
| 'operator' | ||
| 'tuple' | ||
| 'union' | ||
| 'nullish' | ||
)[]; | ||
}; | ||
const defaultOptions: Options = { | ||
checkIntersections: true, | ||
checkUnions: true, | ||
groupOrder: [ | ||
'named', | ||
'keyword', | ||
'operator', | ||
'literal', | ||
'function', | ||
'import', | ||
'conditional', | ||
'object', | ||
'tuple', | ||
'intersection', | ||
'union', | ||
'nullish', | ||
], | ||
}; | ||
``` | ||
### `groupOrder` | ||
@@ -135,0 +94,0 @@ |
@@ -1,22 +0,12 @@ | ||
# Enforces consistent spacing before function parenthesis (`space-before-function-paren`) | ||
--- | ||
description: 'Enforce consistent spacing before function parenthesis.' | ||
--- | ||
## Rule Details | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/space-before-function-paren** for documentation. | ||
## Examples | ||
This rule extends the base [`eslint/space-before-function-paren`](https://eslint.org/docs/rules/space-before-function-paren) rule. | ||
It adds support for generic type parameters on function calls. | ||
## How to use | ||
```jsonc | ||
{ | ||
// note you must disable the base rule as it can report incorrect errors | ||
"space-before-function-paren": "off", | ||
"@typescript-eslint/space-before-function-paren": ["error"] | ||
} | ||
``` | ||
## Options | ||
See [`eslint/space-before-function-paren` options](https://eslint.org/docs/rules/space-before-function-paren#options). | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/space-before-function-paren.md)</sup> |
@@ -1,7 +0,12 @@ | ||
# This rule is aimed at ensuring there are spaces around infix operators. (`space-infix-ops`) | ||
--- | ||
description: 'Require spacing around infix operators.' | ||
--- | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/space-infix-ops** for documentation. | ||
This rule extends the base [`eslint/space-infix-ops`](https://eslint.org/docs/rules/space-infix-ops) rule. | ||
It adds support for enum members. | ||
It also add support for enum members | ||
```ts | ||
@@ -12,16 +17,1 @@ enum MyEnum { | ||
``` | ||
## How to use | ||
```jsonc | ||
{ | ||
"space-infix-ops": "off", | ||
"@typescript-eslint/space-infix-ops": ["error", { "int32Hint": false }] | ||
} | ||
``` | ||
## Options | ||
See [`eslint/space-infix-ops` options](https://eslint.org/docs/rules/space-infix-ops#options). | ||
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/space-infix-ops.md)</sup> |
@@ -1,3 +0,9 @@ | ||
# Restricts the types allowed in boolean expressions (`strict-boolean-expressions`) | ||
--- | ||
description: 'Disallow certain types in boolean expressions.' | ||
--- | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/strict-boolean-expressions** for documentation. | ||
Forbids usage of non-boolean types in expressions where a boolean is expected. | ||
@@ -18,4 +24,6 @@ `boolean` and `never` types are always allowed. | ||
Examples of **incorrect** code for this rule: | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -51,3 +59,3 @@ // nullable numbers are considered unsafe by default | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -84,25 +92,2 @@ ```tsx | ||
```ts | ||
type Options = { | ||
allowString?: boolean; | ||
allowNumber?: boolean; | ||
allowNullableObject?: boolean; | ||
allowNullableBoolean?: boolean; | ||
allowNullableString?: boolean; | ||
allowNullableNumber?: boolean; | ||
allowAny?: boolean; | ||
}; | ||
const defaultOptions: Options = { | ||
allowString: true, | ||
allowNumber: true, | ||
allowNullableObject: true, | ||
allowNullableBoolean: false, | ||
allowNullableString: false, | ||
allowNullableNumber: false, | ||
allowAny: false, | ||
allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: false, | ||
}; | ||
``` | ||
### `allowString` | ||
@@ -145,2 +130,8 @@ | ||
### `allowNullableEnum` | ||
Allows `enum | null | undefined` in a boolean context. | ||
This is unsafe because nullable enums can be either a falsy number or nullish. | ||
Set this to `true` if you don't mind implicitly treating an enum whose value is zero the same as a nullish value. | ||
### `allowAny` | ||
@@ -162,6 +153,36 @@ | ||
## Fixes and Suggestions | ||
This rule provides following fixes and suggestions for particular types in boolean context: | ||
- `boolean` - Always allowed - no fix needed. | ||
- `string` - (when `allowString` is `false`) - Provides following suggestions: | ||
- Change condition to check string's length (`str` → `str.length > 0`) | ||
- Change condition to check for empty string (`str` → `str !== ""`) | ||
- Explicitly cast value to a boolean (`str` → `Boolean(str)`) | ||
- `number` - (when `allowNumber` is `false`): | ||
- For `array.length` - Provides **autofix**: | ||
- Change condition to check for 0 (`array.length` → `array.length > 0`) | ||
- For other number values - Provides following suggestions: | ||
- Change condition to check for 0 (`num` → `num !== 0`) | ||
- Change condition to check for NaN (`num` → `!Number.isNaN(num)`) | ||
- Explicitly cast value to a boolean (`num` → `Boolean(num)`) | ||
- `object | null | undefined` - (when `allowNullableObject` is `false`) - Provides **autofix**: | ||
- Change condition to check for null/undefined (`maybeObj` → `maybeObj != null`) | ||
- `boolean | null | undefined` - Provides following suggestions: | ||
- Explicitly treat nullish value the same as false (`maybeBool` → `maybeBool ?? false`) | ||
- Change condition to check for true/false (`maybeBool` → `maybeBool === true`) | ||
- `string | null | undefined` - Provides following suggestions: | ||
- Change condition to check for null/undefined (`maybeStr` → `maybeStr != null`) | ||
- Explicitly treat nullish value the same as an empty string (`maybeStr` → `maybeStr ?? ""`) | ||
- Explicitly cast value to a boolean (`maybeStr` → `Boolean(maybeStr)`) | ||
- `number | null | undefined` - Provides following suggestions: | ||
- Change condition to check for null/undefined (`maybeNum` → `maybeNum != null`) | ||
- Explicitly treat nullish value the same as 0 (`maybeNum` → `maybeNum ?? 0`) | ||
- Explicitly cast value to a boolean (`maybeNum` → `Boolean(maybeNum)`) | ||
- `any` and `unknown` - Provides following suggestions: | ||
- Explicitly cast value to a boolean (`value` → `Boolean(value)`) | ||
## Related To | ||
- TSLint: [strict-boolean-expressions](https://palantir.github.io/tslint/rules/strict-boolean-expressions) | ||
- [no-unnecessary-condition](./no-unnecessary-condition.md) - Similar rule which reports always-truthy and always-falsy values in conditions |
@@ -1,7 +0,20 @@ | ||
# Exhaustiveness checking in switch with union type (`switch-exhaustiveness-check`) | ||
--- | ||
description: 'Require switch-case statements to be exhaustive with union type.' | ||
--- | ||
Union type may have a lot of parts. It's easy to forget to consider all cases in switch. This rule reminds which parts are missing. If domain of the problem requires to have only a partial switch, developer may _explicitly_ add a default clause. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/switch-exhaustiveness-check** for documentation. | ||
Examples of **incorrect** code for this rule: | ||
When working with union types in TypeScript, it's common to want to write a `switch` statement intended to contain a `case` for each constituent (possible type in the union). | ||
However, if the union type changes, it's easy to forget to modify the cases to account for any new types. | ||
This rule reports when a `switch` statement over a value typed as a union of literals is missing a case for any of those literal types and does not have a `default` clause. | ||
## Examples | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -21,10 +34,9 @@ type Day = | ||
switch (day) { | ||
case 'Monday': { | ||
case 'Monday': | ||
result = 1; | ||
break; | ||
} | ||
} | ||
``` | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
@@ -45,34 +57,27 @@ ```ts | ||
switch (day) { | ||
case 'Monday': { | ||
case 'Monday': | ||
result = 1; | ||
break; | ||
} | ||
case 'Tuesday': { | ||
case 'Tuesday': | ||
result = 2; | ||
break; | ||
} | ||
case 'Wednesday': { | ||
case 'Wednesday': | ||
result = 3; | ||
break; | ||
} | ||
case 'Thursday': { | ||
case 'Thursday': | ||
result = 4; | ||
break; | ||
} | ||
case 'Friday': { | ||
case 'Friday': | ||
result = 5; | ||
break; | ||
} | ||
case 'Saturday': { | ||
case 'Saturday': | ||
result = 6; | ||
break; | ||
} | ||
case 'Sunday': { | ||
case 'Sunday': | ||
result = 7; | ||
break; | ||
} | ||
} | ||
``` | ||
or | ||
### ✅ Correct | ||
@@ -93,9 +98,7 @@ ```ts | ||
switch (day) { | ||
case 'Monday': { | ||
case 'Monday': | ||
result = 1; | ||
break; | ||
} | ||
default: { | ||
default: | ||
result = 42; | ||
} | ||
} | ||
@@ -106,2 +109,2 @@ ``` | ||
If program doesn't have union types with many parts. Downside of this rule is the need for type information, so it's slower than regular rules. | ||
If you don't frequently `switch` over union types with many parts, or intentionally wish to leave out some parts. |
@@ -1,9 +0,17 @@ | ||
# Sets preference level for triple slash directives versus ES6-style import declarations (`triple-slash-reference`) | ||
--- | ||
description: 'Disallow certain triple slash directives in favor of ES6-style import declarations.' | ||
--- | ||
Use of triple-slash reference type directives is discouraged in favor of the newer `import` style. This rule allows you to ban use of `/// <reference path="" />`, `/// <reference types="" />`, or `/// <reference lib="" />` directives. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/triple-slash-reference** for documentation. | ||
Consider using this rule in place of [`no-triple-slash-reference`](./no-triple-slash-reference.md) which has been deprecated. | ||
TypeScript's `///` triple-slash references are a way to indicate that types from another module are available in a file. | ||
Use of triple-slash reference type directives is generally discouraged in favor of ECMAScript Module `import`s. | ||
This rule reports on the use of `/// <reference path="..." />`, `/// <reference types="..." />`, or `/// <reference lib="..." />` directives. | ||
## Rule Details | ||
## Examples | ||
## Options | ||
With `{ "path": "never", "types": "never", "lib": "never" }` options set, the following will all be **incorrect** usage: | ||
@@ -54,6 +62,1 @@ | ||
If you want to use all flavors of triple slash reference directives. | ||
## Compatibility | ||
- TSLint: [no-reference](http://palantir.github.io/tslint/rules/no-reference/) | ||
- TSLint: [no-reference-import](https://palantir.github.io/tslint/rules/no-reference-import/) |
@@ -1,3 +0,9 @@ | ||
# Require consistent spacing around type annotations (`type-annotation-spacing`) | ||
--- | ||
description: 'Require consistent spacing around type annotations.' | ||
--- | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/type-annotation-spacing** for documentation. | ||
Spacing around type annotations improves readability of the code. Although the most commonly used style guideline for type annotations in TypeScript prescribes adding a space after the colon, but not before it, it is subjective to the preferences of a project. For example: | ||
@@ -32,3 +38,3 @@ | ||
## Rule Details | ||
## Examples | ||
@@ -39,14 +45,8 @@ This rule aims to enforce specific spacing patterns around type annotations and function types in type literals. | ||
This rule has an object option: | ||
Examples of code for this rule with no options at all: | ||
- `"before": false`, (default for colon) disallows spaces before the colon/arrow. | ||
- `"before": true`, (default for arrow) requires a space before the colon/arrow. | ||
- `"after": true`, (default) requires a space after the colon/arrow. | ||
- `"after": false`, disallows spaces after the colon/arrow. | ||
- `"overrides"`, overrides the default options for type annotations with `colon` (e.g. `const foo: string`) and function types with `arrow` (e.g. `type Foo = () => {}`). Additionally allows granular overrides for `variable` (`const foo: string`),`parameter` (`function foo(bar: string) {...}`),`property` (`interface Foo { bar: string }`) and `returnType` (`function foo(): string {...}`) annotations. | ||
<!--tabs--> | ||
### defaults | ||
### ❌ Incorrect | ||
Examples of **incorrect** code for this rule with no options at all: | ||
<!-- prettier-ignore --> | ||
@@ -79,3 +79,3 @@ ```ts | ||
Examples of **correct** code for this rule with no options at all: | ||
### ✅ Correct | ||
@@ -97,4 +97,8 @@ <!-- prettier-ignore --> | ||
Examples of **incorrect** code for this rule with `{ "before": false, "after": true }`: | ||
Examples of code for this rule with `{ "before": false, "after": true }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
<!-- prettier-ignore --> | ||
@@ -127,3 +131,3 @@ ```ts | ||
Examples of **correct** code for this rule with `{ "before": false, "after": true }`: | ||
#### ✅ Correct | ||
@@ -145,4 +149,8 @@ <!-- prettier-ignore --> | ||
Examples of **incorrect** code for this rule with `{ "before": true, "after": true }` options: | ||
Examples of code for this rule with `{ "before": true, "after": true }` options: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
<!-- prettier-ignore --> | ||
@@ -175,3 +183,3 @@ ```ts | ||
Examples of **correct** code for this rule with `{ "before": true, "after": true }` options: | ||
#### ✅ Correct | ||
@@ -193,4 +201,8 @@ <!-- prettier-ignore --> | ||
Examples of **incorrect** code for this rule with `{ "before": false, "after": false, overrides: { colon: { before: true, after: true }} }` options: | ||
Examples of code for this rule with `{ "before": false, "after": false, overrides: { colon: { before: true, after: true }} }` options: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
<!-- prettier-ignore --> | ||
@@ -223,3 +235,3 @@ ```ts | ||
Examples of **correct** code for this rule with `{ "before": false, "after": false, overrides: { colon: { before: true, after: true }} }` options: | ||
#### ✅ Correct | ||
@@ -245,4 +257,8 @@ <!-- prettier-ignore --> | ||
Examples of **incorrect** code for this rule with `{ "before": false, "after": false, overrides: { arrow: { before: true, after: true }} }` options: | ||
Examples of code for this rule with `{ "before": false, "after": false, overrides: { arrow: { before: true, after: true }} }` options: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
<!-- prettier-ignore --> | ||
@@ -275,3 +291,3 @@ ```ts | ||
Examples of **correct** code for this rule with `{ "before": false, "after": false, overrides: { arrow: { before: true, after: true }} }` options: | ||
#### ✅ Correct | ||
@@ -299,5 +315,1 @@ <!-- prettier-ignore --> | ||
- [Type Inference](https://www.typescriptlang.org/docs/handbook/type-inference.html) | ||
## Compatibility | ||
- TSLint: [`typedef-whitespace`](https://palantir.github.io/tslint/rules/typedef-whitespace/) |
@@ -1,6 +0,15 @@ | ||
# Requires type annotations to exist (`typedef`) | ||
--- | ||
description: 'Require type annotations in certain places.' | ||
--- | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/typedef** for documentation. | ||
TypeScript cannot always infer types for all places in code. | ||
Some locations require type annotations for their types to be inferred. | ||
This rule can enforce type annotations in locations regardless of whether they're required. | ||
This is typically used to maintain consistency for element types that sometimes require them. | ||
```ts | ||
@@ -19,3 +28,7 @@ class ContainsText { | ||
**_Note:_** requiring type annotations unnecessarily can be cumbersome to maintain and generally reduces code readability. | ||
> To enforce type definitions existing on call signatures, use [`explicit-function-return-type`](./explicit-function-return-type.md), or [`explicit-module-boundary-types`](./explicit-module-boundary-types.md). | ||
:::caution | ||
Requiring type annotations unnecessarily can be cumbersome to maintain and generally reduces code readability. | ||
TypeScript is often better at inferring types than easily written type annotations would allow. | ||
@@ -25,35 +38,6 @@ | ||
## Rule Details | ||
::: | ||
This rule can enforce type annotations in locations regardless of whether they're required. | ||
This is typically used to maintain consistency for element types that sometimes require them. | ||
> To enforce type definitions existing on call signatures as per TSLint's `arrow-call-signature` and `call-signature` options, use `explicit-function-return-type`, or `explicit-module-boundary-types`. | ||
## Options | ||
```ts | ||
type Options = { | ||
arrayDestructuring?: boolean; | ||
arrowParameter?: boolean; | ||
memberVariableDeclaration?: boolean; | ||
objectDestructuring?: boolean; | ||
parameter?: boolean; | ||
propertyDeclaration?: boolean; | ||
variableDeclaration?: boolean; | ||
variableDeclarationIgnoreFunction?: boolean; | ||
}; | ||
const defaultOptions: Options = { | ||
arrayDestructuring: false, | ||
arrowParameter: false, | ||
memberVariableDeclaration: false, | ||
objectDestructuring: false, | ||
parameter: false, | ||
propertyDeclaration: false, | ||
variableDeclaration: false, | ||
variableDeclarationIgnoreFunction: false, | ||
}; | ||
``` | ||
For example, with the following configuration: | ||
@@ -82,4 +66,8 @@ | ||
Examples of **incorrect** code with `{ "arrayDestructuring": true }`: | ||
Examples of code with `{ "arrayDestructuring": true }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -90,3 +78,3 @@ const [a] = [1]; | ||
Examples of **correct** code with `{ "arrayDestructuring": true }`: | ||
#### ✅ Correct | ||
@@ -106,4 +94,8 @@ ```ts | ||
Examples of **incorrect** code with `{ "arrowParameter": true }`: | ||
Examples of code with `{ "arrowParameter": true }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -119,6 +111,6 @@ const logsSize = size => console.log(size); | ||
Examples of **correct** code with `{ "arrowParameter": true }`: | ||
#### ✅ Correct | ||
```ts | ||
const logsSize = (size: number) => console.log(text); | ||
const logsSize = (size: number) => console.log(size); | ||
@@ -136,4 +128,8 @@ ['hello', 'world'].map((text: string) => text.length); | ||
Examples of **incorrect** code with `{ "memberVariableDeclaration": true }`: | ||
Examples of code with `{ "memberVariableDeclaration": true }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -146,3 +142,3 @@ class ContainsText { | ||
Examples of **correct** code with `{ "memberVariableDeclaration": true }`: | ||
#### ✅ Correct | ||
@@ -160,4 +156,8 @@ ```ts | ||
Examples of **incorrect** code with `{ "objectDestructuring": true }`: | ||
Examples of code with `{ "objectDestructuring": true }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -168,3 +168,3 @@ const { length } = 'text'; | ||
Examples of **correct** code with `{ "objectDestructuring": true }`: | ||
#### ✅ Correct | ||
@@ -183,4 +183,8 @@ ```ts | ||
Examples of **incorrect** code with `{ "parameter": true }`: | ||
Examples of code with `{ "parameter": true }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -212,3 +216,3 @@ function logsSize(size): void { | ||
Examples of **correct** code with `{ "parameter": true }`: | ||
#### ✅ Correct | ||
@@ -245,4 +249,8 @@ ```ts | ||
Examples of **incorrect** code with `{ "propertyDeclaration": true }`: | ||
Examples of code with `{ "propertyDeclaration": true }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -255,3 +263,3 @@ type Members = { | ||
Examples of **correct** code with `{ "propertyDeclaration": true }`: | ||
#### ✅ Correct | ||
@@ -269,4 +277,8 @@ ```ts | ||
Examples of **incorrect** code with `{ "variableDeclaration": true }`: | ||
Examples of code with `{ "variableDeclaration": true }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -278,3 +290,3 @@ const text = 'text'; | ||
Examples of **correct** code with `{ "variableDeclaration": true }`: | ||
#### ✅ Correct | ||
@@ -291,4 +303,8 @@ ```ts | ||
Examples of **incorrect** code with `{ "variableDeclaration": true, "variableDeclarationIgnoreFunction": true }`: | ||
Examples of code with `{ "variableDeclaration": true, "variableDeclarationIgnoreFunction": true }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts | ||
@@ -298,3 +314,3 @@ const text = 'text'; | ||
Examples of **correct** code with `{ "variableDeclaration": true, "variableDeclarationIgnoreFunction": true }`: | ||
#### ✅ Correct | ||
@@ -323,5 +339,1 @@ ```ts | ||
- [Type Inference](https://www.typescriptlang.org/docs/handbook/type-inference.html) | ||
## Compatibility | ||
- TSLint: [`typedef`](https://palantir.github.io/tslint/rules/typedef) |
@@ -1,11 +0,25 @@ | ||
# Enforces unbound methods are called with their expected scope (`unbound-method`) | ||
--- | ||
description: 'Enforce unbound methods are called with their expected scope.' | ||
--- | ||
Warns when a method is used outside of a method call. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/unbound-method** for documentation. | ||
Class functions don't preserve the class scope when passed as standalone variables. | ||
Class method functions don't preserve the class scope when passed as standalone variables ("unbound"). | ||
If your function does not access `this`, [you can annotate it with `this: void`](https://www.typescriptlang.org/docs/handbook/2/functions.html#declaring-this-in-a-function), or consider using an arrow function instead. | ||
Otherwise, passing class methods around as values can remove type safety by failing to capture `this`. | ||
## Rule Details | ||
This rule reports when a class method is referenced in an unbound manner. | ||
Examples of **incorrect** code for this rule | ||
:::note Tip | ||
If you're working with `jest`, you can use [`eslint-plugin-jest`'s version of this rule](https://github.com/jest-community/eslint-plugin-jest/blob/main/docs/rules/unbound-method.md) to lint your test files, which knows when it's ok to pass an unbound method to `expect` calls. | ||
::: | ||
## Examples | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
@@ -36,3 +50,3 @@ class MyClass { | ||
Examples of **correct** code for this rule | ||
### ✅ Correct | ||
@@ -69,6 +83,2 @@ ```ts | ||
The rule accepts an options object with the following property: | ||
- `ignoreStatic` to not check whether `static` methods are correctly bound | ||
### `ignoreStatic` | ||
@@ -91,15 +101,2 @@ | ||
### Example | ||
```json | ||
{ | ||
"@typescript-eslint/unbound-method": [ | ||
"error", | ||
{ | ||
"ignoreStatic": true | ||
} | ||
] | ||
} | ||
``` | ||
## When Not To Use It | ||
@@ -109,4 +106,2 @@ | ||
## Related To | ||
- TSLint: [no-unbound-method](https://palantir.github.io/tslint/rules/no-unbound-method/) | ||
If you're wanting to use `toBeCalled` and similar matches in `jest` tests, you can disable this rule for your test files in favor of [`eslint-plugin-jest`'s version of this rule](https://github.com/jest-community/eslint-plugin-jest/blob/main/docs/rules/unbound-method.md). |
@@ -1,33 +0,70 @@ | ||
# Warns for any two overloads that could be unified into one by using a union or an optional/rest parameter (`unified-signatures`) | ||
--- | ||
description: 'Disallow two overloads that could be unified into one with a union or an optional/rest parameter.' | ||
--- | ||
Warns for any two overloads that could be unified into one by using a union or an optional/rest parameter. | ||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/unified-signatures** for documentation. | ||
## Rule Details | ||
Function overload signatures are a TypeScript way to define a function that can be called in multiple very different ways. | ||
Overload signatures add syntax and theoretical bloat, so it's generally best to avoid using them when possible. | ||
Switching to union types and/or optional or rest parameters can often avoid the need for overload signatures. | ||
This rule aims to keep the source code as maintainable as possible by reducing the amount of overloads. | ||
This rule reports when function overload signatures can be replaced by a single function signature. | ||
Examples of **incorrect** code for this rule: | ||
## Examples | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
function f(x: number): void; | ||
function f(x: string): void; | ||
function x(x: number): void; | ||
function x(x: string): void; | ||
``` | ||
```ts | ||
f(): void; | ||
f(...x: number[]): void; | ||
function y(): void; | ||
function y(...x: number[]): void; | ||
``` | ||
Examples of **correct** code for this rule: | ||
### ✅ Correct | ||
```ts | ||
function f(x: number | string): void; | ||
function x(x: number | string): void; | ||
``` | ||
```ts | ||
function f(x?: ...number[]): void; | ||
function y(...x: number[]): void; | ||
``` | ||
## Related to | ||
```ts | ||
// This rule won't check overload signatures with different rest parameter types. | ||
// See https://github.com/microsoft/TypeScript/issues/5077 | ||
function f(...a: number[]): void; | ||
function f(...a: string[]): void; | ||
``` | ||
- TSLint: [`unified-signatures`](https://palantir.github.io/tslint/rules/unified-signatures/) | ||
## Options | ||
### `ignoreDifferentlyNamedParameters` | ||
Examples of code for this rule with `ignoreDifferentlyNamedParameters`: | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
function f(a: number): void; | ||
function f(a: string): void; | ||
``` | ||
### ✅ Correct | ||
```ts | ||
function f(a: number): void; | ||
function f(b: string): void; | ||
``` | ||
## Options |
{ | ||
"name": "@typescript-eslint/eslint-plugin", | ||
"version": "4.15.1", | ||
"version": "5.62.0", | ||
"description": "TypeScript plugin for ESLint", | ||
@@ -12,3 +12,3 @@ "keywords": [ | ||
"engines": { | ||
"node": "^10.12.0 || >=12.0.0" | ||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0" | ||
}, | ||
@@ -18,2 +18,3 @@ "files": [ | ||
"docs", | ||
"index.d.ts", | ||
"package.json", | ||
@@ -33,12 +34,13 @@ "README.md", | ||
"main": "dist/index.js", | ||
"types": "index.d.ts", | ||
"scripts": { | ||
"build": "tsc -b tsconfig.build.json", | ||
"check:docs": "jest tests/docs.test.ts --runTestsByPath --silent --runInBand", | ||
"check:configs": "jest tests/configs.test.ts --runTestsByPath --silent --runInBand", | ||
"check-docs": "jest tests/docs.test.ts --runTestsByPath --silent --runInBand", | ||
"check-configs": "jest tests/configs.test.ts --runTestsByPath --silent --runInBand", | ||
"clean": "tsc -b tsconfig.build.json --clean", | ||
"postclean": "rimraf dist", | ||
"format": "prettier --write \"./**/*.{ts,js,json,md}\" --ignore-path ../../.prettierignore", | ||
"generate:configs": "../../node_modules/.bin/ts-node --files --transpile-only tools/generate-configs.ts", | ||
"generate:rules-lists": "../../node_modules/.bin/ts-node --files --transpile-only tools/generate-rules-lists.ts", | ||
"lint": "eslint . --ext .js,.ts --ignore-path ../../.eslintignore", | ||
"postclean": "rimraf dist && rimraf coverage", | ||
"format": "prettier --write \"./**/*.{ts,mts,cts,tsx,js,mjs,cjs,jsx,json,md,css}\" --ignore-path ../../.prettierignore", | ||
"generate:breaking-changes": "yarn tsx tools/generate-breaking-changes.ts", | ||
"generate:configs": "yarn tsx tools/generate-configs.ts", | ||
"lint": "nx lint", | ||
"test": "jest --coverage", | ||
@@ -48,23 +50,31 @@ "typecheck": "tsc -p tsconfig.json --noEmit" | ||
"dependencies": { | ||
"@typescript-eslint/experimental-utils": "4.15.1", | ||
"@typescript-eslint/scope-manager": "4.15.1", | ||
"debug": "^4.1.1", | ||
"functional-red-black-tree": "^1.0.1", | ||
"lodash": "^4.17.15", | ||
"regexpp": "^3.0.0", | ||
"semver": "^7.3.2", | ||
"tsutils": "^3.17.1" | ||
"@eslint-community/regexpp": "^4.4.0", | ||
"@typescript-eslint/scope-manager": "5.62.0", | ||
"@typescript-eslint/type-utils": "5.62.0", | ||
"@typescript-eslint/utils": "5.62.0", | ||
"debug": "^4.3.4", | ||
"graphemer": "^1.4.0", | ||
"ignore": "^5.2.0", | ||
"natural-compare-lite": "^1.4.0", | ||
"semver": "^7.3.7", | ||
"tsutils": "^3.21.0" | ||
}, | ||
"devDependencies": { | ||
"@types/debug": "*", | ||
"@types/json-schema": "*", | ||
"@types/marked": "*", | ||
"@types/natural-compare-lite": "^1.4.0", | ||
"@types/prettier": "*", | ||
"chalk": "^4.0.0", | ||
"marked": "^2.0.0", | ||
"chalk": "^5.0.1", | ||
"cross-fetch": "^3.1.5", | ||
"json-schema": "*", | ||
"markdown-table": "^3.0.2", | ||
"marked": "^4.0.15", | ||
"prettier": "*", | ||
"title-case": "^3.0.3", | ||
"typescript": "*" | ||
}, | ||
"peerDependencies": { | ||
"@typescript-eslint/parser": "^4.0.0", | ||
"eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" | ||
"@typescript-eslint/parser": "^5.0.0", | ||
"eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" | ||
}, | ||
@@ -80,3 +90,3 @@ "peerDependenciesMeta": { | ||
}, | ||
"gitHead": "f9980c25fa383b4c6ec79f1f7635102103ee27e2" | ||
"gitHead": "cba0d113bba1bbcee69149c954dc6bd4c658c714" | ||
} |
235
README.md
@@ -1,233 +0,10 @@ | ||
<h1 align="center">ESLint Plugin TypeScript</h1> | ||
# `@typescript-eslint/eslint-plugin` | ||
<p align="center">An ESLint plugin which provides lint rules for TypeScript codebases.</p> | ||
An ESLint plugin which provides lint rules for TypeScript codebases. | ||
<p align="center"> | ||
<img src="https://github.com/typescript-eslint/typescript-eslint/workflows/CI/badge.svg" alt="CI" /> | ||
<a href="https://www.npmjs.com/package/@typescript-eslint/eslint-plugin"><img src="https://img.shields.io/npm/v/@typescript-eslint/eslint-plugin.svg?style=flat-square" alt="NPM Version" /></a> | ||
<a href="https://www.npmjs.com/package/@typescript-eslint/eslint-plugin"><img src="https://img.shields.io/npm/dm/@typescript-eslint/eslint-plugin.svg?style=flat-square" alt="NPM Downloads" /></a> | ||
</p> | ||
[![NPM Version](https://img.shields.io/npm/v/@typescript-eslint/eslint-plugin.svg?style=flat-square)](https://www.npmjs.com/package/@typescript-eslint/eslint-plugin) | ||
[![NPM Downloads](https://img.shields.io/npm/dm/@typescript-eslint/eslint-plugin.svg?style=flat-square)](https://www.npmjs.com/package/@typescript-eslint/eslint-plugin) | ||
## Getting Started | ||
👉 See **https://typescript-eslint.io/getting-started** for our Getting Started docs. | ||
- **[You can find our Getting Started docs here](../../docs/getting-started/linting/README.md)** | ||
- **[You can find our FAQ / Troubleshooting docs here](../../docs/getting-started/linting/FAQ.md)** | ||
These docs walk you through setting up ESLint, this plugin, and our parser. If you know what you're doing and just want to quick start, read on... | ||
## Quick-start | ||
### Installation | ||
Make sure you have TypeScript and [`@typescript-eslint/parser`](../parser) installed: | ||
```bash | ||
$ yarn add -D typescript @typescript-eslint/parser | ||
$ npm i --save-dev typescript @typescript-eslint/parser | ||
``` | ||
Then install the plugin: | ||
```bash | ||
$ yarn add -D @typescript-eslint/eslint-plugin | ||
$ npm i --save-dev @typescript-eslint/eslint-plugin | ||
``` | ||
It is important that you use the same version number for `@typescript-eslint/parser` and `@typescript-eslint/eslint-plugin`. | ||
**Note:** If you installed ESLint globally (using the `-g` flag) then you must also install `@typescript-eslint/eslint-plugin` globally. | ||
### Usage | ||
Add `@typescript-eslint/parser` to the `parser` field and `@typescript-eslint` to the plugins section of your `.eslintrc` configuration file, then configure the rules you want to use under the rules section. | ||
```json | ||
{ | ||
"parser": "@typescript-eslint/parser", | ||
"plugins": ["@typescript-eslint"], | ||
"rules": { | ||
"@typescript-eslint/rule-name": "error" | ||
} | ||
} | ||
``` | ||
You can also enable all the recommended rules for our plugin. Add `plugin:@typescript-eslint/recommended` in extends: | ||
```json | ||
{ | ||
"extends": ["plugin:@typescript-eslint/recommended"] | ||
} | ||
``` | ||
**Note: Make sure to use `eslint --ext .js,.ts` since by [default](https://eslint.org/docs/user-guide/command-line-interface#--ext) `eslint` will only search for `.js` files.** | ||
### Recommended Configs | ||
You can also use [`eslint:recommended`](https://eslint.org/docs/rules/) (the set of rules which are recommended for all projects by the ESLint Team) with this plugin: | ||
```json | ||
{ | ||
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"] | ||
} | ||
``` | ||
As of version 2 of this plugin, _by design_, none of the rules in the main `recommended` config require type-checking in order to run. This means that they are more lightweight and faster to run. | ||
Some highly valuable rules simply require type-checking in order to be implemented correctly, however, so we provide an additional config you can extend from called `recommended-requiring-type-checking`. You would apply this _in addition_ to the recommended configs previously mentioned, e.g.: | ||
```json | ||
{ | ||
"extends": [ | ||
"eslint:recommended", | ||
"plugin:@typescript-eslint/recommended", | ||
"plugin:@typescript-eslint/recommended-requiring-type-checking" | ||
] | ||
} | ||
``` | ||
Pro Tip: For larger codebases you may want to consider splitting our linting into two separate stages: 1. fast feedback rules which operate purely based on syntax (no type-checking), 2. rules which are based on semantics (type-checking). | ||
**[You can read more about linting with type information here](../../docs/getting-started/linting/TYPED_LINTING.md)** | ||
## Supported Rules | ||
<!-- begin base rule list --> | ||
**Key**: :heavy_check_mark: = recommended, :wrench: = fixable, :thought_balloon: = requires type information | ||
| Name | Description | :heavy_check_mark: | :wrench: | :thought_balloon: | | ||
| --------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------ | -------- | ----------------- | | ||
| [`@typescript-eslint/adjacent-overload-signatures`](./docs/rules/adjacent-overload-signatures.md) | Require that member overloads be consecutive | :heavy_check_mark: | | | | ||
| [`@typescript-eslint/array-type`](./docs/rules/array-type.md) | Requires using either `T[]` or `Array<T>` for arrays | | :wrench: | | | ||
| [`@typescript-eslint/await-thenable`](./docs/rules/await-thenable.md) | Disallows awaiting a value that is not a Thenable | :heavy_check_mark: | | :thought_balloon: | | ||
| [`@typescript-eslint/ban-ts-comment`](./docs/rules/ban-ts-comment.md) | Bans `@ts-<directive>` comments from being used or requires descriptions after directive | :heavy_check_mark: | | | | ||
| [`@typescript-eslint/ban-tslint-comment`](./docs/rules/ban-tslint-comment.md) | Bans `// tslint:<rule-flag>` comments from being used | | :wrench: | | | ||
| [`@typescript-eslint/ban-types`](./docs/rules/ban-types.md) | Bans specific types from being used | :heavy_check_mark: | :wrench: | | | ||
| [`@typescript-eslint/class-literal-property-style`](./docs/rules/class-literal-property-style.md) | Ensures that literals on classes are exposed in a consistent style | | :wrench: | | | ||
| [`@typescript-eslint/consistent-indexed-object-style`](./docs/rules/consistent-indexed-object-style.md) | Enforce or disallow the use of the record type | | :wrench: | | | ||
| [`@typescript-eslint/consistent-type-assertions`](./docs/rules/consistent-type-assertions.md) | Enforces consistent usage of type assertions | | | | | ||
| [`@typescript-eslint/consistent-type-definitions`](./docs/rules/consistent-type-definitions.md) | Consistent with type definition either `interface` or `type` | | :wrench: | | | ||
| [`@typescript-eslint/consistent-type-imports`](./docs/rules/consistent-type-imports.md) | Enforces consistent usage of type imports | | :wrench: | | | ||
| [`@typescript-eslint/explicit-function-return-type`](./docs/rules/explicit-function-return-type.md) | Require explicit return types on functions and class methods | | | | | ||
| [`@typescript-eslint/explicit-member-accessibility`](./docs/rules/explicit-member-accessibility.md) | Require explicit accessibility modifiers on class properties and methods | | :wrench: | | | ||
| [`@typescript-eslint/explicit-module-boundary-types`](./docs/rules/explicit-module-boundary-types.md) | Require explicit return and argument types on exported functions' and classes' public class methods | :heavy_check_mark: | | | | ||
| [`@typescript-eslint/member-delimiter-style`](./docs/rules/member-delimiter-style.md) | Require a specific member delimiter style for interfaces and type literals | | :wrench: | | | ||
| [`@typescript-eslint/member-ordering`](./docs/rules/member-ordering.md) | Require a consistent member declaration order | | | | | ||
| [`@typescript-eslint/method-signature-style`](./docs/rules/method-signature-style.md) | Enforces using a particular method signature syntax. | | :wrench: | | | ||
| [`@typescript-eslint/naming-convention`](./docs/rules/naming-convention.md) | Enforces naming conventions for everything across a codebase | | | :thought_balloon: | | ||
| [`@typescript-eslint/no-base-to-string`](./docs/rules/no-base-to-string.md) | Requires that `.toString()` is only called on objects which provide useful information when stringified | | | :thought_balloon: | | ||
| [`@typescript-eslint/no-confusing-non-null-assertion`](./docs/rules/no-confusing-non-null-assertion.md) | Disallow non-null assertion in locations that may be confusing | | :wrench: | | | ||
| [`@typescript-eslint/no-confusing-void-expression`](./docs/rules/no-confusing-void-expression.md) | Requires expressions of type void to appear in statement position | | :wrench: | :thought_balloon: | | ||
| [`@typescript-eslint/no-dynamic-delete`](./docs/rules/no-dynamic-delete.md) | Disallow the delete operator with computed key expressions | | :wrench: | | | ||
| [`@typescript-eslint/no-empty-interface`](./docs/rules/no-empty-interface.md) | Disallow the declaration of empty interfaces | :heavy_check_mark: | :wrench: | | | ||
| [`@typescript-eslint/no-explicit-any`](./docs/rules/no-explicit-any.md) | Disallow usage of the `any` type | :heavy_check_mark: | :wrench: | | | ||
| [`@typescript-eslint/no-extra-non-null-assertion`](./docs/rules/no-extra-non-null-assertion.md) | Disallow extra non-null assertion | :heavy_check_mark: | :wrench: | | | ||
| [`@typescript-eslint/no-extraneous-class`](./docs/rules/no-extraneous-class.md) | Forbids the use of classes as namespaces | | | | | ||
| [`@typescript-eslint/no-floating-promises`](./docs/rules/no-floating-promises.md) | Requires Promise-like values to be handled appropriately | :heavy_check_mark: | | :thought_balloon: | | ||
| [`@typescript-eslint/no-for-in-array`](./docs/rules/no-for-in-array.md) | Disallow iterating over an array with a for-in loop | :heavy_check_mark: | | :thought_balloon: | | ||
| [`@typescript-eslint/no-implicit-any-catch`](./docs/rules/no-implicit-any-catch.md) | Disallow usage of the implicit `any` type in catch clauses | | :wrench: | | | ||
| [`@typescript-eslint/no-inferrable-types`](./docs/rules/no-inferrable-types.md) | Disallows explicit type declarations for variables or parameters initialized to a number, string, or boolean | :heavy_check_mark: | :wrench: | | | ||
| [`@typescript-eslint/no-invalid-void-type`](./docs/rules/no-invalid-void-type.md) | Disallows usage of `void` type outside of generic or return types | | | | | ||
| [`@typescript-eslint/no-misused-new`](./docs/rules/no-misused-new.md) | Enforce valid definition of `new` and `constructor` | :heavy_check_mark: | | | | ||
| [`@typescript-eslint/no-misused-promises`](./docs/rules/no-misused-promises.md) | Avoid using promises in places not designed to handle them | :heavy_check_mark: | | :thought_balloon: | | ||
| [`@typescript-eslint/no-namespace`](./docs/rules/no-namespace.md) | Disallow the use of custom TypeScript modules and namespaces | :heavy_check_mark: | | | | ||
| [`@typescript-eslint/no-non-null-asserted-optional-chain`](./docs/rules/no-non-null-asserted-optional-chain.md) | Disallows using a non-null assertion after an optional chain expression | :heavy_check_mark: | | | | ||
| [`@typescript-eslint/no-non-null-assertion`](./docs/rules/no-non-null-assertion.md) | Disallows non-null assertions using the `!` postfix operator | :heavy_check_mark: | | | | ||
| [`@typescript-eslint/no-parameter-properties`](./docs/rules/no-parameter-properties.md) | Disallow the use of parameter properties in class constructors | | | | | ||
| [`@typescript-eslint/no-require-imports`](./docs/rules/no-require-imports.md) | Disallows invocation of `require()` | | | | | ||
| [`@typescript-eslint/no-this-alias`](./docs/rules/no-this-alias.md) | Disallow aliasing `this` | :heavy_check_mark: | | | | ||
| [`@typescript-eslint/no-type-alias`](./docs/rules/no-type-alias.md) | Disallow the use of type aliases | | | | | ||
| [`@typescript-eslint/no-unnecessary-boolean-literal-compare`](./docs/rules/no-unnecessary-boolean-literal-compare.md) | Flags unnecessary equality comparisons against boolean literals | | :wrench: | :thought_balloon: | | ||
| [`@typescript-eslint/no-unnecessary-condition`](./docs/rules/no-unnecessary-condition.md) | Prevents conditionals where the type is always truthy or always falsy | | :wrench: | :thought_balloon: | | ||
| [`@typescript-eslint/no-unnecessary-qualifier`](./docs/rules/no-unnecessary-qualifier.md) | Warns when a namespace qualifier is unnecessary | | :wrench: | :thought_balloon: | | ||
| [`@typescript-eslint/no-unnecessary-type-arguments`](./docs/rules/no-unnecessary-type-arguments.md) | Enforces that type arguments will not be used if not required | | :wrench: | :thought_balloon: | | ||
| [`@typescript-eslint/no-unnecessary-type-assertion`](./docs/rules/no-unnecessary-type-assertion.md) | Warns if a type assertion does not change the type of an expression | :heavy_check_mark: | :wrench: | :thought_balloon: | | ||
| [`@typescript-eslint/no-unnecessary-type-constraint`](./docs/rules/no-unnecessary-type-constraint.md) | Disallows unnecessary constraints on generic types | | :wrench: | | | ||
| [`@typescript-eslint/no-unsafe-assignment`](./docs/rules/no-unsafe-assignment.md) | Disallows assigning any to variables and properties | :heavy_check_mark: | | :thought_balloon: | | ||
| [`@typescript-eslint/no-unsafe-call`](./docs/rules/no-unsafe-call.md) | Disallows calling an any type value | :heavy_check_mark: | | :thought_balloon: | | ||
| [`@typescript-eslint/no-unsafe-member-access`](./docs/rules/no-unsafe-member-access.md) | Disallows member access on any typed variables | :heavy_check_mark: | | :thought_balloon: | | ||
| [`@typescript-eslint/no-unsafe-return`](./docs/rules/no-unsafe-return.md) | Disallows returning any from a function | :heavy_check_mark: | | :thought_balloon: | | ||
| [`@typescript-eslint/no-var-requires`](./docs/rules/no-var-requires.md) | Disallows the use of require statements except in import statements | :heavy_check_mark: | | | | ||
| [`@typescript-eslint/non-nullable-type-assertion-style`](./docs/rules/non-nullable-type-assertion-style.md) | Prefers a non-null assertion over explicit type cast when possible | | :wrench: | :thought_balloon: | | ||
| [`@typescript-eslint/prefer-as-const`](./docs/rules/prefer-as-const.md) | Prefer usage of `as const` over literal type | :heavy_check_mark: | :wrench: | | | ||
| [`@typescript-eslint/prefer-enum-initializers`](./docs/rules/prefer-enum-initializers.md) | Prefer initializing each enums member value | | | | | ||
| [`@typescript-eslint/prefer-for-of`](./docs/rules/prefer-for-of.md) | Prefer a ‘for-of’ loop over a standard ‘for’ loop if the index is only used to access the array being iterated | | | | | ||
| [`@typescript-eslint/prefer-function-type`](./docs/rules/prefer-function-type.md) | Use function types instead of interfaces with call signatures | | :wrench: | | | ||
| [`@typescript-eslint/prefer-includes`](./docs/rules/prefer-includes.md) | Enforce `includes` method over `indexOf` method | | :wrench: | :thought_balloon: | | ||
| [`@typescript-eslint/prefer-literal-enum-member`](./docs/rules/prefer-literal-enum-member.md) | Require that all enum members be literal values to prevent unintended enum member name shadow issues | | | | | ||
| [`@typescript-eslint/prefer-namespace-keyword`](./docs/rules/prefer-namespace-keyword.md) | Require the use of the `namespace` keyword instead of the `module` keyword to declare custom TypeScript modules | :heavy_check_mark: | :wrench: | | | ||
| [`@typescript-eslint/prefer-nullish-coalescing`](./docs/rules/prefer-nullish-coalescing.md) | Enforce the usage of the nullish coalescing operator instead of logical chaining | | | :thought_balloon: | | ||
| [`@typescript-eslint/prefer-optional-chain`](./docs/rules/prefer-optional-chain.md) | Prefer using concise optional chain expressions instead of chained logical ands | | | | | ||
| [`@typescript-eslint/prefer-readonly`](./docs/rules/prefer-readonly.md) | Requires that private members are marked as `readonly` if they're never modified outside of the constructor | | :wrench: | :thought_balloon: | | ||
| [`@typescript-eslint/prefer-readonly-parameter-types`](./docs/rules/prefer-readonly-parameter-types.md) | Requires that function parameters are typed as readonly to prevent accidental mutation of inputs | | | :thought_balloon: | | ||
| [`@typescript-eslint/prefer-reduce-type-parameter`](./docs/rules/prefer-reduce-type-parameter.md) | Prefer using type parameter when calling `Array#reduce` instead of casting | | :wrench: | :thought_balloon: | | ||
| [`@typescript-eslint/prefer-regexp-exec`](./docs/rules/prefer-regexp-exec.md) | Enforce that `RegExp#exec` is used instead of `String#match` if no global flag is provided | :heavy_check_mark: | | :thought_balloon: | | ||
| [`@typescript-eslint/prefer-string-starts-ends-with`](./docs/rules/prefer-string-starts-ends-with.md) | Enforce the use of `String#startsWith` and `String#endsWith` instead of other equivalent methods of checking substrings | | :wrench: | :thought_balloon: | | ||
| [`@typescript-eslint/prefer-ts-expect-error`](./docs/rules/prefer-ts-expect-error.md) | Recommends using `@ts-expect-error` over `@ts-ignore` | | :wrench: | | | ||
| [`@typescript-eslint/promise-function-async`](./docs/rules/promise-function-async.md) | Requires any function or method that returns a Promise to be marked async | | :wrench: | :thought_balloon: | | ||
| [`@typescript-eslint/require-array-sort-compare`](./docs/rules/require-array-sort-compare.md) | Requires `Array#sort` calls to always provide a `compareFunction` | | | :thought_balloon: | | ||
| [`@typescript-eslint/restrict-plus-operands`](./docs/rules/restrict-plus-operands.md) | When adding two variables, operands must both be of type number or of type string | :heavy_check_mark: | | :thought_balloon: | | ||
| [`@typescript-eslint/restrict-template-expressions`](./docs/rules/restrict-template-expressions.md) | Enforce template literal expressions to be of string type | :heavy_check_mark: | | :thought_balloon: | | ||
| [`@typescript-eslint/sort-type-union-intersection-members`](./docs/rules/sort-type-union-intersection-members.md) | Enforces that members of a type union/intersection are sorted alphabetically | | :wrench: | | | ||
| [`@typescript-eslint/strict-boolean-expressions`](./docs/rules/strict-boolean-expressions.md) | Restricts the types allowed in boolean expressions | | | :thought_balloon: | | ||
| [`@typescript-eslint/switch-exhaustiveness-check`](./docs/rules/switch-exhaustiveness-check.md) | Exhaustiveness checking in switch with union type | | | :thought_balloon: | | ||
| [`@typescript-eslint/triple-slash-reference`](./docs/rules/triple-slash-reference.md) | Sets preference level for triple slash directives versus ES6-style import declarations | :heavy_check_mark: | | | | ||
| [`@typescript-eslint/type-annotation-spacing`](./docs/rules/type-annotation-spacing.md) | Require consistent spacing around type annotations | | :wrench: | | | ||
| [`@typescript-eslint/typedef`](./docs/rules/typedef.md) | Requires type annotations to exist | | | | | ||
| [`@typescript-eslint/unbound-method`](./docs/rules/unbound-method.md) | Enforces unbound methods are called with their expected scope | :heavy_check_mark: | | :thought_balloon: | | ||
| [`@typescript-eslint/unified-signatures`](./docs/rules/unified-signatures.md) | Warns for any two overloads that could be unified into one by using a union or an optional/rest parameter | | | | | ||
<!-- end base rule list --> | ||
### Extension Rules | ||
In some cases, ESLint provides a rule itself, but it doesn't support TypeScript syntax; either it crashes, or it ignores the syntax, or it falsely reports against it. | ||
In these cases, we create what we call an extension rule; a rule within our plugin that has the same functionality, but also supports TypeScript. | ||
<!-- begin extension rule list --> | ||
**Key**: :heavy_check_mark: = recommended, :wrench: = fixable, :thought_balloon: = requires type information | ||
| Name | Description | :heavy_check_mark: | :wrench: | :thought_balloon: | | ||
| ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | ------------------ | -------- | ----------------- | | ||
| [`@typescript-eslint/brace-style`](./docs/rules/brace-style.md) | Enforce consistent brace style for blocks | | :wrench: | | | ||
| [`@typescript-eslint/comma-dangle`](./docs/rules/comma-dangle.md) | Require or disallow trailing comma | | :wrench: | | | ||
| [`@typescript-eslint/comma-spacing`](./docs/rules/comma-spacing.md) | Enforces consistent spacing before and after commas | | :wrench: | | | ||
| [`@typescript-eslint/default-param-last`](./docs/rules/default-param-last.md) | Enforce default parameters to be last | | | | | ||
| [`@typescript-eslint/dot-notation`](./docs/rules/dot-notation.md) | enforce dot notation whenever possible | | :wrench: | :thought_balloon: | | ||
| [`@typescript-eslint/func-call-spacing`](./docs/rules/func-call-spacing.md) | Require or disallow spacing between function identifiers and their invocations | | :wrench: | | | ||
| [`@typescript-eslint/indent`](./docs/rules/indent.md) | Enforce consistent indentation | | :wrench: | | | ||
| [`@typescript-eslint/init-declarations`](./docs/rules/init-declarations.md) | require or disallow initialization in variable declarations | | | | | ||
| [`@typescript-eslint/keyword-spacing`](./docs/rules/keyword-spacing.md) | Enforce consistent spacing before and after keywords | | :wrench: | | | ||
| [`@typescript-eslint/lines-between-class-members`](./docs/rules/lines-between-class-members.md) | Require or disallow an empty line between class members | | :wrench: | | | ||
| [`@typescript-eslint/no-array-constructor`](./docs/rules/no-array-constructor.md) | Disallow generic `Array` constructors | :heavy_check_mark: | :wrench: | | | ||
| [`@typescript-eslint/no-dupe-class-members`](./docs/rules/no-dupe-class-members.md) | Disallow duplicate class members | | | | | ||
| [`@typescript-eslint/no-duplicate-imports`](./docs/rules/no-duplicate-imports.md) | Disallow duplicate imports | | | | | ||
| [`@typescript-eslint/no-empty-function`](./docs/rules/no-empty-function.md) | Disallow empty functions | :heavy_check_mark: | | | | ||
| [`@typescript-eslint/no-extra-parens`](./docs/rules/no-extra-parens.md) | Disallow unnecessary parentheses | | :wrench: | | | ||
| [`@typescript-eslint/no-extra-semi`](./docs/rules/no-extra-semi.md) | Disallow unnecessary semicolons | :heavy_check_mark: | :wrench: | | | ||
| [`@typescript-eslint/no-implied-eval`](./docs/rules/no-implied-eval.md) | Disallow the use of `eval()`-like methods | :heavy_check_mark: | | :thought_balloon: | | ||
| [`@typescript-eslint/no-invalid-this`](./docs/rules/no-invalid-this.md) | Disallow `this` keywords outside of classes or class-like objects | | | | | ||
| [`@typescript-eslint/no-loop-func`](./docs/rules/no-loop-func.md) | Disallow function declarations that contain unsafe references inside loop statements | | | | | ||
| [`@typescript-eslint/no-loss-of-precision`](./docs/rules/no-loss-of-precision.md) | Disallow literal numbers that lose precision | | | | | ||
| [`@typescript-eslint/no-magic-numbers`](./docs/rules/no-magic-numbers.md) | Disallow magic numbers | | | | | ||
| [`@typescript-eslint/no-redeclare`](./docs/rules/no-redeclare.md) | Disallow variable redeclaration | | | | | ||
| [`@typescript-eslint/no-shadow`](./docs/rules/no-shadow.md) | Disallow variable declarations from shadowing variables declared in the outer scope | | | | | ||
| [`@typescript-eslint/no-throw-literal`](./docs/rules/no-throw-literal.md) | Disallow throwing literals as exceptions | | | :thought_balloon: | | ||
| [`@typescript-eslint/no-unused-expressions`](./docs/rules/no-unused-expressions.md) | Disallow unused expressions | | | | | ||
| [`@typescript-eslint/no-unused-vars`](./docs/rules/no-unused-vars.md) | Disallow unused variables | :heavy_check_mark: | | | | ||
| [`@typescript-eslint/no-use-before-define`](./docs/rules/no-use-before-define.md) | Disallow the use of variables before they are defined | | | | | ||
| [`@typescript-eslint/no-useless-constructor`](./docs/rules/no-useless-constructor.md) | Disallow unnecessary constructors | | | | | ||
| [`@typescript-eslint/object-curly-spacing`](./docs/rules/object-curly-spacing.md) | Enforce consistent spacing inside braces | | :wrench: | | | ||
| [`@typescript-eslint/quotes`](./docs/rules/quotes.md) | Enforce the consistent use of either backticks, double, or single quotes | | :wrench: | | | ||
| [`@typescript-eslint/require-await`](./docs/rules/require-await.md) | Disallow async functions which have no `await` expression | :heavy_check_mark: | | :thought_balloon: | | ||
| [`@typescript-eslint/return-await`](./docs/rules/return-await.md) | Enforces consistent returning of awaited values | | :wrench: | :thought_balloon: | | ||
| [`@typescript-eslint/semi`](./docs/rules/semi.md) | Require or disallow semicolons instead of ASI | | :wrench: | | | ||
| [`@typescript-eslint/space-before-function-paren`](./docs/rules/space-before-function-paren.md) | Enforces consistent spacing before function parenthesis | | :wrench: | | | ||
| [`@typescript-eslint/space-infix-ops`](./docs/rules/space-infix-ops.md) | This rule is aimed at ensuring there are spaces around infix operators. | | :wrench: | | | ||
<!-- end extension rule list --> | ||
## Contributing | ||
[See the contributing guide here](../../CONTRIBUTING.md). | ||
> See https://typescript-eslint.io for general documentation on typescript-eslint, the tooling that allows you to run ESLint and Prettier on TypeScript code. |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2429360
483
30156
12
13
11
1
+ Addedgraphemer@^1.4.0
+ Addedignore@^5.2.0
+ Addednatural-compare-lite@^1.4.0
+ Added@eslint-community/eslint-utils@4.4.0(transitive)
+ Added@eslint-community/regexpp@4.11.0(transitive)
+ Added@eslint/eslintrc@2.1.4(transitive)
+ Added@eslint/js@8.57.0(transitive)
+ Added@humanwhocodes/config-array@0.11.14(transitive)
+ Added@humanwhocodes/module-importer@1.0.1(transitive)
+ Added@humanwhocodes/object-schema@2.0.3(transitive)
+ Added@types/semver@7.5.8(transitive)
+ Added@typescript-eslint/parser@5.62.0(transitive)
+ Added@typescript-eslint/scope-manager@5.62.0(transitive)
+ Added@typescript-eslint/type-utils@5.62.0(transitive)
+ Added@typescript-eslint/types@5.62.0(transitive)
+ Added@typescript-eslint/typescript-estree@5.62.0(transitive)
+ Added@typescript-eslint/utils@5.62.0(transitive)
+ Added@typescript-eslint/visitor-keys@5.62.0(transitive)
+ Added@ungap/structured-clone@1.2.0(transitive)
+ Addedacorn@8.12.1(transitive)
+ Addedargparse@2.0.1(transitive)
+ Addedeslint@8.57.0(transitive)
+ Addedeslint-scope@7.2.2(transitive)
+ Addedeslint-visitor-keys@3.4.3(transitive)
+ Addedespree@9.6.1(transitive)
+ Addedfind-up@5.0.0(transitive)
+ Addedglob-parent@6.0.2(transitive)
+ Addedgraphemer@1.4.0(transitive)
+ Addedis-path-inside@3.0.3(transitive)
+ Addedjs-yaml@4.1.0(transitive)
+ Addedlocate-path@6.0.0(transitive)
+ Addednatural-compare-lite@1.4.0(transitive)
+ Addedp-limit@3.1.0(transitive)
+ Addedp-locate@5.0.0(transitive)
+ Addedpath-exists@4.0.0(transitive)
+ Addedyocto-queue@0.1.0(transitive)
- Removedfunctional-red-black-tree@^1.0.1
- Removedlodash@^4.17.15
- Removedregexpp@^3.0.0
- Removed@babel/code-frame@7.12.11(transitive)
- Removed@babel/helper-validator-identifier@7.24.7(transitive)
- Removed@babel/highlight@7.24.7(transitive)
- Removed@eslint/eslintrc@0.4.3(transitive)
- Removed@humanwhocodes/config-array@0.5.0(transitive)
- Removed@humanwhocodes/object-schema@1.2.1(transitive)
- Removed@typescript-eslint/experimental-utils@4.15.1(transitive)
- Removed@typescript-eslint/parser@4.33.0(transitive)
- Removed@typescript-eslint/scope-manager@4.15.14.33.0(transitive)
- Removed@typescript-eslint/types@4.15.14.33.0(transitive)
- Removed@typescript-eslint/typescript-estree@4.15.14.33.0(transitive)
- Removed@typescript-eslint/visitor-keys@4.15.14.33.0(transitive)
- Removedacorn@7.4.1(transitive)
- Removedajv@8.17.1(transitive)
- Removedansi-colors@4.1.3(transitive)
- Removedansi-styles@3.2.1(transitive)
- Removedargparse@1.0.10(transitive)
- Removedastral-regex@2.0.0(transitive)
- Removedchalk@2.4.2(transitive)
- Removedcolor-convert@1.9.3(transitive)
- Removedcolor-name@1.1.3(transitive)
- Removedemoji-regex@8.0.0(transitive)
- Removedenquirer@2.4.1(transitive)
- Removedescape-string-regexp@1.0.5(transitive)
- Removedeslint@7.32.0(transitive)
- Removedeslint-utils@2.1.0(transitive)
- Removedeslint-visitor-keys@1.3.02.1.0(transitive)
- Removedespree@7.3.1(transitive)
- Removedesprima@4.0.1(transitive)
- Removedfast-uri@3.0.1(transitive)
- Removedfunctional-red-black-tree@1.0.1(transitive)
- Removedhas-flag@3.0.0(transitive)
- Removedignore@4.0.6(transitive)
- Removedis-fullwidth-code-point@3.0.0(transitive)
- Removedjs-tokens@4.0.0(transitive)
- Removedjs-yaml@3.14.1(transitive)
- Removedjson-schema-traverse@1.0.0(transitive)
- Removedlodash@4.17.21(transitive)
- Removedlodash.truncate@4.4.2(transitive)
- Removedpicocolors@1.1.0(transitive)
- Removedprogress@2.0.3(transitive)
- Removedregexpp@3.2.0(transitive)
- Removedrequire-from-string@2.0.2(transitive)
- Removedslice-ansi@4.0.0(transitive)
- Removedsprintf-js@1.0.3(transitive)
- Removedstring-width@4.2.3(transitive)
- Removedsupports-color@5.5.0(transitive)
- Removedtable@6.8.2(transitive)
- Removedv8-compile-cache@2.4.0(transitive)
Updateddebug@^4.3.4
Updatedsemver@^7.3.7
Updatedtsutils@^3.21.0