@typescript-eslint/eslint-plugin
Advanced tools
Comparing version 6.13.2 to 6.18.1
@@ -17,13 +17,5 @@ "use strict"; | ||
'@typescript-eslint/ban-types': 'error', | ||
'block-spacing': 'off', | ||
'@typescript-eslint/block-spacing': 'error', | ||
'brace-style': 'off', | ||
'@typescript-eslint/brace-style': 'error', | ||
'@typescript-eslint/class-literal-property-style': 'error', | ||
'class-methods-use-this': 'off', | ||
'@typescript-eslint/class-methods-use-this': 'error', | ||
'comma-dangle': 'off', | ||
'@typescript-eslint/comma-dangle': 'error', | ||
'comma-spacing': 'off', | ||
'@typescript-eslint/comma-spacing': 'error', | ||
'@typescript-eslint/consistent-generic-constructors': 'error', | ||
@@ -42,19 +34,6 @@ '@typescript-eslint/consistent-indexed-object-style': 'error', | ||
'@typescript-eslint/explicit-module-boundary-types': 'error', | ||
'func-call-spacing': 'off', | ||
'@typescript-eslint/func-call-spacing': 'error', | ||
indent: 'off', | ||
'@typescript-eslint/indent': 'error', | ||
'init-declarations': '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', | ||
'@typescript-eslint/lines-between-class-members': 'error', | ||
'max-params': 'off', | ||
'@typescript-eslint/max-params': 'error', | ||
'@typescript-eslint/member-delimiter-style': 'error', | ||
'@typescript-eslint/member-ordering': 'error', | ||
@@ -78,6 +57,2 @@ '@typescript-eslint/method-signature-style': 'error', | ||
'@typescript-eslint/no-extra-non-null-assertion': 'error', | ||
'no-extra-parens': 'off', | ||
'@typescript-eslint/no-extra-parens': 'error', | ||
'no-extra-semi': 'off', | ||
'@typescript-eslint/no-extra-semi': 'error', | ||
'@typescript-eslint/no-extraneous-class': 'error', | ||
@@ -141,13 +116,10 @@ '@typescript-eslint/no-floating-promises': 'error', | ||
'@typescript-eslint/no-useless-empty-export': 'error', | ||
'@typescript-eslint/no-useless-template-literals': 'error', | ||
'@typescript-eslint/no-var-requires': 'error', | ||
'@typescript-eslint/non-nullable-type-assertion-style': 'error', | ||
'object-curly-spacing': 'off', | ||
'@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', | ||
'@typescript-eslint/prefer-enum-initializers': 'error', | ||
'prefer-destructuring': 'off', | ||
'@typescript-eslint/prefer-destructuring': 'error', | ||
'@typescript-eslint/prefer-enum-initializers': 'error', | ||
'@typescript-eslint/prefer-for-of': 'error', | ||
@@ -168,4 +140,2 @@ '@typescript-eslint/prefer-function-type': 'error', | ||
'@typescript-eslint/promise-function-async': 'error', | ||
quotes: 'off', | ||
'@typescript-eslint/quotes': 'error', | ||
'@typescript-eslint/require-array-sort-compare': 'error', | ||
@@ -178,15 +148,6 @@ 'require-await': 'off', | ||
'@typescript-eslint/return-await': 'error', | ||
semi: 'off', | ||
'@typescript-eslint/semi': 'error', | ||
'@typescript-eslint/sort-type-constituents': 'error', | ||
'space-before-blocks': 'off', | ||
'@typescript-eslint/space-before-blocks': 'error', | ||
'space-before-function-paren': 'off', | ||
'@typescript-eslint/space-before-function-paren': 'error', | ||
'space-infix-ops': 'off', | ||
'@typescript-eslint/space-infix-ops': 'error', | ||
'@typescript-eslint/strict-boolean-expressions': 'error', | ||
'@typescript-eslint/switch-exhaustiveness-check': 'error', | ||
'@typescript-eslint/triple-slash-reference': 'error', | ||
'@typescript-eslint/type-annotation-spacing': 'error', | ||
'@typescript-eslint/typedef': 'error', | ||
@@ -193,0 +154,0 @@ '@typescript-eslint/unbound-method': 'error', |
@@ -37,3 +37,6 @@ "use strict"; | ||
'@typescript-eslint/no-unsafe-return': 'off', | ||
'@typescript-eslint/no-unsafe-unary-minus': 'off', | ||
'@typescript-eslint/no-useless-template-literals': 'off', | ||
'@typescript-eslint/non-nullable-type-assertion-style': 'off', | ||
'@typescript-eslint/prefer-destructuring': 'off', | ||
'@typescript-eslint/prefer-includes': 'off', | ||
@@ -40,0 +43,0 @@ '@typescript-eslint/prefer-nullish-coalescing': 'off', |
@@ -59,2 +59,3 @@ "use strict"; | ||
'@typescript-eslint/no-useless-constructor': 'error', | ||
'@typescript-eslint/no-useless-template-literals': 'error', | ||
'@typescript-eslint/no-var-requires': 'error', | ||
@@ -61,0 +62,0 @@ '@typescript-eslint/prefer-as-const': 'error', |
@@ -102,3 +102,3 @@ "use strict"; | ||
* Check the body for overload methods. | ||
* @param {ASTNode} node the body to be inspected. | ||
* @param node the body to be inspected. | ||
*/ | ||
@@ -105,0 +105,0 @@ function checkBodyForOverloadMethods(node) { |
@@ -11,2 +11,4 @@ "use strict"; | ||
meta: { | ||
deprecated: true, | ||
replacedBy: ['@stylistic/ts/block-spacing'], | ||
type: 'layout', | ||
@@ -13,0 +15,0 @@ docs: { |
@@ -10,2 +10,4 @@ "use strict"; | ||
meta: { | ||
deprecated: true, | ||
replacedBy: ['@stylistic/ts/brace-style'], | ||
type: 'layout', | ||
@@ -12,0 +14,0 @@ docs: { |
@@ -32,2 +32,4 @@ "use strict"; | ||
meta: { | ||
deprecated: true, | ||
replacedBy: ['@stylistic/ts/comma-dangle'], | ||
type: 'layout', | ||
@@ -34,0 +36,0 @@ docs: { |
@@ -9,2 +9,4 @@ "use strict"; | ||
meta: { | ||
deprecated: true, | ||
replacedBy: ['@stylistic/ts/comma-spacing'], | ||
type: 'layout', | ||
@@ -11,0 +13,0 @@ docs: { |
@@ -136,3 +136,3 @@ "use strict"; | ||
// We have both type and value violations. | ||
const allExportNames = report.typeBasedSpecifiers.map(specifier => `${specifier.local.name}`); | ||
const allExportNames = report.typeBasedSpecifiers.map(specifier => specifier.local.name); | ||
if (allExportNames.length === 1) { | ||
@@ -139,0 +139,0 @@ const exportNames = allExportNames[0]; |
@@ -189,3 +189,4 @@ "use strict"; | ||
report.node.importKind !== 'type') { | ||
/** checks if import has type assertions | ||
/** | ||
* checks if import has type assertions | ||
* ``` | ||
@@ -192,0 +193,0 @@ * import * as type from 'mod' assert { type: 'json' }; |
@@ -8,2 +8,4 @@ "use strict"; | ||
meta: { | ||
deprecated: true, | ||
replacedBy: ['@stylistic/ts/func-call-spacing'], | ||
type: 'layout', | ||
@@ -62,4 +64,3 @@ docs: { | ||
* Check if open space is present in a function name | ||
* @param {ASTNode} node node to evaluate | ||
* @returns {void} | ||
* @param node node to evaluate | ||
* @private | ||
@@ -66,0 +67,0 @@ */ |
@@ -82,2 +82,4 @@ "use strict"; | ||
meta: { | ||
deprecated: true, | ||
replacedBy: ['@stylistic/ts/indent'], | ||
type: 'layout', | ||
@@ -84,0 +86,0 @@ docs: { |
@@ -101,2 +101,3 @@ "use strict"; | ||
const no_useless_empty_export_1 = __importDefault(require("./no-useless-empty-export")); | ||
const no_useless_template_literals_1 = __importDefault(require("./no-useless-template-literals")); | ||
const no_var_requires_1 = __importDefault(require("./no-var-requires")); | ||
@@ -239,2 +240,3 @@ const non_nullable_type_assertion_style_1 = __importDefault(require("./non-nullable-type-assertion-style")); | ||
'no-useless-empty-export': no_useless_empty_export_1.default, | ||
'no-useless-template-literals': no_useless_template_literals_1.default, | ||
'no-var-requires': no_var_requires_1.default, | ||
@@ -241,0 +243,0 @@ 'non-nullable-type-assertion-style': non_nullable_type_assertion_style_1.default, |
@@ -15,2 +15,4 @@ "use strict"; | ||
meta: { | ||
deprecated: true, | ||
replacedBy: ['@stylistic/ts/key-spacing'], | ||
type: 'layout', | ||
@@ -17,0 +19,0 @@ docs: { |
@@ -27,2 +27,4 @@ "use strict"; | ||
meta: { | ||
deprecated: true, | ||
replacedBy: ['@stylistic/ts/keyword-spacing'], | ||
type: 'layout', | ||
@@ -29,0 +31,0 @@ docs: { |
@@ -37,2 +37,4 @@ "use strict"; | ||
meta: { | ||
deprecated: true, | ||
replacedBy: ['@stylistic/ts/lines-around-comment'], | ||
type: 'layout', | ||
@@ -39,0 +41,0 @@ docs: { |
@@ -20,2 +20,4 @@ "use strict"; | ||
meta: { | ||
deprecated: true, | ||
replacedBy: ['@stylistic/ts/lines-between-class-members'], | ||
type: 'layout', | ||
@@ -22,0 +24,0 @@ docs: { |
@@ -67,2 +67,4 @@ "use strict"; | ||
meta: { | ||
deprecated: true, | ||
replacedBy: ['@stylistic/ts/member-delimiter-style'], | ||
type: 'layout', | ||
@@ -228,3 +230,3 @@ docs: { | ||
* Check the member separator being used matches the delimiter. | ||
* @param {ASTNode} node the node to be evaluated. | ||
* @param node the node to be evaluated. | ||
*/ | ||
@@ -231,0 +233,0 @@ function checkMemberSeparatorStyle(node) { |
@@ -284,3 +284,2 @@ "use strict"; | ||
* @param node the node to be evaluated. | ||
* @param sourceCode | ||
*/ | ||
@@ -595,3 +594,3 @@ function getMemberName(node, sourceCode) { | ||
* @param members Members to be validated. | ||
* @param caseSensitive indicates if the alpha ordering is case sensitive or not. | ||
* @param order What order the members should be sorted in. | ||
* | ||
@@ -598,0 +597,0 @@ * @return True if all members are correctly sorted. |
@@ -13,2 +13,4 @@ "use strict"; | ||
meta: { | ||
deprecated: true, | ||
replacedBy: ['@stylistic/ts/no-extra-parens'], | ||
type: 'layout', | ||
@@ -15,0 +17,0 @@ docs: { |
@@ -9,2 +9,4 @@ "use strict"; | ||
meta: { | ||
deprecated: true, | ||
replacedBy: ['@stylistic/ts/no-extra-semi'], | ||
type: 'suggestion', | ||
@@ -11,0 +13,0 @@ docs: { |
@@ -34,2 +34,5 @@ "use strict"; | ||
const messageRejectionHandler = 'A rejection handler that is not a function will be ignored.'; | ||
const messagePromiseArray = "An array of Promises may be unintentional. Consider handling the promises' fulfillment or rejection with Promise.all or similar."; | ||
const messagePromiseArrayVoid = "An array of Promises may be unintentional. Consider handling the promises' fulfillment or rejection with Promise.all or similar," + | ||
' or explicitly marking the expression as ignored with the `void` operator.'; | ||
exports.default = (0, util_1.createRule)({ | ||
@@ -51,2 +54,4 @@ name: 'no-floating-promises', | ||
floatingUselessRejectionHandlerVoid: messageBaseVoid + ' ' + messageRejectionHandler, | ||
floatingPromiseArray: messagePromiseArray, | ||
floatingPromiseArrayVoid: messagePromiseArrayVoid, | ||
}, | ||
@@ -89,7 +94,15 @@ schema: [ | ||
} | ||
const { isUnhandled, nonFunctionHandler } = isUnhandledPromise(checker, expression); | ||
const { isUnhandled, nonFunctionHandler, promiseArray } = isUnhandledPromise(checker, expression); | ||
if (isUnhandled) { | ||
if (options.ignoreVoid) { | ||
if (promiseArray) { | ||
context.report({ | ||
node, | ||
messageId: options.ignoreVoid | ||
? 'floatingPromiseArrayVoid' | ||
: 'floatingPromiseArray', | ||
}); | ||
} | ||
else if (options.ignoreVoid) { | ||
context.report({ | ||
node, | ||
messageId: nonFunctionHandler | ||
@@ -183,4 +196,9 @@ ? 'floatingUselessRejectionHandlerVoid' | ||
} | ||
const tsNode = services.esTreeNodeToTSNodeMap.get(node); | ||
// Check the type. At this point it can't be unhandled if it isn't a promise | ||
if (!isPromiseLike(checker, services.esTreeNodeToTSNodeMap.get(node))) { | ||
// or array thereof. | ||
if (isPromiseArray(checker, tsNode)) { | ||
return { isUnhandled: true, promiseArray: true }; | ||
} | ||
if (!isPromiseLike(checker, tsNode)) { | ||
return { isUnhandled: false }; | ||
@@ -245,2 +263,23 @@ } | ||
}); | ||
function isPromiseArray(checker, node) { | ||
const type = checker.getTypeAtLocation(node); | ||
for (const ty of tsutils | ||
.unionTypeParts(type) | ||
.map(t => checker.getApparentType(t))) { | ||
if (checker.isArrayType(ty)) { | ||
const arrayType = checker.getTypeArguments(ty)[0]; | ||
if (isPromiseLike(checker, node, arrayType)) { | ||
return true; | ||
} | ||
} | ||
if (checker.isTupleType(ty)) { | ||
for (const tupleElementType of checker.getTypeArguments(ty)) { | ||
if (isPromiseLike(checker, node, tupleElementType)) { | ||
return true; | ||
} | ||
} | ||
} | ||
} | ||
return false; | ||
} | ||
// Modified from tsutils.isThenable() to only consider thenables which can be | ||
@@ -250,4 +289,4 @@ // rejected/caught via a second parameter. Original source (MIT licensed): | ||
// https://github.com/ajafff/tsutils/blob/49d0d31050b44b81e918eae4fbaf1dfe7b7286af/util/type.ts#L95-L125 | ||
function isPromiseLike(checker, node) { | ||
const type = checker.getTypeAtLocation(node); | ||
function isPromiseLike(checker, node, type) { | ||
type ??= checker.getTypeAtLocation(node); | ||
for (const ty of tsutils.unionTypeParts(checker.getApparentType(type))) { | ||
@@ -254,0 +293,0 @@ const then = ty.getProperty('then'); |
@@ -37,3 +37,3 @@ "use strict"; | ||
messages: { | ||
forInViolation: 'For-in loops over arrays are forbidden. Use for-of or array.forEach instead.', | ||
forInViolation: 'For-in loops over arrays skips holes, returns indices as strings, and may visit the prototype chain or other enumerable properties. Use a more robust iteration method such as for-of or array.forEach instead.', | ||
}, | ||
@@ -40,0 +40,0 @@ schema: [], |
@@ -28,3 +28,2 @@ "use strict"; | ||
* @param node The AST node to check. | ||
* @returns Whether or not the node is within a loop. | ||
*/ | ||
@@ -31,0 +30,0 @@ function checkForLoops(node) { |
@@ -27,19 +27,9 @@ "use strict"; | ||
const suggest = []; | ||
function convertTokenToOptional(replacement) { | ||
return (fixer) => { | ||
const operator = sourceCode.getTokenAfter(node.expression, util_1.isNonNullAssertionPunctuator); | ||
if (operator) { | ||
return fixer.replaceText(operator, replacement); | ||
} | ||
return null; | ||
}; | ||
// it always exists in non-null assertion | ||
const nonNullOperator = sourceCode.getTokenAfter(node.expression, util_1.isNonNullAssertionPunctuator); | ||
function replaceTokenWithOptional() { | ||
return fixer => fixer.replaceText(nonNullOperator, '?.'); | ||
} | ||
function removeToken() { | ||
return (fixer) => { | ||
const operator = sourceCode.getTokenAfter(node.expression, util_1.isNonNullAssertionPunctuator); | ||
if (operator) { | ||
return fixer.remove(operator); | ||
} | ||
return null; | ||
}; | ||
return fixer => fixer.remove(nonNullOperator); | ||
} | ||
@@ -53,3 +43,3 @@ if (node.parent.type === utils_1.AST_NODE_TYPES.MemberExpression && | ||
messageId: 'suggestOptionalChain', | ||
fix: convertTokenToOptional('?.'), | ||
fix: replaceTokenWithOptional(), | ||
}); | ||
@@ -61,3 +51,11 @@ } | ||
messageId: 'suggestOptionalChain', | ||
fix: convertTokenToOptional('?'), | ||
fix(fixer) { | ||
// x!.y?.z | ||
// ^ punctuator | ||
const punctuator = sourceCode.getTokenAfter(nonNullOperator); | ||
return [ | ||
fixer.remove(nonNullOperator), | ||
fixer.insertTextBefore(punctuator, '?'), | ||
]; | ||
}, | ||
}); | ||
@@ -89,3 +87,3 @@ } | ||
messageId: 'suggestOptionalChain', | ||
fix: convertTokenToOptional('?.'), | ||
fix: replaceTokenWithOptional(), | ||
}); | ||
@@ -92,0 +90,0 @@ } |
"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 eslint_utils_1 = require("@typescript-eslint/utils/eslint-utils"); | ||
const util_1 = require("../util"); | ||
exports.default = (0, util_1.createRule)({ | ||
const util = __importStar(require("../util")); | ||
exports.default = util.createRule({ | ||
name: 'no-require-imports', | ||
@@ -13,3 +36,15 @@ meta: { | ||
}, | ||
schema: [], | ||
schema: [ | ||
{ | ||
type: 'object', | ||
properties: { | ||
allow: { | ||
type: 'array', | ||
items: { type: 'string' }, | ||
description: 'Patterns of import paths to allow requiring from.', | ||
}, | ||
}, | ||
additionalProperties: false, | ||
}, | ||
], | ||
messages: { | ||
@@ -19,6 +54,15 @@ noRequireImports: 'A `require()` style import is forbidden.', | ||
}, | ||
defaultOptions: [], | ||
create(context) { | ||
defaultOptions: [{ allow: [] }], | ||
create(context, options) { | ||
const allowPatterns = options[0].allow.map(pattern => new RegExp(pattern, 'u')); | ||
function isImportPathAllowed(importPath) { | ||
return allowPatterns.some(pattern => importPath.match(pattern)); | ||
} | ||
return { | ||
'CallExpression[callee.name="require"]'(node) { | ||
if (node.arguments[0]?.type === utils_1.AST_NODE_TYPES.Literal && | ||
typeof node.arguments[0].value === 'string' && | ||
isImportPathAllowed(node.arguments[0].value)) { | ||
return; | ||
} | ||
const variable = utils_1.ASTUtils.findVariable((0, eslint_utils_1.getScope)(context), 'require'); | ||
@@ -35,2 +79,7 @@ // ignore non-global require usage as it's something user-land custom instead | ||
TSExternalModuleReference(node) { | ||
if (node.expression.type === utils_1.AST_NODE_TYPES.Literal && | ||
typeof node.expression.value === 'string' && | ||
isImportPathAllowed(node.expression.value)) { | ||
return; | ||
} | ||
context.report({ | ||
@@ -37,0 +86,0 @@ node, |
@@ -124,2 +124,11 @@ "use strict"; | ||
} | ||
function shouldCreateRule(baseRules, options) { | ||
if (Object.keys(baseRules).length === 0 || options.length === 0) { | ||
return false; | ||
} | ||
if (!isOptionsArrayOfStringOrObject(options)) { | ||
return !!(options[0].paths?.length || options[0].patterns?.length); | ||
} | ||
return true; | ||
} | ||
exports.default = (0, util_1.createRule)({ | ||
@@ -141,3 +150,3 @@ name: 'no-restricted-imports', | ||
const { options } = context; | ||
if (options.length === 0) { | ||
if (!shouldCreateRule(rules, options)) { | ||
return {}; | ||
@@ -144,0 +153,0 @@ } |
@@ -387,3 +387,3 @@ "use strict"; | ||
* Checks the current context for shadowed variables. | ||
* @param {Scope} scope Fixme | ||
* @param scope Fixme | ||
*/ | ||
@@ -390,0 +390,0 @@ function checkForShadows(scope) { |
@@ -230,4 +230,4 @@ "use strict"; | ||
* Known exceptions: | ||
* * https://github.com/microsoft/TypeScript/issues/32627 | ||
* * https://github.com/microsoft/TypeScript/issues/37160 (handled) | ||
* - https://github.com/microsoft/TypeScript/issues/32627 | ||
* - https://github.com/microsoft/TypeScript/issues/37160 (handled) | ||
*/ | ||
@@ -421,3 +421,3 @@ const BOOL_OPERATORS = new Set([ | ||
// ``` | ||
function isNullableOriginFromPrev(node) { | ||
function isMemberExpressionNullableOriginFromObject(node) { | ||
const prevType = (0, util_1.getConstrainedTypeAtLocation)(services, node.object); | ||
@@ -441,7 +441,20 @@ const property = node.property; | ||
} | ||
function isCallExpressionNullableOriginFromCallee(node) { | ||
const prevType = (0, util_1.getConstrainedTypeAtLocation)(services, node.callee); | ||
if (prevType.isUnion()) { | ||
const isOwnNullable = prevType.types.some(type => { | ||
const signatures = type.getCallSignatures(); | ||
return signatures.some(sig => (0, util_1.isNullableType)(sig.getReturnType(), { allowUndefined: true })); | ||
}); | ||
return (!isOwnNullable && (0, util_1.isNullableType)(prevType, { allowUndefined: true })); | ||
} | ||
return false; | ||
} | ||
function isOptionableExpression(node) { | ||
const type = (0, util_1.getConstrainedTypeAtLocation)(services, node); | ||
const isOwnNullable = node.type === utils_1.AST_NODE_TYPES.MemberExpression | ||
? !isNullableOriginFromPrev(node) | ||
: true; | ||
? !isMemberExpressionNullableOriginFromObject(node) | ||
: node.type === utils_1.AST_NODE_TYPES.CallExpression | ||
? !isCallExpressionNullableOriginFromCallee(node) | ||
: true; | ||
const possiblyVoid = (0, util_1.isTypeFlagSet)(type, ts.TypeFlags.Void); | ||
@@ -448,0 +461,0 @@ return ((0, util_1.isTypeFlagSet)(type, ts.TypeFlags.Any | ts.TypeFlags.Unknown) || |
@@ -38,3 +38,3 @@ "use strict"; | ||
messages: { | ||
unaryMinus: 'Invalid type "{{type}}" of template literal expression.', | ||
unaryMinus: 'Argument of unary negation should be assignable to number | bigint but is {{type}} instead.', | ||
}, | ||
@@ -41,0 +41,0 @@ schema: [], |
@@ -102,4 +102,4 @@ "use strict"; | ||
* 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. | ||
* @param node a node to check | ||
* @returns True if the node is a sibling of the rest property, otherwise false. | ||
*/ | ||
@@ -106,0 +106,0 @@ function hasRestSibling(node) { |
@@ -17,8 +17,29 @@ "use strict"; | ||
}, | ||
schema: [], | ||
schema: [ | ||
{ | ||
type: 'object', | ||
properties: { | ||
allow: { | ||
type: 'array', | ||
items: { type: 'string' }, | ||
description: 'Patterns of import paths to allow requiring from.', | ||
}, | ||
}, | ||
additionalProperties: false, | ||
}, | ||
], | ||
}, | ||
defaultOptions: [], | ||
create(context) { | ||
defaultOptions: [{ allow: [] }], | ||
create(context, options) { | ||
const allowPatterns = options[0].allow.map(pattern => new RegExp(pattern, 'u')); | ||
function isImportPathAllowed(importPath) { | ||
return allowPatterns.some(pattern => importPath.match(pattern)); | ||
} | ||
return { | ||
'CallExpression[callee.name="require"]'(node) { | ||
if (node.arguments[0]?.type === utils_1.AST_NODE_TYPES.Literal && | ||
typeof node.arguments[0].value === 'string' && | ||
isImportPathAllowed(node.arguments[0].value)) { | ||
return; | ||
} | ||
const parent = node.parent.type === utils_1.AST_NODE_TYPES.ChainExpression | ||
@@ -25,0 +46,0 @@ ? node.parent.parent |
@@ -13,2 +13,3 @@ "use strict"; | ||
...baseRule.meta, | ||
deprecated: true, | ||
docs: { | ||
@@ -18,2 +19,3 @@ description: 'Enforce consistent spacing inside braces', | ||
}, | ||
replacedBy: ['@stylistic/ts/object-curly-spacing'], | ||
}, | ||
@@ -20,0 +22,0 @@ defaultOptions: ['never'], |
@@ -121,4 +121,4 @@ "use strict"; | ||
* This checks the last token of the node is the closing brace of a block. | ||
* @param node The node to check. | ||
* @param sourceCode The source code to get tokens. | ||
* @param node The node to check. | ||
* @returns `true` if the node is a block-like statement. | ||
@@ -230,4 +230,4 @@ * @private | ||
* ;[1, 2, 3].forEach(bar) | ||
* @param node The node to get. | ||
* @param sourceCode The source code to get tokens. | ||
* @param node The node to get. | ||
* @returns The actual last token. | ||
@@ -442,2 +442,4 @@ * @private | ||
meta: { | ||
deprecated: true, | ||
replacedBy: ['@stylistic/ts/padding-line-between-statements'], | ||
type: 'layout', | ||
@@ -444,0 +446,0 @@ docs: { |
@@ -62,3 +62,2 @@ "use strict"; | ||
* @param node The parent of member being checked | ||
* @param tsThisTypes | ||
*/ | ||
@@ -65,0 +64,0 @@ function checkMember(member, node, tsThisTypes = null) { |
@@ -182,2 +182,9 @@ "use strict"; | ||
const DIRECTLY_INSIDE_CONSTRUCTOR = 0; | ||
var TypeToClassRelation; | ||
(function (TypeToClassRelation) { | ||
TypeToClassRelation[TypeToClassRelation["ClassAndInstance"] = 0] = "ClassAndInstance"; | ||
TypeToClassRelation[TypeToClassRelation["Class"] = 1] = "Class"; | ||
TypeToClassRelation[TypeToClassRelation["Instance"] = 2] = "Instance"; | ||
TypeToClassRelation[TypeToClassRelation["None"] = 3] = "None"; | ||
})(TypeToClassRelation || (TypeToClassRelation = {})); | ||
class ClassScope { | ||
@@ -221,17 +228,55 @@ constructor(checker, classNode, onlyInlineLambdas) { | ||
} | ||
getTypeToClassRelation(type) { | ||
if (type.isIntersection()) { | ||
let result = TypeToClassRelation.None; | ||
for (const subType of type.types) { | ||
const subTypeResult = this.getTypeToClassRelation(subType); | ||
switch (subTypeResult) { | ||
case TypeToClassRelation.Class: | ||
if (result === TypeToClassRelation.Instance) { | ||
return TypeToClassRelation.ClassAndInstance; | ||
} | ||
result = TypeToClassRelation.Class; | ||
break; | ||
case TypeToClassRelation.Instance: | ||
if (result === TypeToClassRelation.Class) { | ||
return TypeToClassRelation.ClassAndInstance; | ||
} | ||
result = TypeToClassRelation.Instance; | ||
break; | ||
} | ||
} | ||
return result; | ||
} | ||
if (type.isUnion()) { | ||
// any union of class/instance and something else will prevent access to | ||
// private members, so we assume that union consists only of classes | ||
// or class instances, because otherwise tsc will report an error | ||
return this.getTypeToClassRelation(type.types[0]); | ||
} | ||
if (!type.getSymbol() || !(0, util_1.typeIsOrHasBaseType)(type, this.classType)) { | ||
return TypeToClassRelation.None; | ||
} | ||
const typeIsClass = tsutils.isObjectType(type) && | ||
tsutils.isObjectFlagSet(type, ts.ObjectFlags.Anonymous); | ||
if (typeIsClass) { | ||
return TypeToClassRelation.Class; | ||
} | ||
return TypeToClassRelation.Instance; | ||
} | ||
addVariableModification(node) { | ||
const modifierType = this.checker.getTypeAtLocation(node.expression); | ||
if (!modifierType.getSymbol() || | ||
!(0, util_1.typeIsOrHasBaseType)(modifierType, this.classType)) { | ||
return; | ||
} | ||
const modifyingStatic = tsutils.isObjectType(modifierType) && | ||
tsutils.isObjectFlagSet(modifierType, ts.ObjectFlags.Anonymous); | ||
if (!modifyingStatic && | ||
const relationOfModifierTypeToClass = this.getTypeToClassRelation(modifierType); | ||
if (relationOfModifierTypeToClass === TypeToClassRelation.Instance && | ||
this.constructorScopeDepth === DIRECTLY_INSIDE_CONSTRUCTOR) { | ||
return; | ||
} | ||
(modifyingStatic | ||
? this.staticVariableModifications | ||
: this.memberVariableModifications).add(node.name.text); | ||
if (relationOfModifierTypeToClass === TypeToClassRelation.Instance || | ||
relationOfModifierTypeToClass === TypeToClassRelation.ClassAndInstance) { | ||
this.memberVariableModifications.add(node.name.text); | ||
} | ||
if (relationOfModifierTypeToClass === TypeToClassRelation.Class || | ||
relationOfModifierTypeToClass === TypeToClassRelation.ClassAndInstance) { | ||
this.staticVariableModifications.add(node.name.text); | ||
} | ||
} | ||
@@ -238,0 +283,0 @@ enterConstructor(node) { |
@@ -59,3 +59,3 @@ "use strict"; | ||
* Check if a given node type is a string. | ||
* @param node The node type to check. | ||
* @param type The node type to check. | ||
*/ | ||
@@ -67,3 +67,3 @@ function isStringType(type) { | ||
* Check if a given node type is a RegExp. | ||
* @param node The node type to check. | ||
* @param type The node type to check. | ||
*/ | ||
@@ -70,0 +70,0 @@ function isRegExpType(type) { |
@@ -59,3 +59,2 @@ "use strict"; | ||
* @param node The node to check. | ||
* @param kind The method name to get a character. | ||
*/ | ||
@@ -62,0 +61,0 @@ function isCharacter(node) { |
@@ -10,2 +10,4 @@ "use strict"; | ||
meta: { | ||
deprecated: true, | ||
replacedBy: ['@stylistic/ts/quotes'], | ||
type: 'layout', | ||
@@ -12,0 +14,0 @@ docs: { |
@@ -14,3 +14,3 @@ "use strict"; | ||
docs: { | ||
description: 'Require `Array#sort` calls to always provide a `compareFunction`', | ||
description: 'Require `Array#sort` and `Array#toSorted` calls to always provide a `compareFunction`', | ||
requiresTypeChecking: true, | ||
@@ -39,3 +39,2 @@ }, | ||
* Check if a given node is an array which all elements are string. | ||
* @param node | ||
*/ | ||
@@ -50,12 +49,14 @@ function isStringArrayNode(node) { | ||
} | ||
function checkSortArgument(callee) { | ||
const calleeObjType = (0, util_1.getConstrainedTypeAtLocation)(services, callee.object); | ||
if (options.ignoreStringArrays && isStringArrayNode(callee.object)) { | ||
return; | ||
} | ||
if ((0, util_1.isTypeArrayTypeOrUnionOfArrayTypes)(calleeObjType, checker)) { | ||
context.report({ node: callee.parent, messageId: 'requireCompare' }); | ||
} | ||
} | ||
return { | ||
"CallExpression[arguments.length=0] > MemberExpression[property.name='sort'][computed=false]"(callee) { | ||
const calleeObjType = (0, util_1.getConstrainedTypeAtLocation)(services, callee.object); | ||
if (options.ignoreStringArrays && isStringArrayNode(callee.object)) { | ||
return; | ||
} | ||
if ((0, util_1.isTypeArrayTypeOrUnionOfArrayTypes)(calleeObjType, checker)) { | ||
context.report({ node: callee.parent, messageId: 'requireCompare' }); | ||
} | ||
}, | ||
"CallExpression[arguments.length=0] > MemberExpression[property.name='sort'][computed=false]": checkSortArgument, | ||
"CallExpression[arguments.length=0] > MemberExpression[property.name='toSorted'][computed=false]": checkSortArgument, | ||
}; | ||
@@ -62,0 +63,0 @@ }, |
@@ -104,6 +104,8 @@ "use strict"; | ||
/** | ||
* mark `scopeInfo.isAsyncYield` to `true` if its a generator | ||
* function and the delegate is `true` | ||
* Mark `scopeInfo.isAsyncYield` to `true` if it | ||
* 1) delegates async generator function | ||
* or | ||
* 2) yields thenable type | ||
*/ | ||
function markAsHasDelegateGen(node) { | ||
function visitYieldExpression(node) { | ||
if (!scopeInfo?.isGen || !node.argument) { | ||
@@ -113,6 +115,12 @@ return; | ||
if (node.argument.type === utils_1.AST_NODE_TYPES.Literal) { | ||
// making this `false` as for literals we don't need to check the definition | ||
// ignoring this as for literals we don't need to check the definition | ||
// eg : async function* run() { yield* 1 } | ||
scopeInfo.isAsyncYield ||= false; | ||
return; | ||
} | ||
if (!node.delegate) { | ||
if (isThenableType(services.esTreeNodeToTSNodeMap.get(node.argument))) { | ||
scopeInfo.isAsyncYield = true; | ||
} | ||
return; | ||
} | ||
const type = services.getTypeAtLocation(node.argument); | ||
@@ -138,3 +146,3 @@ const typesToCheck = expandUnionOrIntersectionType(type); | ||
'ForOfStatement[await = true]': markAsHasAwait, | ||
'YieldExpression[delegate = true]': markAsHasDelegateGen, | ||
YieldExpression: visitYieldExpression, | ||
// check body-less async arrow function. | ||
@@ -141,0 +149,0 @@ // ignore `async () => await foo` because it's obviously correct |
@@ -10,2 +10,4 @@ "use strict"; | ||
meta: { | ||
deprecated: true, | ||
replacedBy: ['@stylistic/ts/semi'], | ||
type: 'layout', | ||
@@ -12,0 +14,0 @@ docs: { |
@@ -10,2 +10,4 @@ "use strict"; | ||
meta: { | ||
deprecated: true, | ||
replacedBy: ['@stylistic/ts/space-before-blocks'], | ||
type: 'layout', | ||
@@ -12,0 +14,0 @@ docs: { |
@@ -9,2 +9,4 @@ "use strict"; | ||
meta: { | ||
deprecated: true, | ||
replacedBy: ['@stylistic/ts/space-before-function-paren'], | ||
type: 'layout', | ||
@@ -56,4 +58,4 @@ docs: { | ||
* Determines whether a function has a name. | ||
* @param {ASTNode} node The function node. | ||
* @returns {boolean} Whether the function has a name. | ||
* @param node The function node. | ||
* @returns Whether the function has a name. | ||
*/ | ||
@@ -72,4 +74,3 @@ function isNamedFunction(node) { | ||
* Gets the config for a given function | ||
* @param {ASTNode} node The function node | ||
* @returns {string} "always", "never", or "ignore" | ||
* @param node The function node | ||
*/ | ||
@@ -95,4 +96,3 @@ function getConfigForFunction(node) { | ||
* Checks the parens of a function node | ||
* @param {ASTNode} node A function node | ||
* @returns {void} | ||
* @param node A function node | ||
*/ | ||
@@ -99,0 +99,0 @@ function checkFunction(node) { |
@@ -12,2 +12,4 @@ "use strict"; | ||
meta: { | ||
deprecated: true, | ||
replacedBy: ['@stylistic/ts/space-infix-ops'], | ||
type: 'layout', | ||
@@ -14,0 +16,0 @@ docs: { |
@@ -42,4 +42,7 @@ "use strict"; | ||
type: 'object', | ||
additionalProperties: false, | ||
properties: { | ||
allowDefaultCaseForExhaustiveSwitch: { | ||
description: `If 'true', allow 'default' cases on switch statements with exhaustive cases.`, | ||
type: 'boolean', | ||
}, | ||
requireDefaultForNonUnion: { | ||
@@ -50,2 +53,3 @@ description: `If 'true', require a 'default' clause for switches on non-union types.`, | ||
}, | ||
additionalProperties: false, | ||
}, | ||
@@ -55,7 +59,13 @@ ], | ||
switchIsNotExhaustive: 'Switch is not exhaustive. Cases not matched: {{missingBranches}}', | ||
dangerousDefaultCase: 'The switch statement is exhaustive, so the default case is unnecessary.', | ||
addMissingCases: 'Add branches for missing cases.', | ||
}, | ||
}, | ||
defaultOptions: [{ requireDefaultForNonUnion: false }], | ||
create(context, [{ requireDefaultForNonUnion }]) { | ||
defaultOptions: [ | ||
{ | ||
allowDefaultCaseForExhaustiveSwitch: true, | ||
requireDefaultForNonUnion: false, | ||
}, | ||
], | ||
create(context, [{ allowDefaultCaseForExhaustiveSwitch, requireDefaultForNonUnion }]) { | ||
const sourceCode = (0, eslint_utils_1.getSourceCode)(context); | ||
@@ -65,2 +75,75 @@ const services = (0, util_1.getParserServices)(context); | ||
const compilerOptions = services.program.getCompilerOptions(); | ||
function getSwitchMetadata(node) { | ||
const defaultCase = node.cases.find(switchCase => switchCase.test == null); | ||
const discriminantType = (0, util_1.getConstrainedTypeAtLocation)(services, node.discriminant); | ||
const symbolName = discriminantType.getSymbol()?.escapedName; | ||
const containsNonLiteralType = doesTypeContainNonLiteralType(discriminantType); | ||
if (!discriminantType.isUnion()) { | ||
return { | ||
symbolName, | ||
missingBranchTypes: [], | ||
defaultCase, | ||
isUnion: false, | ||
containsNonLiteralType, | ||
}; | ||
} | ||
const caseTypes = new Set(); | ||
for (const switchCase of node.cases) { | ||
// If the `test` property of the switch case is `null`, then we are on a | ||
// `default` case. | ||
if (switchCase.test == null) { | ||
continue; | ||
} | ||
const caseType = (0, util_1.getConstrainedTypeAtLocation)(services, switchCase.test); | ||
caseTypes.add(caseType); | ||
} | ||
const unionTypes = tsutils.unionTypeParts(discriminantType); | ||
const missingBranchTypes = unionTypes.filter(unionType => !caseTypes.has(unionType)); | ||
return { | ||
symbolName, | ||
missingBranchTypes, | ||
defaultCase, | ||
isUnion: true, | ||
containsNonLiteralType, | ||
}; | ||
} | ||
/** | ||
* For example: | ||
* | ||
* - `"foo" | "bar"` is a type with all literal types. | ||
* - `"foo" | number` is a type that contains non-literal types. | ||
* | ||
* Default cases are never superfluous in switches with non-literal types. | ||
*/ | ||
function doesTypeContainNonLiteralType(type) { | ||
const types = tsutils.unionTypeParts(type); | ||
return types.some(type => !isFlagSet(type.getFlags(), ts.TypeFlags.Literal | ts.TypeFlags.Undefined | ts.TypeFlags.Null)); | ||
} | ||
function checkSwitchExhaustive(node, switchMetadata) { | ||
const { missingBranchTypes, symbolName, defaultCase } = switchMetadata; | ||
// We only trigger the rule if a `default` case does not exist, since that | ||
// would disqualify the switch statement from having cases that exactly | ||
// match the members of a union. | ||
if (missingBranchTypes.length > 0 && defaultCase === undefined) { | ||
context.report({ | ||
node: node.discriminant, | ||
messageId: 'switchIsNotExhaustive', | ||
data: { | ||
missingBranches: missingBranchTypes | ||
.map(missingType => tsutils.isTypeFlagSet(missingType, ts.TypeFlags.ESSymbolLike) | ||
? `typeof ${missingType.getSymbol()?.escapedName}` | ||
: checker.typeToString(missingType)) | ||
.join(' | '), | ||
}, | ||
suggest: [ | ||
{ | ||
messageId: 'addMissingCases', | ||
fix(fixer) { | ||
return fixSwitch(fixer, node, missingBranchTypes, symbolName?.toString()); | ||
}, | ||
}, | ||
], | ||
}); | ||
} | ||
} | ||
function fixSwitch(fixer, node, missingBranchTypes, // null means default branch | ||
@@ -71,4 +154,4 @@ symbolName) { | ||
? ' '.repeat(lastCase.loc.start.column) | ||
: // if there are no cases, use indentation of the switch statement | ||
// and leave it to user to format it correctly | ||
: // If there are no cases, use indentation of the switch statement and | ||
// leave it to the user to format it correctly. | ||
' '.repeat(node.loc.start.column); | ||
@@ -81,10 +164,12 @@ const missingCases = []; | ||
} | ||
// While running this rule on checker.ts of TypeScript project | ||
// While running this rule on the "checker.ts" file of TypeScript, the | ||
// the fix introduced a compiler error due to: | ||
// | ||
// ```ts | ||
// type __String = (string & { | ||
// __escapedIdentifier: void; | ||
// }) | (void & { | ||
// __escapedIdentifier: void; | ||
// }) | InternalSymbolName; | ||
// __escapedIdentifier: void; | ||
// }) | (void & { | ||
// __escapedIdentifier: void; | ||
// }) | InternalSymbolName; | ||
// ``` | ||
// | ||
@@ -116,3 +201,3 @@ // The following check fixes it. | ||
} | ||
// there were no existing cases | ||
// There were no existing cases. | ||
const openingBrace = sourceCode.getTokenAfter(node.discriminant, util_1.isOpeningBraceToken); | ||
@@ -122,29 +207,27 @@ const closingBrace = sourceCode.getTokenAfter(node.discriminant, util_1.isClosingBraceToken); | ||
} | ||
function checkSwitchExhaustive(node) { | ||
const discriminantType = (0, util_1.getConstrainedTypeAtLocation)(services, node.discriminant); | ||
const symbolName = discriminantType.getSymbol()?.escapedName; | ||
if (discriminantType.isUnion()) { | ||
const unionTypes = tsutils.unionTypeParts(discriminantType); | ||
const caseTypes = new Set(); | ||
for (const switchCase of node.cases) { | ||
if (switchCase.test == null) { | ||
// Switch has 'default' branch - do nothing. | ||
return; | ||
} | ||
caseTypes.add((0, util_1.getConstrainedTypeAtLocation)(services, switchCase.test)); | ||
} | ||
const missingBranchTypes = unionTypes.filter(unionType => !caseTypes.has(unionType)); | ||
if (missingBranchTypes.length === 0) { | ||
// All cases matched - do nothing. | ||
return; | ||
} | ||
function checkSwitchUnnecessaryDefaultCase(switchMetadata) { | ||
if (allowDefaultCaseForExhaustiveSwitch) { | ||
return; | ||
} | ||
const { missingBranchTypes, defaultCase, containsNonLiteralType } = switchMetadata; | ||
if (missingBranchTypes.length === 0 && | ||
defaultCase !== undefined && | ||
!containsNonLiteralType) { | ||
context.report({ | ||
node: defaultCase, | ||
messageId: 'dangerousDefaultCase', | ||
}); | ||
} | ||
} | ||
function checkSwitchNoUnionDefaultCase(node, switchMetadata) { | ||
if (!requireDefaultForNonUnion) { | ||
return; | ||
} | ||
const { isUnion, defaultCase } = switchMetadata; | ||
if (!isUnion && defaultCase === undefined) { | ||
context.report({ | ||
node: node.discriminant, | ||
messageId: 'switchIsNotExhaustive', | ||
data: { | ||
missingBranches: missingBranchTypes | ||
.map(missingType => tsutils.isTypeFlagSet(missingType, ts.TypeFlags.ESSymbolLike) | ||
? `typeof ${missingType.getSymbol()?.escapedName}` | ||
: checker.typeToString(missingType)) | ||
.join(' | '), | ||
missingBranches: 'default', | ||
}, | ||
@@ -155,3 +238,3 @@ suggest: [ | ||
fix(fixer) { | ||
return fixSwitch(fixer, node, missingBranchTypes, symbolName?.toString()); | ||
return fixSwitch(fixer, node, [null]); | ||
}, | ||
@@ -162,28 +245,16 @@ }, | ||
} | ||
else if (requireDefaultForNonUnion) { | ||
const hasDefault = node.cases.some(switchCase => switchCase.test == null); | ||
if (!hasDefault) { | ||
context.report({ | ||
node: node.discriminant, | ||
messageId: 'switchIsNotExhaustive', | ||
data: { | ||
missingBranches: 'default', | ||
}, | ||
suggest: [ | ||
{ | ||
messageId: 'addMissingCases', | ||
fix(fixer) { | ||
return fixSwitch(fixer, node, [null]); | ||
}, | ||
}, | ||
], | ||
}); | ||
} | ||
} | ||
} | ||
return { | ||
SwitchStatement: checkSwitchExhaustive, | ||
SwitchStatement(node) { | ||
const switchMetadata = getSwitchMetadata(node); | ||
checkSwitchExhaustive(node, switchMetadata); | ||
checkSwitchUnnecessaryDefaultCase(switchMetadata); | ||
checkSwitchNoUnionDefaultCase(node, switchMetadata); | ||
}, | ||
}; | ||
}, | ||
}); | ||
function isFlagSet(flags, flag) { | ||
return (flags & flag) !== 0; | ||
} | ||
//# sourceMappingURL=switch-exhaustiveness-check.js.map |
@@ -59,2 +59,4 @@ "use strict"; | ||
meta: { | ||
deprecated: true, | ||
replacedBy: ['@stylistic/ts/type-annotation-spacing'], | ||
type: 'layout', | ||
@@ -61,0 +63,0 @@ docs: { |
@@ -32,47 +32,12 @@ "use strict"; | ||
/** | ||
* The following is a list of exceptions to the rule | ||
* Generated via the following script. | ||
* This is statically defined to save making purposely invalid calls every lint run | ||
* ``` | ||
SUPPORTED_GLOBALS.flatMap(namespace => { | ||
const object = window[namespace]; | ||
return Object.getOwnPropertyNames(object) | ||
.filter( | ||
name => | ||
!name.startsWith('_') && | ||
typeof object[name] === 'function', | ||
) | ||
.map(name => { | ||
try { | ||
const x = object[name]; | ||
x(); | ||
} catch (e) { | ||
if (e.message.includes("called on non-object")) { | ||
return `${namespace}.${name}`; | ||
} | ||
} | ||
}); | ||
}).filter(Boolean); | ||
* ``` | ||
* Static methods on these globals are either not `this`-aware or supported being | ||
* called without `this`. | ||
* | ||
* - `Promise` is not in the list because it supports subclassing by using `this` | ||
* - `Array` is in the list because although it supports subclassing, the `this` | ||
* value defaults to `Array` when unbound | ||
* | ||
* This is now a language-design invariant: static methods are never `this`-aware | ||
* because TC39 wants to make `array.map(Class.method)` work! | ||
*/ | ||
const nativelyNotBoundMembers = new Set([ | ||
'Promise.all', | ||
'Promise.race', | ||
'Promise.resolve', | ||
'Promise.reject', | ||
'Promise.allSettled', | ||
'Object.defineProperties', | ||
'Object.defineProperty', | ||
'Reflect.defineProperty', | ||
'Reflect.deleteProperty', | ||
'Reflect.get', | ||
'Reflect.getOwnPropertyDescriptor', | ||
'Reflect.getPrototypeOf', | ||
'Reflect.has', | ||
'Reflect.isExtensible', | ||
'Reflect.ownKeys', | ||
'Reflect.preventExtensions', | ||
'Reflect.set', | ||
'Reflect.setPrototypeOf', | ||
]); | ||
const SUPPORTED_GLOBALS = [ | ||
@@ -87,3 +52,2 @@ 'Number', | ||
'Date', | ||
'Infinity', | ||
'Atomics', | ||
@@ -96,3 +60,3 @@ 'Reflect', | ||
]; | ||
const nativelyBoundMembers = SUPPORTED_GLOBALS.map(namespace => { | ||
const nativelyBoundMembers = new Set(SUPPORTED_GLOBALS.flatMap(namespace => { | ||
if (!(namespace in global)) { | ||
@@ -108,5 +72,3 @@ // node.js might not have namespaces like Intl depending on compilation options | ||
.map(name => `${namespace}.${name}`); | ||
}) | ||
.reduce((arr, names) => arr.concat(names), []) | ||
.filter(name => !nativelyNotBoundMembers.has(name)); | ||
})); | ||
const isNotImported = (symbol, currentSourceFile) => { | ||
@@ -181,3 +143,3 @@ const { valueDeclaration } = symbol; | ||
if (objectSymbol && | ||
nativelyBoundMembers.includes(getMemberFullName(node)) && | ||
nativelyBoundMembers.has(getMemberFullName(node)) && | ||
isNotImported(objectSymbol, currentSourceFile)) { | ||
@@ -201,3 +163,3 @@ return; | ||
(0, util_1.isIdentifier)(initNode) && | ||
nativelyBoundMembers.includes(`${initNode.name}.${property.key.name}`)) { | ||
nativelyBoundMembers.has(`${initNode.name}.${property.key.name}`)) { | ||
return; | ||
@@ -204,0 +166,0 @@ } |
@@ -39,6 +39,6 @@ "use strict"; | ||
* Get the `loc` object of a given name in a `/*globals` directive comment. | ||
* @param {SourceCode} sourceCode The source code to convert index to loc. | ||
* @param {Comment} comment The `/*globals` directive comment which include the name. | ||
* @param {string} name The name to find. | ||
* @returns {SourceLocation} The `loc` object. | ||
* @param sourceCode The source code to convert index to loc. | ||
* @param comment The `/*globals` directive comment which include the name. | ||
* @param name The name to find. | ||
* @returns The `loc` object. | ||
*/ | ||
@@ -45,0 +45,0 @@ function getNameLocationInGlobalDirectiveComment(sourceCode, comment, name) { |
@@ -288,3 +288,2 @@ "use strict"; | ||
* @param variable the variable to check | ||
* @param target the type of node that is expected to be exported | ||
*/ | ||
@@ -291,0 +290,0 @@ function isMergableExported(variable) { |
@@ -230,2 +230,7 @@ "use strict"; | ||
break; | ||
case utils_1.AST_NODE_TYPES.PropertyDefinition: | ||
if (ancestor.typeAnnotation) { | ||
return true; | ||
} | ||
break; | ||
} | ||
@@ -232,0 +237,0 @@ ancestor = ancestor.parent; |
@@ -167,3 +167,3 @@ --- | ||
### Overrides | ||
### `overrides` | ||
@@ -316,3 +316,3 @@ There are three ways in which an override can be used. | ||
### Except specific methods | ||
### `ignoredMethodNames` | ||
@@ -319,0 +319,0 @@ If you want to ignore some specific methods, you can do it by specifying method names. Note that this option does not care for the context, and will ignore every method with these names, which could lead to it missing some cases. You should use this sparingly. |
@@ -25,7 +25,2 @@ --- | ||
// Should indicate that a number is returned | ||
export default function () { | ||
return 1; | ||
} | ||
// Should indicate that a string is returned | ||
@@ -49,12 +44,7 @@ export var arrowFn = () => 'test'; | ||
```ts | ||
// Function is not exported | ||
function test() { | ||
// A function with no return value (void) | ||
export function test(): void { | ||
return; | ||
} | ||
// A return value of type number | ||
export var fn = function (): number { | ||
return 1; | ||
}; | ||
// A return value of type string | ||
@@ -67,8 +57,13 @@ export var arrowFn = (): string => 'test'; | ||
// Class is not exported | ||
class Test { | ||
method() { | ||
export class Test { | ||
// A class method with no return value (void) | ||
method(): void { | ||
return; | ||
} | ||
} | ||
// The function does not apply because it is not an exported function. | ||
function test() { | ||
return; | ||
} | ||
``` | ||
@@ -75,0 +70,0 @@ |
@@ -53,19 +53,8 @@ --- | ||
This rule accepts a single object option with the following default configuration: | ||
### `allowSingleExtends` | ||
```json | ||
{ | ||
"@typescript-eslint/no-empty-interface": [ | ||
"error", | ||
{ | ||
"allowSingleExtends": false | ||
} | ||
] | ||
} | ||
``` | ||
`allowSingleExtends: true` will silence warnings about extending a single interface without adding additional members | ||
- `allowSingleExtends: true` will silence warnings about extending a single interface without adding additional members | ||
## When Not To Use It | ||
If you don't care about having empty/meaningless interfaces, then you will not need this rule. |
@@ -97,2 +97,9 @@ --- | ||
### `fixToUnknown` | ||
By default, this rule will not provide automatic ESLint _fixes_: only opt-in _suggestions_. | ||
Switching types to `unknown` is safer but is likely to cause additional type errors. | ||
Enabling `{ "fixToUnknown": true }` gives the rule an auto-fixer to replace `: any` with `: unknown`. | ||
### `ignoreRestArgs` | ||
@@ -99,0 +106,0 @@ |
@@ -20,2 +20,9 @@ --- | ||
This rule also reports when an Array containing Promises is created and not properly handled. The main way to resolve this is by using one of the Promise concurrency methods to create a single Promise, then handling that according to the procedure above. These methods include: | ||
- `Promise.all()`, | ||
- `Promise.allSettled()`, | ||
- `Promise.any()` | ||
- `Promise.race()` | ||
:::tip | ||
@@ -44,2 +51,4 @@ `no-floating-promises` only detects unhandled Promise _statements_. | ||
Promise.reject('value').finally(); | ||
[1, 2, 3].map(async x => x + 1); | ||
``` | ||
@@ -64,2 +73,4 @@ | ||
await Promise.reject('value').finally(() => {}); | ||
await Promise.all([1, 2, 3].map(async x => x + 1)); | ||
``` | ||
@@ -112,1 +123,5 @@ | ||
- [`no-misused-promises`](./no-misused-promises.md) | ||
## Further Reading | ||
- ["Using Promises" MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises). Note especially the sections on [Promise rejection events](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises#promise_rejection_events) and [Composition](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises#composition). |
@@ -9,7 +9,11 @@ --- | ||
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. | ||
A for-in loop (`for (const i in o)`) iterates over the properties of an Object. | ||
While it is legal to use for-in loops with array values, it is not common. There are several potential bugs with this: | ||
1. It iterates over all enumerable properties, including non-index ones and the entire prototype chain. For example, [`RegExp.prototype.exec`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec) returns an array with additional properties, and `for-in` will iterate over them. Some libraries or even your own code may add additional methods to `Array.prototype` (either as polyfill or as custom methods), and if not done properly, they may be iterated over as well. | ||
2. It skips holes in the array. While sparse arrays are rare and advised against, they are still possible and your code should be able to handle them. | ||
3. The "index" is returned as a string, not a number. This can be caught by TypeScript, but can still lead to subtle bugs. | ||
You may have confused for-in with for-of, which iterates over the elements of the array. If you actually need the index, use a regular `for` loop or the `forEach` method. | ||
## Examples | ||
@@ -16,0 +20,0 @@ |
@@ -47,2 +47,4 @@ --- | ||
### `checkNever` | ||
`checkNever: true` will suggest removing `void` when the argument has type `never`. | ||
@@ -49,0 +51,0 @@ |
@@ -20,3 +20,3 @@ --- | ||
### `"checksConditionals"` | ||
### `checksConditionals` | ||
@@ -77,6 +77,6 @@ If you don't want to check conditionals, you can configure the rule with `"checksConditionals": false`: | ||
### `"checksVoidReturn"` | ||
### `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: | ||
Likewise, if you don't want to check functions that return promises where a void return is | ||
expected, your configuration will look like this: | ||
@@ -187,3 +187,3 @@ ```json | ||
### `"checksSpreads"` | ||
### `checksSpreads` | ||
@@ -190,0 +190,0 @@ If you don't want to check object spreads, you can add this configuration: |
@@ -31,2 +31,24 @@ --- | ||
## Options | ||
### `allow` | ||
A array of strings. These strings will be compiled into regular expressions with the `u` flag and be used to test against the imported path. A common use case is to allow importing `package.json`. This is because `package.json` commonly lives outside of the TS root directory, so statically importing it would lead to root directory conflicts, especially with `resolveJsonModule` enabled. You can also use it to allow importing any JSON if your environment doesn't support JSON modules, or use it for other cases where `import` statements cannot work. | ||
With `{allow: ['/package\\.json$']}`: | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
console.log(require('../data.json').version); | ||
``` | ||
### ✅ Correct | ||
```ts | ||
console.log(require('../package.json').version); | ||
``` | ||
## When Not To Use It | ||
@@ -33,0 +55,0 @@ |
@@ -31,6 +31,4 @@ --- | ||
When set to `true`, the rule will ignore the case when you name a type the same as a variable. | ||
When set to `true`, the rule will ignore the case when you name a type the same as a variable. This is generally safe because you cannot use variables in type locations without a `typeof` operator, so there's little risk of confusion. | ||
TypeScript allows types and variables to shadow one-another. This is generally safe because you cannot use variables in type locations without a `typeof` operator, so there's little risk of confusion. | ||
Examples of **correct** code with `{ ignoreTypeValueShadow: true }`: | ||
@@ -40,13 +38,21 @@ | ||
type Foo = number; | ||
const Foo = 1; | ||
interface Bar { | ||
prop: number; | ||
} | ||
const Bar = 'test'; | ||
function f() { | ||
const Foo = 1; | ||
const Bar = 'test'; | ||
} | ||
``` | ||
:::note | ||
_Shadowing_ specifically refers to two identical identifiers that are in different, nested scopes. This is different from _redeclaration_, which is when two identical identifiers are in the same scope. Redeclaration is covered by the [`no-redeclare`](./no-redeclare.md) rule instead. | ||
::: | ||
### `ignoreFunctionTypeParameterNameValueShadow` | ||
When set to `true`, the rule will ignore the case when you name a function type argument the same as a variable. | ||
When set to `true`, the rule will ignore the case when you name a parameter in a function type the same as a variable. | ||
@@ -53,0 +59,0 @@ Each of a function type's arguments creates a value variable within the scope of the function type. This is done so that you can reference the type later using the `typeof` operator: |
@@ -36,2 +36,71 @@ --- | ||
### `allowDestructuring` | ||
It can sometimes be useful to destructure properties from a class instance, such as retrieving multiple properties from the instance in one of its methods. | ||
`allowDestructuring` allows those destructures and is `true` by default. | ||
You can explicitly disallow them by setting `allowDestructuring` to `false`. | ||
Examples of code for the `{ "allowDestructuring": false }` option: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts option='{ "allowDestructuring": false }' | ||
class ComponentLike { | ||
props: unknown; | ||
state: unknown; | ||
render() { | ||
const { props, state } = this; | ||
console.log(props); | ||
console.log(state); | ||
} | ||
} | ||
``` | ||
#### ✅ Correct | ||
```ts option='{ "allowDestructuring": false }' | ||
class ComponentLike { | ||
props: unknown; | ||
state: unknown; | ||
render() { | ||
console.log(this.props); | ||
console.log(this.state); | ||
} | ||
} | ||
``` | ||
### `allowedNames` | ||
`no-this-alias` can alternately be used to allow only a specific list of names as `this` aliases. | ||
We recommend against this except as a transitory step towards fixing all rule violations. | ||
Examples of code for the `{ "allowedNames": ["self"] }` option: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts option='{ "allowedNames": ["self"] }' | ||
class Example { | ||
method() { | ||
const that = this; | ||
} | ||
} | ||
``` | ||
#### ✅ Correct | ||
```ts option='{ "allowedNames": ["self"] }' | ||
class Example { | ||
method() { | ||
const self = this; | ||
} | ||
} | ||
``` | ||
## When Not To Use It | ||
@@ -38,0 +107,0 @@ |
@@ -23,4 +23,2 @@ --- | ||
```ts | ||
/*eslint @typescript-eslint/no-throw-literal: "error"*/ | ||
throw 'error'; | ||
@@ -57,15 +55,13 @@ | ||
```ts | ||
/*eslint @typescript-eslint/no-throw-literal: "error"*/ | ||
throw new Error(); | ||
throw new Error("error"); | ||
throw new Error('error'); | ||
const e = new Error("error"); | ||
const e = new Error('error'); | ||
throw e; | ||
try { | ||
throw new Error("error"); | ||
throw new Error('error'); | ||
} catch (e) { | ||
throw e; | ||
throw e; | ||
} | ||
@@ -82,4 +78,4 @@ | ||
const foo = { | ||
bar: new Error(); | ||
} | ||
bar: new Error(), | ||
}; | ||
throw foo.bar; | ||
@@ -89,3 +85,3 @@ | ||
// ... | ||
}; | ||
} | ||
throw new CustomError(); | ||
@@ -92,0 +88,0 @@ ``` |
@@ -104,5 +104,4 @@ --- | ||
See [#9998](https://github.com/microsoft/TypeScript/issues/9998) for details. | ||
We recommend using a [type assertion](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-assertions) in those cases. | ||
We recommend upcasting the variable with a [type assertion](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-assertions). | ||
```ts | ||
@@ -109,0 +108,0 @@ let condition = false as boolean; |
@@ -31,2 +31,24 @@ --- | ||
## Options | ||
### `allow` | ||
A array of strings. These strings will be compiled into regular expressions with the `u` flag and be used to test against the imported path. A common use case is to allow importing `package.json`. This is because `package.json` commonly lives outside of the TS root directory, so statically importing it would lead to root directory conflicts, especially with `resolveJsonModule` enabled. You can also use it to allow importing any JSON if your environment doesn't support JSON modules, or use it for other cases where `import` statements cannot work. | ||
With `{allow: ['/package\\.json$']}`: | ||
<!--tabs--> | ||
### ❌ Incorrect | ||
```ts | ||
const foo = require('../data.json'); | ||
``` | ||
### ✅ Correct | ||
```ts | ||
const foo = require('../package.json'); | ||
``` | ||
## When Not To Use It | ||
@@ -33,0 +55,0 @@ |
@@ -22,3 +22,3 @@ --- | ||
### `"allow"` | ||
### `allow` | ||
@@ -49,3 +49,3 @@ If you would like to ignore certain kinds of properties then you may pass an object containing `"allow"` as an array of any of the following options: | ||
### `"prefer"` | ||
### `prefer` | ||
@@ -52,0 +52,0 @@ By default, the rule prefers class property (`"class-property"`). |
@@ -64,4 +64,6 @@ --- | ||
- `allowBitwiseExpressions` set to `true` will allow you to use bitwise expressions in enum initializer (Default: `false`). | ||
### `allowBitwiseExpressions` | ||
When set to `true` will allow you to use bitwise expressions in enum initializer (default: `false`). | ||
Examples of code for the `{ "allowBitwiseExpressions": true }` option: | ||
@@ -68,0 +70,0 @@ |
@@ -170,2 +170,12 @@ --- | ||
### `allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing` | ||
If this is set to `false`, then the rule will error on every file whose `tsconfig.json` does _not_ have the `strictNullChecks` compiler option (or `strict`) set to `true`. | ||
Without `strictNullChecks`, TypeScript essentially erases `undefined` and `null` from the types. This means when this rule inspects the types from a variable, **it will not be able to tell that the variable might be `null` or `undefined`**, which essentially makes this rule useless. | ||
You should be using `strictNullChecks` to ensure complete type-safety in your codebase. | ||
If for some reason you cannot turn on `strictNullChecks`, but still want to use this rule - you can use this option to allow it - but know that the behavior of this rule is _undefined_ with the compiler option turned off. We will not accept bug reports if you are using this option. | ||
## When Not To Use It | ||
@@ -172,0 +182,0 @@ |
@@ -60,3 +60,3 @@ --- | ||
} | ||
const x = { foo(arg: string[]): void; }; | ||
const x = { foo(arg: string[]): void {} }; | ||
function foo(arg: string[]); | ||
@@ -95,3 +95,3 @@ type Foo = (arg: string[]) => void; | ||
function union(arg: readonly string[] | ReadonlyArray<number[]>) {} | ||
function union(arg: readonly string[] | ReadonlyArray<number>) {} | ||
@@ -110,4 +110,7 @@ function primitive1(arg: string) {} | ||
enum Foo { a, b } | ||
function enum(arg: Foo) {} | ||
enum Foo { | ||
a, | ||
b, | ||
} | ||
function enumArg(arg: Foo) {} | ||
@@ -125,3 +128,3 @@ function symb1(arg: symbol) {} | ||
} | ||
const x = { foo(arg: readonly string[]): void; }; | ||
const x = { foo(arg: readonly string[]): void {} }; | ||
function foo(arg: readonly string[]); | ||
@@ -128,0 +131,0 @@ type Foo = (arg: readonly string[]) => void; |
@@ -61,2 +61,70 @@ --- | ||
## Options | ||
### `allowAny` | ||
Whether to ignore functions that return `any` and `unknown`. | ||
If you want additional safety, consider turning this option off, as it makes the rule less able to catch incorrect Promise behaviors. | ||
Examples of code with `{ "allowAny": false }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts option='{ "allowAny": false }' | ||
const returnsAny = () => ({}) as any; | ||
``` | ||
#### ✅ Correct | ||
```ts option='{ "allowAny": false }' | ||
const returnsAny = async () => ({}) as any; | ||
``` | ||
### `allowedPromiseNames` | ||
For projects that use constructs other than the global built-in `Promise` for asynchronous code. | ||
This option allows specifying string names of classes or interfaces that cause a function to be checked as well. | ||
Examples of code with `{ "allowedPromiseNames": ["Bluebird"] }`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts option='{ "allowedPromiseNames": ["Bluebird"] }' | ||
import { Bluebird } from 'bluebird'; | ||
const returnsBluebird = () => new Bluebird(() => {}); | ||
``` | ||
#### ✅ Correct | ||
```ts option='{ "allowedPromiseNames": ["Bluebird"] }' | ||
import { Bluebird } from 'bluebird'; | ||
const returnsBluebird = async () => new Bluebird(() => {}); | ||
``` | ||
### `checkArrowFunctions` | ||
Whether to check arrow functions. | ||
`true` by default, but can be set to `false` to ignore them. | ||
### `checkFunctionDeclarations` | ||
Whether to check standalone function declarations. | ||
`true` by default, but can be set to `false` to ignore them. | ||
### `checkFunctionExpressions` | ||
Whether to check inline function expressions. | ||
`true` by default, but can be set to `false` to ignore them. | ||
### `checkMethodDeclarations` | ||
Whether to check methods on classes and object literals | ||
`true` by default, but can be set to `false` to ignore them. | ||
## When Not To Use It | ||
@@ -63,0 +131,0 @@ |
--- | ||
description: 'Require `Array#sort` calls to always provide a `compareFunction`.' | ||
description: 'Require `Array#sort` and `Array#toSorted` calls to always provide a `compareFunction`.' | ||
--- | ||
@@ -9,3 +9,3 @@ | ||
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)]. | ||
When called without a compare function, `Array#sort()` and `Array#toSorted()` 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)]. | ||
@@ -19,7 +19,7 @@ The result is that elements are sorted alphabetically, regardless of their type. | ||
This rule reports on any call to the `Array#sort()` method that doesn't provide a `compare` argument. | ||
This rule reports on any call to the sort methods that do not provide a `compare` argument. | ||
## Examples | ||
This rule aims to ensure all calls of the native `Array#sort` method provide a `compareFunction`, while ignoring calls to user-defined `sort` methods. | ||
This rule aims to ensure all calls of the native sort methods provide a `compareFunction`, while ignoring calls to user-defined methods. | ||
@@ -26,0 +26,0 @@ <!--tabs--> |
@@ -85,2 +85,42 @@ --- | ||
### `checkIntersections` | ||
Whether to check intersection types (`&`). | ||
Examples of code with `{ "checkIntersections": true }` (the default): | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts option='{ "checkIntersections": true }' | ||
type ExampleIntersection = B & A; | ||
``` | ||
#### ✅ Correct | ||
```ts option='{ "checkIntersections": true }' | ||
type ExampleIntersection = A & B; | ||
``` | ||
### `checkUnions` | ||
Whether to check union types (`|`). | ||
Examples of code with `{ "checkUnions": true }` (the default): | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts option='{ "checkUnions": true }' | ||
type ExampleUnion = B | A; | ||
``` | ||
#### ✅ Correct | ||
```ts option='{ "checkUnions": true }' | ||
type ExampleUnion = A | B; | ||
``` | ||
### `groupOrder` | ||
@@ -104,2 +144,18 @@ | ||
For example, configuring the rule with `{ "groupOrder": ["literal", "nullish" ]}`: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts option='{ "groupOrder": ["literal", "nullish" ]}' | ||
type ExampleGroup = null | 123; | ||
``` | ||
#### ✅ Correct | ||
```ts option='{ "groupOrder": ["literal", "nullish" ]}' | ||
type ExampleGroup = 123 | null; | ||
``` | ||
## When Not To Use It | ||
@@ -106,0 +162,0 @@ |
@@ -9,3 +9,3 @@ --- | ||
When working with union types or enums 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 or the enum). | ||
When working with union types or enums in TypeScript, it's common to want to write a `switch` statement intended to contain a `case` for each possible type in the union or the enum. | ||
However, if the union type or the enum changes, it's easy to forget to modify the cases to account for any new types. | ||
@@ -15,4 +15,43 @@ | ||
There is also an option to check the exhaustiveness of switches on non-union types by requiring a default clause. | ||
## Options | ||
### `allowDefaultCaseForExhaustiveSwitch` | ||
Defaults to true. If set to false, this rule will also report when a `switch` statement has a case for everything in a union and _also_ contains a `default` case. Thus, by setting this option to false, the rule becomes stricter. | ||
When a `switch` statement over a union type is exhaustive, a final `default` case would be a form of dead code. | ||
Additionally, if a new value is added to the union type, a `default` would prevent the `switch-exhaustiveness-check` rule from reporting on the new case not being handled in the `switch` statement. | ||
#### `allowDefaultCaseForExhaustiveSwitch` Caveats | ||
It can sometimes be useful to include a redundant `default` case on an exhaustive `switch` statement if it's possible for values to have types not represented by the union type. | ||
For example, in applications that can have version mismatches between clients and servers, it's possible for a server running a newer software version to send a value not recognized by the client's older typings. | ||
If your project has a small number of intentionally redundant `default` cases, you might want to use an [inline ESLint disable comment](https://eslint.org/docs/latest/use/configure/rules#using-configuration-comments-1) for each of them. | ||
If your project has many intentionally redundant `default` cases, you may want to disable `allowDefaultCaseForExhaustiveSwitch` and use the [`default-case` core ESLint rule](https://eslint.org/docs/latest/rules/default-case) along with [a `satisfies never` check](https://www.typescriptlang.org/play?#code/C4TwDgpgBAYgTgVwJbCgXigcgIZjAGwkygB8sAjbAO2u0wG4AoRgMwSoGNgkB7KqBAGcI8ZMAAULRCgBcsacACUcwcDhIqAcygBvRlCiCA7ig4ALKJIWLd+g1A7ZhWXASJy99+3AjAEcfhw8QgApZA4iJi8AX2YvR2dMShoaTA87Lx8-AIpaGjCkCIYMqFiSgBMIFmwEfGB0rwMpMUNsbkEWJAhBKCoIADcIOCjGrP9A9gBrKh4jKgKikYNY5cZYoA). | ||
### `requireDefaultForNonUnion` | ||
Defaults to false. It set to true, this rule will also report when a `switch` statement switches over a non-union type (like a `number` or `string`, for example) and that `switch` statement does not have a `default` case. Thus, by setting this option to true, the rule becomes stricter. | ||
This is generally desirable so that `number` and `string` switches will be subject to the same exhaustive checks that your other switches are. | ||
Examples of additional **incorrect** code for this rule with `{ requireDefaultForNonUnion: true }`: | ||
```ts option='{ "requireDefaultForNonUnion": true }' showPlaygroundButton | ||
const value: number = Math.floor(Math.random() * 3); | ||
switch (value) { | ||
case 0: | ||
return 0; | ||
case 1: | ||
return 1; | ||
} | ||
``` | ||
Since `value` is a non-union type it requires the switch case to have a default clause only with `requireDefaultForNonUnion` enabled. | ||
<!--/tabs--> | ||
## Examples | ||
@@ -186,25 +225,4 @@ | ||
## Options | ||
### `requireDefaultForNonUnion` | ||
Examples of additional **incorrect** code for this rule with `{ requireDefaultForNonUnion: true }`: | ||
```ts option='{ "requireDefaultForNonUnion": true }' showPlaygroundButton | ||
const value: number = Math.floor(Math.random() * 3); | ||
switch (value) { | ||
case 0: | ||
return 0; | ||
case 1: | ||
return 1; | ||
} | ||
``` | ||
Since `value` is a non-union type it requires the switch case to have a default clause only with `requireDefaultForNonUnion` enabled. | ||
<!--/tabs--> | ||
## When Not To Use It | ||
If you don't frequently `switch` over union types or enums with many parts, or intentionally wish to leave out some parts, this rule may not be for you. |
@@ -11,44 +11,92 @@ --- | ||
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. | ||
This rule reports on the use of `/// <reference lib="..." />`, `/// <reference path="..." />`, or `/// <reference types="..." />` directives. | ||
## Options | ||
With `{ "path": "never", "types": "never", "lib": "never" }` options set, the following will all be **incorrect** usage: | ||
Any number of the three kinds of references can be specified as an option. | ||
Specifying `'always'` disables this lint rule for that kind of reference. | ||
```ts option='{ "path": "never", "types": "never", "lib": "never" }' showPlaygroundButton | ||
/// <reference path="foo" /> | ||
/// <reference types="bar" /> | ||
/// <reference lib="baz" /> | ||
### `lib` | ||
When set to `'never'`, bans `/// <reference lib="..." />` and enforces using an `import` instead: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts option='{ "lib": "never" }' | ||
/// <reference lib="code" /> | ||
globalThis.value; | ||
``` | ||
Examples of **incorrect** code for the `{ "types": "prefer-import" }` option. Note that these are only errors when **both** styles are used for the **same** module: | ||
#### ✅ Correct | ||
```ts option='{ "types": "prefer-import" }' showPlaygroundButton | ||
/// <reference types="foo" /> | ||
import * as foo from 'foo'; | ||
```ts option='{ "lib": "never" }' | ||
import { value } from 'code'; | ||
``` | ||
```ts option='{ "types": "prefer-import" }' showPlaygroundButton | ||
/// <reference types="foo" /> | ||
import foo = require('foo'); | ||
### `path` | ||
When set to `'never'`, bans `/// <reference path="..." />` and enforces using an `import` instead: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts option='{ "path": "never" }' | ||
/// <reference path="code" /> | ||
globalThis.value; | ||
``` | ||
With `{ "path": "always", "types": "always", "lib": "always" }` options set, the following will all be **correct** usage: | ||
#### ✅ Correct | ||
```ts option='{ "path": "always", "types": "always", "lib": "always" }' showPlaygroundButton | ||
/// <reference path="foo" /> | ||
/// <reference types="bar" /> | ||
/// <reference lib="baz" /> | ||
```ts option='{ "path": "never" }' | ||
import { value } from 'code'; | ||
``` | ||
Examples of **correct** code for the `{ "types": "prefer-import" }` option: | ||
### `types` | ||
```ts option='{ "types": "prefer-import" }' showPlaygroundButton | ||
import * as foo from 'foo'; | ||
When set to `'never'`, bans `/// <reference types="..." />` and enforces using an `import` instead: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts option='{ "types": "never" }' | ||
/// <reference types="code" /> | ||
globalThis.value; | ||
``` | ||
```ts option='{ "types": "prefer-import" }' showPlaygroundButton | ||
import foo = require('foo'); | ||
#### ✅ Correct | ||
```ts option='{ "types": "never" }' | ||
import { value } from 'code'; | ||
``` | ||
<!-- /tabs --> | ||
The `types` option may alternately be given a `"prefer-import"` value. | ||
Doing so indicates the rule should only report if there is already an `import` from the same location: | ||
<!--tabs--> | ||
#### ❌ Incorrect | ||
```ts option='{ "types": "prefer-import" }' | ||
/// <reference types="code" /> | ||
import { valueA } from 'code'; | ||
globalThis.valueB; | ||
``` | ||
#### ✅ Correct | ||
```ts option='{ "types": "prefer-import" }' | ||
import { valueA, valueB } from 'code'; | ||
``` | ||
## When Not To Use It | ||
@@ -55,0 +103,0 @@ |
{ | ||
"name": "@typescript-eslint/eslint-plugin", | ||
"version": "6.13.2", | ||
"version": "6.18.1", | ||
"description": "TypeScript plugin for ESLint", | ||
@@ -60,6 +60,6 @@ "files": [ | ||
"@eslint-community/regexpp": "^4.5.1", | ||
"@typescript-eslint/scope-manager": "6.13.2", | ||
"@typescript-eslint/type-utils": "6.13.2", | ||
"@typescript-eslint/utils": "6.13.2", | ||
"@typescript-eslint/visitor-keys": "6.13.2", | ||
"@typescript-eslint/scope-manager": "6.18.1", | ||
"@typescript-eslint/type-utils": "6.18.1", | ||
"@typescript-eslint/utils": "6.18.1", | ||
"@typescript-eslint/visitor-keys": "6.18.1", | ||
"debug": "^4.3.4", | ||
@@ -77,4 +77,4 @@ "graphemer": "^1.4.0", | ||
"@types/natural-compare": "*", | ||
"@typescript-eslint/rule-schema-to-typescript-types": "6.13.2", | ||
"@typescript-eslint/rule-tester": "6.13.2", | ||
"@typescript-eslint/rule-schema-to-typescript-types": "6.18.1", | ||
"@typescript-eslint/rule-tester": "6.18.1", | ||
"ajv": "^6.12.6", | ||
@@ -107,4 +107,3 @@ "chalk": "^5.3.0", | ||
"url": "https://opencollective.com/typescript-eslint" | ||
}, | ||
"gitHead": "cc2c6d3a4a515f7ccc23f10421a8db9c88345876" | ||
} | ||
} |
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
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
2558724
509
30346
+ Added@typescript-eslint/scope-manager@6.18.1(transitive)
+ Added@typescript-eslint/type-utils@6.18.1(transitive)
+ Added@typescript-eslint/types@6.18.1(transitive)
+ Added@typescript-eslint/typescript-estree@6.18.1(transitive)
+ Added@typescript-eslint/utils@6.18.1(transitive)
+ Added@typescript-eslint/visitor-keys@6.18.1(transitive)
- Removed@typescript-eslint/scope-manager@6.13.2(transitive)
- Removed@typescript-eslint/type-utils@6.13.2(transitive)
- Removed@typescript-eslint/types@6.13.2(transitive)
- Removed@typescript-eslint/typescript-estree@6.13.2(transitive)
- Removed@typescript-eslint/utils@6.13.2(transitive)
- Removed@typescript-eslint/visitor-keys@6.13.2(transitive)