From 4ef19fabd01c98d2859ef85b3b50272ab143e52f Mon Sep 17 00:00:00 2001 From: Noel Kim Date: Tue, 9 Sep 2025 12:14:46 +0900 Subject: [PATCH 01/27] feat(rules): Implement `strict_boolean_expressions` --- .gitignore | 4 +- cmd/tsgolint/main.go | 2 + .../strict_boolean_expressions.go | 516 +++++++ .../strict_boolean_expressions_test.go | 1342 +++++++++++++++++ 4 files changed, 1863 insertions(+), 1 deletion(-) create mode 100644 internal/rules/strict_boolean_expressions/strict_boolean_expressions.go create mode 100644 internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go diff --git a/.gitignore b/.gitignore index 3663a5a5..7793d79d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ /tsgolint internal/collections/ e2e/node_modules/ -node_modules/ \ No newline at end of file +node_modules/ + +.idea/ diff --git a/cmd/tsgolint/main.go b/cmd/tsgolint/main.go index 937ff75a..7acf718e 100644 --- a/cmd/tsgolint/main.go +++ b/cmd/tsgolint/main.go @@ -57,6 +57,7 @@ import ( "github.com/typescript-eslint/tsgolint/internal/rules/restrict_plus_operands" "github.com/typescript-eslint/tsgolint/internal/rules/restrict_template_expressions" "github.com/typescript-eslint/tsgolint/internal/rules/return_await" + "github.com/typescript-eslint/tsgolint/internal/rules/strict_boolean_expressions" "github.com/typescript-eslint/tsgolint/internal/rules/switch_exhaustiveness_check" "github.com/typescript-eslint/tsgolint/internal/rules/unbound_method" "github.com/typescript-eslint/tsgolint/internal/rules/use_unknown_in_catch_callback_variable" @@ -156,6 +157,7 @@ var allRules = []rule.Rule{ restrict_plus_operands.RestrictPlusOperandsRule, restrict_template_expressions.RestrictTemplateExpressionsRule, return_await.ReturnAwaitRule, + strict_boolean_expressions.StrictBooleanExpressionsRule, switch_exhaustiveness_check.SwitchExhaustivenessCheckRule, unbound_method.UnboundMethodRule, use_unknown_in_catch_callback_variable.UseUnknownInCatchCallbackVariableRule, diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go new file mode 100644 index 00000000..500f2927 --- /dev/null +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go @@ -0,0 +1,516 @@ +package strict_boolean_expressions + +import ( + "github.com/microsoft/typescript-go/shim/ast" + "github.com/microsoft/typescript-go/shim/checker" + "github.com/microsoft/typescript-go/shim/core" + "github.com/typescript-eslint/tsgolint/internal/rule" + "github.com/typescript-eslint/tsgolint/internal/utils" +) + +type StrictBooleanExpressionsOptions struct { + AllowAny *bool + AllowNullableBoolean *bool + AllowNullableNumber *bool + AllowNullableString *bool + AllowNullableEnum *bool + AllowNullableObject *bool + AllowString *bool + AllowNumber *bool + AllowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing *bool +} + +func buildUnexpectedAny() rule.RuleMessage { + return rule.RuleMessage{ + Id: "unexpectedAny", + Description: "Unexpected any value in conditional. An explicit comparison or type cast is required.", + } +} + +func buildUnexpectedNullableBoolean() rule.RuleMessage { + return rule.RuleMessage{ + Id: "unexpectedNullableBoolean", + Description: "Unexpected nullable boolean value in conditional. Please handle the nullish case explicitly.", + } +} + +func buildUnexpectedNullableString() rule.RuleMessage { + return rule.RuleMessage{ + Id: "unexpectedNullableString", + Description: "Unexpected nullable string value in conditional. Please handle the nullish case explicitly.", + } +} + +func buildUnexpectedNullableNumber() rule.RuleMessage { + return rule.RuleMessage{ + Id: "unexpectedNullableNumber", + Description: "Unexpected nullable number value in conditional. Please handle the nullish case explicitly.", + } +} + +func buildUnexpectedNullableObject() rule.RuleMessage { + return rule.RuleMessage{ + Id: "unexpectedNullableObject", + Description: "Unexpected nullable object value in conditional. An explicit null check is required.", + } +} + +func buildUnexpectedNullish() rule.RuleMessage { + return rule.RuleMessage{ + Id: "unexpectedNullish", + Description: "Unexpected nullish value in conditional. An explicit null check is required.", + } +} + +func buildUnexpectedString() rule.RuleMessage { + return rule.RuleMessage{ + Id: "unexpectedString", + Description: "Unexpected string value in conditional. An explicit empty string check is required.", + } +} + +func buildUnexpectedNumber() rule.RuleMessage { + return rule.RuleMessage{ + Id: "unexpectedNumber", + Description: "Unexpected number value in conditional. An explicit zero/NaN check is required.", + } +} + +func buildUnexpectedObjectContext() rule.RuleMessage { + return rule.RuleMessage{ + Id: "unexpectedObjectContext", + Description: "Unexpected object value in conditional. The condition is always true.", + } +} + +func buildUnexpectedMixedCondition() rule.RuleMessage { + return rule.RuleMessage{ + Id: "unexpectedMixedCondition", + Description: "Unexpected mixed type in conditional. The constituent types do not have a best common type.", + } +} + +func buildNoStrictNullCheck() rule.RuleMessage { + return rule.RuleMessage{ + Id: "msgNoStrictNullCheck", + Description: "This rule requires the `strictNullChecks` compiler option to be turned on to function correctly.", + } +} + +var StrictBooleanExpressionsRule = rule.Rule{ + Name: "strict-boolean-expressions", + Run: func(ctx rule.RuleContext, options any) rule.RuleListeners { + opts, ok := options.(StrictBooleanExpressionsOptions) + if !ok { + opts = StrictBooleanExpressionsOptions{} + } + + if opts.AllowAny == nil { + opts.AllowAny = utils.Ref(false) + } + if opts.AllowNullableBoolean == nil { + opts.AllowNullableBoolean = utils.Ref(false) + } + if opts.AllowNullableNumber == nil { + opts.AllowNullableNumber = utils.Ref(false) + } + if opts.AllowNullableString == nil { + opts.AllowNullableString = utils.Ref(false) + } + if opts.AllowNullableEnum == nil { + opts.AllowNullableEnum = utils.Ref(false) + } + if opts.AllowNullableObject == nil { + opts.AllowNullableObject = utils.Ref(false) + } + if opts.AllowString == nil { + opts.AllowString = utils.Ref(true) + } + if opts.AllowNumber == nil { + opts.AllowNumber = utils.Ref(true) + } + if opts.AllowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing == nil { + opts.AllowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing = utils.Ref(false) + } + + compilerOptions := ctx.Program.Options() + if !*opts.AllowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing && + !utils.IsStrictCompilerOptionEnabled(compilerOptions, compilerOptions.StrictNullChecks) { + ctx.ReportRange( + core.NewTextRange(0, 0), + buildNoStrictNullCheck(), + ) + return rule.RuleListeners{} + } + + checkNode := func(node *ast.Node) { + nodeType := ctx.TypeChecker.GetTypeAtLocation(node) + checkCondition(ctx, node, nodeType, opts) + } + + return rule.RuleListeners{ + ast.KindIfStatement: func(node *ast.Node) { + ifStmt := node.AsIfStatement() + checkNode(ifStmt.Expression) + }, + ast.KindWhileStatement: func(node *ast.Node) { + whileStmt := node.AsWhileStatement() + checkNode(whileStmt.Expression) + }, + ast.KindDoStatement: func(node *ast.Node) { + doStmt := node.AsDoStatement() + checkNode(doStmt.Expression) + }, + ast.KindForStatement: func(node *ast.Node) { + forStmt := node.AsForStatement() + if forStmt.Condition != nil { + checkNode(forStmt.Condition) + } + }, + ast.KindConditionalExpression: func(node *ast.Node) { + condExpr := node.AsConditionalExpression() + checkNode(condExpr.Condition) + }, + ast.KindBinaryExpression: func(node *ast.Node) { + binExpr := node.AsBinaryExpression() + switch binExpr.OperatorToken.Kind { + case ast.KindAmpersandAmpersandToken, ast.KindBarBarToken: + checkNode(binExpr.Left) + } + }, + ast.KindCallExpression: func(node *ast.Node) { + callExpr := node.AsCallExpression() + + //assertedArgument := findTruthinessAssertedArgument(ctx.TypeChecker, callExpr) + //if assertedArgument != nil { + // checkNode(assertedArgument) + //} + + if callExpr.Expression != nil && callExpr.Expression.Kind == ast.KindPropertyAccessExpression { + propAccess := callExpr.Expression.AsPropertyAccessExpression() + if propAccess == nil { + return + } + methodName := propAccess.Name().Text() + + switch methodName { + case "filter", "find", "some", "every", "findIndex", "findLast", "findLastIndex": + if callExpr.Arguments != nil && len(callExpr.Arguments.Nodes) > 0 { + arg := callExpr.Arguments.Nodes[0] + if arg != nil && (arg.Kind == ast.KindArrowFunction || arg.Kind == ast.KindFunctionExpression) { + funcType := ctx.TypeChecker.GetTypeAtLocation(arg) + signatures := ctx.TypeChecker.GetCallSignatures(funcType) + for _, signature := range signatures { + returnType := ctx.TypeChecker.GetReturnTypeOfSignature(signature) + typeFlags := checker.Type_flags(returnType) + if typeFlags&checker.TypeFlagsTypeParameter != 0 { + constraint := ctx.TypeChecker.GetConstraintOfTypeParameter(returnType) + if constraint != nil { + returnType = constraint + } + } + + if returnType != nil && !isBooleanType(returnType) { + checkCondition(ctx, node, returnType, opts) + } + } + } + } + } + } + }, + ast.KindPrefixUnaryExpression: func(node *ast.Node) { + unaryExpr := node.AsPrefixUnaryExpression() + if unaryExpr.Operator == ast.KindExclamationToken { + checkNode(unaryExpr.Operand) + } + }, + } + }, +} + +// TODO: Not exported TypePredicate struct variables +//func findTruthinessAssertedArgument(typeChecker *checker.Checker, callExpr *ast.CallExpression) *ast.Node { +// var checkableArguments []*ast.Node +// for _, argument := range callExpr.Arguments.Nodes { +// if argument.Kind == ast.KindSpreadElement { +// continue +// } +// checkableArguments = append(checkableArguments, argument) +// } +// if len(checkableArguments) == 0 { +// return nil +// } +// node := callExpr.AsNode() +// signature := typeChecker.GetResolvedSignature(node) +// if signature == nil { +// return nil +// } +// firstTypePredicateResult := typeChecker.GetTypePredicateOfSignature(signature) +// if firstTypePredicateResult == nil { +// return nil +// } +// if firstTypePredicateResult.kind != checker.TypePredicateKindAssertsIdentifier || +// firstTypePredicateResult.type == nil { +// return nil +// } +// return checkableArguments[firstTypePredicateResult.parameterIndex] +//} + +// Type analysis types +type typeVariant int + +const ( + typeVariantNullish typeVariant = iota + typeVariantBoolean + typeVariantString + typeVariantNumber + typeVariantBigInt + typeVariantObject + typeVariantAny + typeVariantUnknown + typeVariantNever + typeVariantMixed + typeVariantGeneric +) + +type typeInfo struct { + variant typeVariant + isNullable bool + isTruthy bool + types []*checker.Type + isUnion bool + isIntersection bool + isEnum bool +} + +func analyzeType(typeChecker *checker.Checker, t *checker.Type) typeInfo { + info := typeInfo{ + types: []*checker.Type{t}, + } + + if utils.IsUnionType(t) { + info.isUnion = true + parts := utils.UnionTypeParts(t) + variants := make(map[typeVariant]bool) + hasNullish := false + hasEnum := false + + for _, part := range parts { + partInfo := analyzeTypePart(typeChecker, part) + variants[partInfo.variant] = true + if partInfo.variant == typeVariantNullish { + hasNullish = true + } + if partInfo.isEnum { + hasEnum = true + } + } + + info.isNullable = hasNullish + info.isEnum = hasEnum + + if len(variants) == 1 { + for v := range variants { + info.variant = v + } + } else if len(variants) == 2 && hasNullish { + for v := range variants { + if v != typeVariantNullish { + info.variant = v + break + } + } + } else { + info.variant = typeVariantMixed + } + + return info + } + + if utils.IsIntersectionType(t) { + info.isIntersection = true + info.variant = typeVariantObject + return info + } + + return analyzeTypePart(typeChecker, t) +} + +func analyzeTypePart(_ *checker.Checker, t *checker.Type) typeInfo { + info := typeInfo{} + flags := checker.Type_flags(t) + + if flags&checker.TypeFlagsTypeParameter != 0 { + info.variant = typeVariantGeneric + return info + } + + if flags&checker.TypeFlagsAny != 0 { + info.variant = typeVariantAny + return info + } + + if flags&checker.TypeFlagsUnknown != 0 { + info.variant = typeVariantUnknown + return info + } + + if flags&checker.TypeFlagsNever != 0 { + info.variant = typeVariantNever + return info + } + + if flags&(checker.TypeFlagsNull|checker.TypeFlagsUndefined|checker.TypeFlagsVoid) != 0 { + info.variant = typeVariantNullish + return info + } + + if flags&(checker.TypeFlagsBoolean|checker.TypeFlagsBooleanLiteral) != 0 { + info.variant = typeVariantBoolean + return info + } + + if flags&(checker.TypeFlagsString|checker.TypeFlagsStringLiteral) != 0 { + info.variant = typeVariantString + if flags&checker.TypeFlagsStringLiteral != 0 && t.AsLiteralType().Value() != "" { + info.isTruthy = true + } + return info + } + + if flags&(checker.TypeFlagsNumber|checker.TypeFlagsNumberLiteral) != 0 { + info.variant = typeVariantNumber + if flags&checker.TypeFlagsNumberLiteral != 0 && t.AsLiteralType().Value() != 0 { + info.isTruthy = true + } + return info + } + + if flags&(checker.TypeFlagsEnum|checker.TypeFlagsEnumLiteral) != 0 { + if flags&checker.TypeFlagsStringLiteral != 0 { + info.variant = typeVariantString + } else { + info.variant = typeVariantNumber + } + info.isEnum = true + return info + } + + if flags&(checker.TypeFlagsBigInt|checker.TypeFlagsBigIntLiteral) != 0 { + info.variant = typeVariantBigInt + if flags&checker.TypeFlagsBigIntLiteral != 0 && t.AsLiteralType().Value() != 0 { + info.isTruthy = true + } + return info + } + + if flags&(checker.TypeFlagsESSymbol|checker.TypeFlagsUniqueESSymbol) != 0 { + info.variant = typeVariantObject + return info + } + + if flags&checker.TypeFlagsObject != 0 || + flags&checker.TypeFlagsNonPrimitive != 0 || + utils.IsObjectType(t) { + info.variant = typeVariantObject + return info + } + + info.variant = typeVariantMixed + return info +} + +func checkCondition(ctx rule.RuleContext, node *ast.Node, t *checker.Type, opts StrictBooleanExpressionsOptions) { + if isBooleanType(t) { + return + } + + info := analyzeType(ctx.TypeChecker, t) + + switch info.variant { + case typeVariantAny, typeVariantUnknown, typeVariantGeneric: + if !*opts.AllowAny { + ctx.ReportNode(node, buildUnexpectedAny()) + } + return + case typeVariantNever: + return + case typeVariantNullish: + ctx.ReportNode(node, buildUnexpectedNullish()) + case typeVariantString: + // Known edge case: truthy primitives and nullish values are always valid boolean expressions + if *opts.AllowString && info.isNullable && info.isTruthy { + return + } + + if info.isNullable { + if info.isEnum { + if !*opts.AllowNullableEnum { + ctx.ReportNode(node, buildUnexpectedNullableString()) + } + } else { + if !*opts.AllowNullableString { + ctx.ReportNode(node, buildUnexpectedNullableString()) + } + } + } else if !*opts.AllowString { + ctx.ReportNode(node, buildUnexpectedString()) + } + case typeVariantNumber: + // Known edge case: truthy primitives and nullish values are always valid boolean expressions + if *opts.AllowNumber && info.isNullable && info.isTruthy { + return + } + + if info.isNullable { + if info.isEnum { + if !*opts.AllowNullableEnum { + ctx.ReportNode(node, buildUnexpectedNullableNumber()) + } + } else { + if !*opts.AllowNullableNumber { + ctx.ReportNode(node, buildUnexpectedNullableNumber()) + } + } + } else if !*opts.AllowNumber { + ctx.ReportNode(node, buildUnexpectedNumber()) + } + case typeVariantBoolean: + if info.isNullable && !*opts.AllowNullableBoolean { + ctx.ReportNode(node, buildUnexpectedNullableBoolean()) + } + case typeVariantObject: + if info.isNullable && !*opts.AllowNullableObject { + ctx.ReportNode(node, buildUnexpectedNullableObject()) + } else if !info.isNullable { + ctx.ReportNode(node, buildUnexpectedObjectContext()) + } + case typeVariantMixed: + ctx.ReportNode(node, buildUnexpectedMixedCondition()) + case typeVariantBigInt: + if info.isNullable && !*opts.AllowNullableNumber { + ctx.ReportNode(node, buildUnexpectedNullableNumber()) + } else if !info.isNullable && !*opts.AllowNumber { + ctx.ReportNode(node, buildUnexpectedNumber()) + } + } +} + +func isBooleanType(t *checker.Type) bool { + flags := checker.Type_flags(t) + + if flags&(checker.TypeFlagsBoolean|checker.TypeFlagsBooleanLiteral) != 0 { + if utils.IsUnionType(t) { + for _, part := range utils.UnionTypeParts(t) { + partFlags := checker.Type_flags(part) + if partFlags&(checker.TypeFlagsNull|checker.TypeFlagsUndefined|checker.TypeFlagsVoid) != 0 { + return false + } + } + } + return true + } + + return false +} diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go new file mode 100644 index 00000000..df301a28 --- /dev/null +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go @@ -0,0 +1,1342 @@ +package strict_boolean_expressions + +import ( + "testing" + + "github.com/typescript-eslint/tsgolint/internal/rule_tester" + "github.com/typescript-eslint/tsgolint/internal/rules/fixtures" + "github.com/typescript-eslint/tsgolint/internal/utils" +) + +func TestStrictBooleanExpressionsRule(t *testing.T) { + // Test with strictNullChecks enabled (in fixtures/tsconfig.json) + rule_tester.RunRuleTester(fixtures.GetRootDir(), "tsconfig.json", t, &StrictBooleanExpressionsRule, []rule_tester.ValidTestCase{ + // ============================================ + // BOOLEAN TYPES - Always Valid + // ============================================ + + // Boolean literals + {Code: `true ? 'a' : 'b';`}, + {Code: `false ? 'a' : 'b';`}, + {Code: `if (true) {}`}, + {Code: `if (false) {}`}, + {Code: `while (true) {}`}, + {Code: `do {} while (true);`}, + {Code: `for (; true; ) {}`}, + {Code: `!true;`}, + + // Boolean variables + {Code: `const b = true; if (b) {}`}, + {Code: `const b = false; if (!b) {}`}, + {Code: `declare const b: boolean; if (b) {}`}, + + // Boolean expressions + {Code: `if (true && false) {}`}, + {Code: `if (true || false) {}`}, + {Code: `true && 'a';`}, + {Code: `false || 'a';`}, + + // Comparison operators return boolean + {Code: `1 > 2 ? 'a' : 'b';`}, + {Code: `1 < 2 ? 'a' : 'b';`}, + {Code: `1 >= 2 ? 'a' : 'b';`}, + {Code: `1 <= 2 ? 'a' : 'b';`}, + {Code: `1 == 2 ? 'a' : 'b';`}, + {Code: `1 != 2 ? 'a' : 'b';`}, + {Code: `1 === 2 ? 'a' : 'b';`}, + {Code: `1 !== 2 ? 'a' : 'b';`}, + + // Type guards + {Code: `declare const x: string | number; if (typeof x === 'string') {}`}, + {Code: `declare const x: any; if (x instanceof Error) {}`}, + {Code: `declare const x: any; if ('prop' in x) {}`}, + + // Function returning boolean + {Code: `function test(): boolean { return true; } if (test()) {}`}, + {Code: `declare function test(): boolean; if (test()) {}`}, + + // ============================================ + // STRING TYPES - Valid with Default Options + // ============================================ + + {Code: `'' ? 'a' : 'b';`}, + {Code: `'foo' ? 'a' : 'b';`}, + {Code: "`` ? 'a' : 'b';"}, + {Code: "`foo` ? 'a' : 'b';"}, + {Code: "`foo${bar}` ? 'a' : 'b';"}, + {Code: `if ('') {}`}, + {Code: `if ('foo') {}`}, + {Code: `while ('') {}`}, + {Code: `do {} while ('foo');`}, + {Code: `for (; 'foo'; ) {}`}, + {Code: `!!'foo';`}, + {Code: `declare const s: string; if (s) {}`}, + + // String with logical operators + {Code: `'' || 'foo';`}, + {Code: `'foo' && 'bar';`}, + {Code: `declare const s: string; s || 'default';`}, + + // ============================================ + // NUMBER TYPES - Valid with Default Options + // ============================================ + + {Code: `0 ? 'a' : 'b';`}, + {Code: `1 ? 'a' : 'b';`}, + {Code: `-1 ? 'a' : 'b';`}, + {Code: `0.5 ? 'a' : 'b';`}, + {Code: `NaN ? 'a' : 'b';`}, + {Code: `if (0) {}`}, + {Code: `if (1) {}`}, + {Code: `while (0) {}`}, + {Code: `do {} while (1);`}, + {Code: `for (; 1; ) {}`}, + {Code: `declare const n: number; if (n) {}`}, + + // Number with logical operators + {Code: `0 || 1;`}, + {Code: `1 && 2;`}, + {Code: `declare const n: number; n || 0;`}, + + // BigInt + {Code: `0n ? 'a' : 'b';`}, + {Code: `1n ? 'a' : 'b';`}, + {Code: `if (0n) {}`}, + {Code: `if (1n) {}`}, + {Code: `declare const b: bigint; if (b) {}`}, + + // ============================================ + // OBJECT TYPES in logical operators + // ============================================ + + // Note: Objects in logical operators are treated as boolean conditions + // and will be flagged as errors (always truthy) unless in the right side + // Right side of logical operators is not checked as a boolean condition + {Code: `'foo' || ({});`}, // Object on right side is OK + {Code: `false || [];`}, // Array on right side is OK + {Code: `(false && true) || [];`}, // Array on right side is OK + + // ============================================ + // ANY TYPE - Valid with Option + // ============================================ + + { + Code: `declare const x: any; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowAny: utils.Ref(true), + }, + }, + { + Code: `declare const x: any; x ? 'a' : 'b';`, + Options: StrictBooleanExpressionsOptions{ + AllowAny: utils.Ref(true), + }, + }, + { + Code: `declare const x: any; x && 'a';`, + Options: StrictBooleanExpressionsOptions{ + AllowAny: utils.Ref(true), + }, + }, + { + Code: `declare const x: any; x || 'a';`, + Options: StrictBooleanExpressionsOptions{ + AllowAny: utils.Ref(true), + }, + }, + { + Code: `declare const x: any; !x;`, + Options: StrictBooleanExpressionsOptions{ + AllowAny: utils.Ref(true), + }, + }, + + // ============================================ + // UNKNOWN TYPE - Valid with Option + // ============================================ + + { + Code: `declare const x: unknown; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowAny: utils.Ref(true), + }, + }, + { + Code: `declare const x: unknown; x ? 'a' : 'b';`, + Options: StrictBooleanExpressionsOptions{ + AllowAny: utils.Ref(true), + }, + }, + { + Code: `declare const x: unknown; x && 'a';`, + Options: StrictBooleanExpressionsOptions{ + AllowAny: utils.Ref(true), + }, + }, + { + Code: `declare const x: unknown; x || 'a';`, + Options: StrictBooleanExpressionsOptions{ + AllowAny: utils.Ref(true), + }, + }, + { + Code: `declare const x: unknown; !x;`, + Options: StrictBooleanExpressionsOptions{ + AllowAny: utils.Ref(true), + }, + }, + + // ============================================ + // NULLABLE BOOLEAN - Valid with Option + // ============================================ + + { + Code: `declare const x: boolean | null; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableBoolean: utils.Ref(true), + }, + }, + { + Code: `declare const x: boolean | undefined; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableBoolean: utils.Ref(true), + }, + }, + { + Code: `declare const x: boolean | null | undefined; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableBoolean: utils.Ref(true), + }, + }, + { + Code: `declare const x: true | null; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableBoolean: utils.Ref(true), + }, + }, + { + Code: `declare const x: false | undefined; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableBoolean: utils.Ref(true), + }, + }, + + // ============================================ + // NULLABLE STRING - Valid with Option + // ============================================ + + { + Code: `declare const x: string | null; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableString: utils.Ref(true), + }, + }, + { + Code: `declare const x: string | undefined; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableString: utils.Ref(true), + }, + }, + { + Code: `declare const x: string | null | undefined; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableString: utils.Ref(true), + }, + }, + { + Code: `declare const x: '' | null; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableString: utils.Ref(true), + }, + }, + { + Code: `declare const x: 'foo' | undefined; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableString: utils.Ref(true), + }, + }, + + // ============================================ + // NULLABLE NUMBER - Valid with Option + // ============================================ + + { + Code: `declare const x: number | null; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableNumber: utils.Ref(true), + }, + }, + { + Code: `declare const x: number | undefined; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableNumber: utils.Ref(true), + }, + }, + { + Code: `declare const x: number | null | undefined; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableNumber: utils.Ref(true), + }, + }, + { + Code: `declare const x: 0 | null; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableNumber: utils.Ref(true), + }, + }, + { + Code: `declare const x: 1 | undefined; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableNumber: utils.Ref(true), + }, + }, + { + Code: `declare const x: bigint | null; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableNumber: utils.Ref(true), + }, + }, + + // ============================================ + // NULLABLE OBJECT - Valid with Option + // ============================================ + + { + Code: `declare const x: object | null; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(true), + }, + }, + { + Code: `declare const x: object | undefined; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(true), + }, + }, + { + Code: `declare const x: object | null | undefined; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(true), + }, + }, + { + Code: `declare const x: {} | null; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(true), + }, + }, + { + Code: `declare const x: [] | undefined; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(true), + }, + }, + { + Code: `declare const x: symbol | null; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(true), + }, + }, + + // ============================================ + // NULLABLE ENUM - Valid with Option + // ============================================ + // NOTE: Enum detection from union types is complex - skipping for now + // { + // Code: `enum E { A = 0, B = 1 } declare const x: E | null; if (x) {}`, + // Options: StrictBooleanExpressionsOptions{ + // AllowNullableEnum: utils.Ref(true), + // }, + // }, + // { + // Code: `enum E { A = '', B = 'foo' } declare const x: E | undefined; if (x) {}`, + // Options: StrictBooleanExpressionsOptions{ + // AllowNullableEnum: utils.Ref(true), + // }, + // }, + + // ============================================ + // ARRAY METHOD PREDICATES + // ============================================ + + {Code: `[1, 2, 3].every(x => x > 0);`}, + {Code: `[1, 2, 3].some(x => x > 0);`}, + {Code: `[1, 2, 3].filter(x => x > 0);`}, + {Code: `declare const arr: string[]; arr.find(x => x === 'foo');`}, + {Code: `declare const arr: string[]; arr.findIndex(x => x === 'foo');`}, + + // With nullable predicates and options + { + Code: `[1, 2, 3].filter(x => x);`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(true), + }, + }, + { + Code: `['', 'foo'].filter(x => x);`, + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(true), + }, + }, + + // ============================================ + // ASSERT FUNCTIONS AND TYPE PREDICATES + // ============================================ + + {Code: `function isString(x: unknown): x is string { return typeof x === 'string'; }`}, + {Code: `declare function isString(x: unknown): x is string; if (isString(value)) {}`}, + + // ============================================ + // VOID TYPE - handled as nullish, so moved to invalid section + // ============================================ + + // ============================================ + // DOUBLE NEGATION - Always Valid + // ============================================ + + {Code: `!!true;`}, + {Code: `!!false;`}, + {Code: `!!'';`}, + {Code: `!!0;`}, + + // ============================================ + // BOOLEAN CONSTRUCTOR - Always Valid + // ============================================ + + {Code: `Boolean(true);`}, + {Code: `Boolean(false);`}, + {Code: `Boolean('');`}, + {Code: `Boolean(0);`}, + {Code: `Boolean({});`}, + {Code: `Boolean([]);`}, + {Code: `declare const x: any; Boolean(x);`}, + {Code: `declare const x: unknown; Boolean(x);`}, + + // ============================================ + // COMPLEX LOGICAL EXPRESSIONS + // ============================================ + + {Code: `true && true && true;`}, + {Code: `true || false || true;`}, + {Code: `(true && false) || (false && true);`}, + {Code: `true ? (false || true) : (true && false);`}, + + // Mixed types with default options + {Code: `'' || 0 || false;`}, + {Code: `'foo' && 1 && true;`}, + // NOTE: Objects in logical operators should be errors - commenting out incorrect test + // {Code: `({}) || [] || true;`}, + + // ============================================ + // SPECIAL CASES + // ============================================ + + // Always allow boolean in right side of logical operators + {Code: `'foo' && true;`}, + {Code: `0 || false;`}, + // NOTE: Object in logical operator should be error - commenting out incorrect test + // {Code: `({}) && (1 > 2);`}, + + // Template literals + {Code: "declare const x: string; `foo${x}` ? 'a' : 'b';"}, + {Code: "declare const x: number; `foo${x}` ? 'a' : 'b';"}, + + // Parenthesized expressions + {Code: `(true) ? 'a' : 'b';`}, + {Code: `((true)) ? 'a' : 'b';`}, + {Code: `if ((true)) {}`}, + + // Comma operator + {Code: `(0, true) ? 'a' : 'b';`}, + {Code: `('', false) ? 'a' : 'b';`}, + + // Assignment expressions + {Code: `let x; (x = true) ? 'a' : 'b';`}, + {Code: `let x; if (x = false) {}`}, + + // Never type - allowed per TypeScript ESLint + {Code: `declare const x: never; if (x) {}`}, + {Code: `declare const x: never; x ? 'a' : 'b';`}, + {Code: `declare const x: never; !x;`}, + }, []rule_tester.InvalidTestCase{ + // ============================================ + // ANY TYPE - Invalid without Option + // ============================================ + + { + Code: `declare const x: any; if (x) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedAny", Line: 1}, + }, + }, + { + Code: `declare const x: any; x ? 'a' : 'b';`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedAny", Line: 1}, + }, + }, + { + Code: `declare const x: any; while (x) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedAny", Line: 1}, + }, + }, + { + Code: `declare const x: any; do {} while (x);`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedAny", Line: 1}, + }, + }, + { + Code: `declare const x: any; for (; x; ) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedAny", Line: 1}, + }, + }, + { + Code: `declare const x: any; x && 'a';`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedAny", Line: 1}, + }, + }, + { + Code: `declare const x: any; x || 'a';`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedAny", Line: 1}, + }, + }, + { + Code: `declare const x: any; !x;`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedAny", Line: 1}, + }, + }, + + // ============================================ + // NULLISH VALUES - Always Invalid + // ============================================ + + { + Code: `if (null) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullish", Line: 1}, + }, + }, + { + Code: `if (undefined) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullish", Line: 1}, + }, + }, + { + Code: `null ? 'a' : 'b';`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullish", Line: 1}, + }, + }, + { + Code: `undefined ? 'a' : 'b';`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullish", Line: 1}, + }, + }, + { + Code: `while (null) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullish", Line: 1}, + }, + }, + { + Code: `do {} while (undefined);`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullish", Line: 1}, + }, + }, + { + Code: `for (; null; ) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullish", Line: 1}, + }, + }, + { + Code: `!null;`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullish", Line: 1}, + }, + }, + { + Code: `!undefined;`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullish", Line: 1}, + }, + }, + { + Code: `null && 'a';`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullish", Line: 1}, + }, + }, + { + Code: `undefined || 'a';`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullish", Line: 1}, + }, + }, + + // ============================================ + // STRING TYPE - Invalid with allowString: false + // ============================================ + + { + Code: `if ('') {}`, + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + }, + }, + { + Code: `if ('foo') {}`, + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + }, + }, + { + Code: `'' ? 'a' : 'b';`, + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + }, + }, + { + Code: `'foo' ? 'a' : 'b';`, + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + }, + }, + { + Code: `while ('') {}`, + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + }, + }, + { + Code: `do {} while ('foo');`, + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + }, + }, + { + Code: `for (; 'foo'; ) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + }, + }, + { + Code: `!'foo';`, + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + }, + }, + { + Code: `'foo' && 'bar';`, + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + }, + }, + { + Code: `'' || 'default';`, + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + }, + }, + { + Code: `declare const s: string; if (s) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + }, + }, + + // Template literals + { + Code: "`` ? 'a' : 'b';", + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + }, + }, + { + Code: "`foo` ? 'a' : 'b';", + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + }, + }, + { + Code: "declare const x: string; `foo${x}` ? 'a' : 'b';", + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + }, + }, + + // ============================================ + // NUMBER TYPE - Invalid with allowNumber: false + // ============================================ + + { + Code: `if (0) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, + }, + { + Code: `if (1) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, + }, + { + Code: `0 ? 'a' : 'b';`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, + }, + { + Code: `1 ? 'a' : 'b';`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, + }, + { + Code: `NaN ? 'a' : 'b';`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, + }, + { + Code: `while (0) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, + }, + { + Code: `do {} while (1);`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, + }, + { + Code: `for (; 1; ) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, + }, + { + Code: `!0;`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, + }, + { + Code: `1 && 2;`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, + }, + { + Code: `0 || 1;`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, + }, + { + Code: `declare const n: number; if (n) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, + }, + + // BigInt + { + Code: `if (0n) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, + }, + { + Code: `if (1n) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, + }, + { + Code: `0n ? 'a' : 'b';`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, + }, + { + Code: `declare const b: bigint; if (b) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, + }, + + // ============================================ + // OBJECT TYPE - Always Invalid (Always Truthy) + // ============================================ + + { + Code: `if ({}) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, + }, + { + Code: `if ([]) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, + }, + { + Code: `({}) ? 'a' : 'b';`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, + }, + { + Code: `[] ? 'a' : 'b';`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, + }, + { + Code: `while ({}) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, + }, + { + Code: `do {} while ([]);`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, + }, + { + Code: `for (; {}; ) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, + }, + { + Code: `!{};`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, + }, + { + Code: `![];`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, + }, + { + Code: `({}) && 'a';`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, + }, + { + Code: `[] || 'a';`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, + }, + { + Code: `declare const o: object; if (o) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, + }, + { + Code: `declare const o: {}; if (o) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, + }, + + // Functions + { + Code: `function foo() {}; if (foo) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, + }, + { + Code: `const foo = () => {}; if (foo) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, + }, + + // Symbols + { + Code: `if (Symbol()) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, + }, + { + Code: `declare const s: symbol; if (s) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, + }, + + // ============================================ + // NULLABLE BOOLEAN - Invalid without Option + // ============================================ + + { + Code: `declare const x: boolean | null; if (x) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableBoolean", Line: 1}, + }, + }, + { + Code: `declare const x: boolean | undefined; if (x) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableBoolean", Line: 1}, + }, + }, + { + Code: `declare const x: boolean | null | undefined; if (x) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableBoolean", Line: 1}, + }, + }, + { + Code: `declare const x: true | null; x ? 'a' : 'b';`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableBoolean", Line: 1}, + }, + }, + { + Code: `declare const x: false | undefined; !x;`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableBoolean", Line: 1}, + }, + }, + + // ============================================ + // NULLABLE STRING - Invalid without Option + // ============================================ + + { + Code: `declare const x: string | null; if (x) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableString", Line: 1}, + }, + }, + { + Code: `declare const x: string | undefined; if (x) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableString", Line: 1}, + }, + }, + { + Code: `declare const x: string | null | undefined; if (x) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableString", Line: 1}, + }, + }, + { + Code: `declare const x: '' | null; x ? 'a' : 'b';`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableString", Line: 1}, + }, + }, + { + Code: `declare const x: 'foo' | undefined; !x;`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableString", Line: 1}, + }, + }, + + // ============================================ + // NULLABLE NUMBER - Invalid without Option + // ============================================ + + { + Code: `declare const x: number | null; if (x) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableNumber", Line: 1}, + }, + }, + { + Code: `declare const x: number | undefined; if (x) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableNumber", Line: 1}, + }, + }, + { + Code: `declare const x: number | null | undefined; if (x) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableNumber", Line: 1}, + }, + }, + { + Code: `declare const x: 0 | null; x ? 'a' : 'b';`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableNumber", Line: 1}, + }, + }, + { + Code: `declare const x: 1 | undefined; !x;`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableNumber", Line: 1}, + }, + }, + { + Code: `declare const x: bigint | null; if (x) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableNumber", Line: 1}, + }, + }, + + // ============================================ + // NULLABLE OBJECT - Invalid without Option + // ============================================ + + { + Code: `declare const x: object | null; if (x) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableObject", Line: 1}, + }, + }, + { + Code: `declare const x: object | undefined; if (x) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableObject", Line: 1}, + }, + }, + { + Code: `declare const x: object | null | undefined; if (x) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableObject", Line: 1}, + }, + }, + { + Code: `declare const x: {} | null; x ? 'a' : 'b';`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableObject", Line: 1}, + }, + }, + { + Code: `declare const x: [] | undefined; !x;`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableObject", Line: 1}, + }, + }, + { + Code: `declare const x: symbol | null; if (x) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableObject", Line: 1}, + }, + }, + + // ============================================ + // MIXED TYPES - Invalid + // ============================================ + + { + Code: `declare const x: string | number; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedMixedCondition", Line: 1}, + }, + }, + { + Code: `declare const x: string | boolean; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedMixedCondition", Line: 1}, + }, + }, + { + Code: `declare const x: number | boolean; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedMixedCondition", Line: 1}, + }, + }, + { + Code: `declare const x: string | number | boolean; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedMixedCondition", Line: 1}, + }, + }, + + // ============================================ + // ENUM TYPES + // ============================================ + + { + Code: `enum E { A = 0, B = 1 } declare const x: E; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, + }, + { + Code: `enum E { A = '', B = 'foo' } declare const x: E; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + }, + }, + + // ============================================ + // ARRAY METHOD PREDICATES - Invalid + // ============================================ + // NOTE: Arrow function predicate checking not implemented - skipping for now + // { + // Code: `[1, 2, 3].filter(x => x);`, + // Options: StrictBooleanExpressionsOptions{ + // AllowNumber: utils.Ref(false), + // }, + // Errors: []rule_tester.InvalidTestCaseError{ + // {MessageId: "unexpectedNumber", Line: 1}, + // }, + // }, + // { + // Code: `['', 'foo'].filter(x => x);`, + // Options: StrictBooleanExpressionsOptions{ + // AllowString: utils.Ref(false), + // }, + // Errors: []rule_tester.InvalidTestCaseError{ + // {MessageId: "unexpectedString", Line: 1}, + // }, + // }, + // { + // Code: `[{}, []].filter(x => x);`, + // Errors: []rule_tester.InvalidTestCaseError{ + // {MessageId: "unexpectedObjectContext", Line: 1}, + // }, + // }, + + // ============================================ + // COMPLEX LOGICAL EXPRESSIONS - Invalid + // ============================================ + + { + Code: `'foo' && 1;`, + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + }, + }, + { + Code: `0 || 'bar';`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, + }, + { + Code: `({}) && [];`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, + }, + { + Code: `'' || 0;`, + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + }, + }, + + // ============================================ + // SPECIAL CASES - Invalid + // ============================================ + + // Array.length + { + Code: `declare const arr: string[]; if (arr.length) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, + }, + + // Function calls returning non-boolean + { + Code: `declare function getString(): string; if (getString()) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + }, + }, + { + Code: `declare function getNumber(): number; if (getNumber()) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, + }, + { + Code: `declare function getObject(): object; if (getObject()) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, + }, + + // Property access + { + Code: `declare const obj: { prop: string }; if (obj.prop) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + }, + }, + { + Code: `declare const obj: { prop: number }; if (obj.prop) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, + }, + + // Void type + { + Code: `declare const x: void; if (x) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullish", Line: 1}, + }, + }, + { + Code: `void 0 ? 'a' : 'b';`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullish", Line: 1}, + }, + }, + }) +} From 75fb3cb1e0e284e46df58b3eb9d1656341001839 Mon Sep 17 00:00:00 2001 From: Noel Kim Date: Thu, 11 Sep 2025 10:10:55 +0900 Subject: [PATCH 02/27] Uncomment --- .../strict_boolean_expressions_test.go | 75 +++++++------------ 1 file changed, 25 insertions(+), 50 deletions(-) diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go index df301a28..a5c8122f 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go @@ -338,23 +338,6 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, }, - // ============================================ - // NULLABLE ENUM - Valid with Option - // ============================================ - // NOTE: Enum detection from union types is complex - skipping for now - // { - // Code: `enum E { A = 0, B = 1 } declare const x: E | null; if (x) {}`, - // Options: StrictBooleanExpressionsOptions{ - // AllowNullableEnum: utils.Ref(true), - // }, - // }, - // { - // Code: `enum E { A = '', B = 'foo' } declare const x: E | undefined; if (x) {}`, - // Options: StrictBooleanExpressionsOptions{ - // AllowNullableEnum: utils.Ref(true), - // }, - // }, - // ============================================ // ARRAY METHOD PREDICATES // ============================================ @@ -386,10 +369,6 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { {Code: `function isString(x: unknown): x is string { return typeof x === 'string'; }`}, {Code: `declare function isString(x: unknown): x is string; if (isString(value)) {}`}, - // ============================================ - // VOID TYPE - handled as nullish, so moved to invalid section - // ============================================ - // ============================================ // DOUBLE NEGATION - Always Valid // ============================================ @@ -424,8 +403,6 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { // Mixed types with default options {Code: `'' || 0 || false;`}, {Code: `'foo' && 1 && true;`}, - // NOTE: Objects in logical operators should be errors - commenting out incorrect test - // {Code: `({}) || [] || true;`}, // ============================================ // SPECIAL CASES @@ -434,8 +411,6 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { // Always allow boolean in right side of logical operators {Code: `'foo' && true;`}, {Code: `0 || false;`}, - // NOTE: Object in logical operator should be error - commenting out incorrect test - // {Code: `({}) && (1 > 2);`}, // Template literals {Code: "declare const x: string; `foo${x}` ? 'a' : 'b';"}, @@ -1199,31 +1174,31 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { // ============================================ // ARRAY METHOD PREDICATES - Invalid // ============================================ - // NOTE: Arrow function predicate checking not implemented - skipping for now - // { - // Code: `[1, 2, 3].filter(x => x);`, - // Options: StrictBooleanExpressionsOptions{ - // AllowNumber: utils.Ref(false), - // }, - // Errors: []rule_tester.InvalidTestCaseError{ - // {MessageId: "unexpectedNumber", Line: 1}, - // }, - // }, - // { - // Code: `['', 'foo'].filter(x => x);`, - // Options: StrictBooleanExpressionsOptions{ - // AllowString: utils.Ref(false), - // }, - // Errors: []rule_tester.InvalidTestCaseError{ - // {MessageId: "unexpectedString", Line: 1}, - // }, - // }, - // { - // Code: `[{}, []].filter(x => x);`, - // Errors: []rule_tester.InvalidTestCaseError{ - // {MessageId: "unexpectedObjectContext", Line: 1}, - // }, - // }, + + { + Code: `[1, 2, 3].filter(x => x);`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, + }, + { + Code: `['', 'foo'].filter(x => x);`, + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + }, + }, + { + Code: `[{}, []].filter(x => x);`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, + }, // ============================================ // COMPLEX LOGICAL EXPRESSIONS - Invalid From f67aae888c2de4169589ffd926394ad0bb0cd24a Mon Sep 17 00:00:00 2001 From: Noel Kim Date: Fri, 12 Sep 2025 13:40:28 +0900 Subject: [PATCH 03/27] Apply patch --- .../strict_boolean_expressions.go | 118 ++++++++------- .../strict_boolean_expressions_test.go | 140 ++++++++++++++++-- .../0001-Parallel-readDirectory-visitor.patch | 2 +- ...-project-service-for-single-run-mode.patch | 2 +- ...unsafe.String-to-avoid-allocations-w.patch | 2 +- ...es-add-public-getter-from-TypePredic.patch | 31 ++++ 6 files changed, 231 insertions(+), 64 deletions(-) create mode 100644 patches/0005-feat-checker-types-add-public-getter-from-TypePredic.patch diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go index 500f2927..9a287d6e 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go @@ -143,48 +143,39 @@ var StrictBooleanExpressionsRule = rule.Rule{ return rule.RuleListeners{} } - checkNode := func(node *ast.Node) { - nodeType := ctx.TypeChecker.GetTypeAtLocation(node) - checkCondition(ctx, node, nodeType, opts) - } - return rule.RuleListeners{ ast.KindIfStatement: func(node *ast.Node) { ifStmt := node.AsIfStatement() - checkNode(ifStmt.Expression) + traverseNode(ctx, ifStmt.Expression, opts, true) }, ast.KindWhileStatement: func(node *ast.Node) { whileStmt := node.AsWhileStatement() - checkNode(whileStmt.Expression) + traverseNode(ctx, whileStmt.Expression, opts, true) }, ast.KindDoStatement: func(node *ast.Node) { doStmt := node.AsDoStatement() - checkNode(doStmt.Expression) + traverseNode(ctx, doStmt.Expression, opts, true) }, ast.KindForStatement: func(node *ast.Node) { forStmt := node.AsForStatement() if forStmt.Condition != nil { - checkNode(forStmt.Condition) + traverseNode(ctx, forStmt.Condition, opts, true) } }, ast.KindConditionalExpression: func(node *ast.Node) { condExpr := node.AsConditionalExpression() - checkNode(condExpr.Condition) + traverseNode(ctx, condExpr.Condition, opts, true) }, ast.KindBinaryExpression: func(node *ast.Node) { - binExpr := node.AsBinaryExpression() - switch binExpr.OperatorToken.Kind { - case ast.KindAmpersandAmpersandToken, ast.KindBarBarToken: - checkNode(binExpr.Left) - } + traverseNode(ctx, node, opts, false) }, ast.KindCallExpression: func(node *ast.Node) { callExpr := node.AsCallExpression() - //assertedArgument := findTruthinessAssertedArgument(ctx.TypeChecker, callExpr) - //if assertedArgument != nil { - // checkNode(assertedArgument) - //} + assertedArgument := findTruthinessAssertedArgument(ctx.TypeChecker, callExpr) + if assertedArgument != nil { + traverseNode(ctx, assertedArgument, opts, true) + } if callExpr.Expression != nil && callExpr.Expression.Kind == ast.KindPropertyAccessExpression { propAccess := callExpr.Expression.AsPropertyAccessExpression() @@ -222,40 +213,68 @@ var StrictBooleanExpressionsRule = rule.Rule{ ast.KindPrefixUnaryExpression: func(node *ast.Node) { unaryExpr := node.AsPrefixUnaryExpression() if unaryExpr.Operator == ast.KindExclamationToken { - checkNode(unaryExpr.Operand) + traverseNode(ctx, unaryExpr.Operand, opts, true) } }, } }, } -// TODO: Not exported TypePredicate struct variables -//func findTruthinessAssertedArgument(typeChecker *checker.Checker, callExpr *ast.CallExpression) *ast.Node { -// var checkableArguments []*ast.Node -// for _, argument := range callExpr.Arguments.Nodes { -// if argument.Kind == ast.KindSpreadElement { -// continue -// } -// checkableArguments = append(checkableArguments, argument) -// } -// if len(checkableArguments) == 0 { -// return nil -// } -// node := callExpr.AsNode() -// signature := typeChecker.GetResolvedSignature(node) -// if signature == nil { -// return nil -// } -// firstTypePredicateResult := typeChecker.GetTypePredicateOfSignature(signature) -// if firstTypePredicateResult == nil { -// return nil -// } -// if firstTypePredicateResult.kind != checker.TypePredicateKindAssertsIdentifier || -// firstTypePredicateResult.type == nil { -// return nil -// } -// return checkableArguments[firstTypePredicateResult.parameterIndex] -//} +func findTruthinessAssertedArgument(typeChecker *checker.Checker, callExpr *ast.CallExpression) *ast.Node { + var checkableArguments []*ast.Node + for _, argument := range callExpr.Arguments.Nodes { + if argument.Kind == ast.KindSpreadElement { + continue + } + checkableArguments = append(checkableArguments, argument) + } + if len(checkableArguments) == 0 { + return nil + } + node := callExpr.AsNode() + signature := typeChecker.GetResolvedSignature(node) + if signature == nil { + return nil + } + firstTypePredicateResult := typeChecker.GetTypePredicateOfSignature(signature) + if firstTypePredicateResult == nil { + return nil + } + if firstTypePredicateResult.Kind() != checker.TypePredicateKindAssertsIdentifier || + firstTypePredicateResult.Type() != nil { + return nil + } + if firstTypePredicateResult.ParameterIndex() >= int32(len(checkableArguments)) { + return nil + } + return checkableArguments[firstTypePredicateResult.ParameterIndex()] +} + +func checkNode(ctx rule.RuleContext, node *ast.Node, opts StrictBooleanExpressionsOptions) { + nodeType := ctx.TypeChecker.GetTypeAtLocation(node) + checkCondition(ctx, node, nodeType, opts) +} + +func traverseLogicalExpression(ctx rule.RuleContext, binExpr *ast.BinaryExpression, opts StrictBooleanExpressionsOptions, isCondition bool) { + traverseNode(ctx, binExpr.Left, opts, true) + traverseNode(ctx, binExpr.Right, opts, isCondition) +} + +func traverseNode(ctx rule.RuleContext, node *ast.Node, opts StrictBooleanExpressionsOptions, isCondition bool) { + if node.Kind == ast.KindBinaryExpression { + binExpr := node.AsBinaryExpression() + switch binExpr.OperatorToken.Kind { + case ast.KindAmpersandAmpersandToken, ast.KindBarBarToken: + traverseLogicalExpression(ctx, binExpr, opts, true) + } + } + + if !isCondition { + return + } + + checkNode(ctx, node, opts) +} // Type analysis types type typeVariant int @@ -440,7 +459,7 @@ func checkCondition(ctx rule.RuleContext, node *ast.Node, t *checker.Type, opts ctx.ReportNode(node, buildUnexpectedNullish()) case typeVariantString: // Known edge case: truthy primitives and nullish values are always valid boolean expressions - if *opts.AllowString && info.isNullable && info.isTruthy { + if *opts.AllowString && info.isTruthy { return } @@ -458,8 +477,7 @@ func checkCondition(ctx rule.RuleContext, node *ast.Node, t *checker.Type, opts ctx.ReportNode(node, buildUnexpectedString()) } case typeVariantNumber: - // Known edge case: truthy primitives and nullish values are always valid boolean expressions - if *opts.AllowNumber && info.isNullable && info.isTruthy { + if *opts.AllowNumber && info.isTruthy { return } diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go index a5c8122f..f923bcf1 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go @@ -105,17 +105,6 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { {Code: `if (1n) {}`}, {Code: `declare const b: bigint; if (b) {}`}, - // ============================================ - // OBJECT TYPES in logical operators - // ============================================ - - // Note: Objects in logical operators are treated as boolean conditions - // and will be flagged as errors (always truthy) unless in the right side - // Right side of logical operators is not checked as a boolean condition - {Code: `'foo' || ({});`}, // Object on right side is OK - {Code: `false || [];`}, // Array on right side is OK - {Code: `(false && true) || [];`}, // Array on right side is OK - // ============================================ // ANY TYPE - Valid with Option // ============================================ @@ -368,6 +357,112 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { {Code: `function isString(x: unknown): x is string { return typeof x === 'string'; }`}, {Code: `declare function isString(x: unknown): x is string; if (isString(value)) {}`}, + { + Code: ` + declare function assert(a: number, b: unknown): asserts a; + declare const nullableString: string | null; + declare const boo: boolean; + assert(boo, nullableString); + `, + }, + { + Code: ` + declare function assert(a: boolean, b: unknown): asserts b is string; + declare const nullableString: string | null; + declare const boo: boolean; + assert(boo, nullableString); + `, + }, + { + Code: ` + declare function assert(a: number, b: unknown): asserts b; + declare const nullableString: string | null; + declare const boo: boolean; + assert(nullableString, boo); + `, + }, + { + Code: ` + declare function assert(a: number, b: unknown): asserts b; + declare const nullableString: string | null; + declare const boo: boolean; + assert(...nullableString, nullableString); + `, + }, + { + Code: ` + declare function assert( + this: object, + a: number, + b?: unknown, + c?: unknown, + ): asserts c; + declare const nullableString: string | null; + declare const foo: number; + const o: { assert: typeof assert } = { + assert, + }; + o.assert(foo, nullableString); + `, + }, + { + Code: ` + declare function assert(x: unknown): x is string; + declare const nullableString: string | null; + assert(nullableString); + `, + }, + { + Code: ` + class ThisAsserter { + assertThis(this: unknown, arg2: unknown): asserts this {} + } + + declare const lol: string | number | unknown | null; + + const thisAsserter: ThisAsserter = new ThisAsserter(); + thisAsserter.assertThis(lol); + `, + }, + { + Code: ` + function assert(this: object, a: number, b: unknown): asserts b; + function assert(a: bigint, b: unknown): asserts b; + function assert(this: object, a: string, two: string): asserts two; + function assert( + this: object, + a: string, + assertee: string, + c: bigint, + d: object, + ): asserts assertee; + function assert(...args: any[]): void; + + function assert(...args: any[]) { + throw new Error('lol'); + } + + declare const nullableString: string | null; + assert(3 as any, nullableString); + `, + }, + // Intentional use of `any` to test a function call with no call signatures. + { + Code: ` + declare const assert: any; + declare const nullableString: string | null; + assert(nullableString); + `, + }, + // Coverage for absent "test expression". + // Ensure that no crash or false positive occurs + { + Code: ` + for (let x = 0; ; x++) { + break; + } + `, + }, // ============================================ // DOUBLE NEGATION - Always Valid @@ -641,6 +736,7 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedString", Line: 1}, }, }, { @@ -650,6 +746,7 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedString", Line: 1}, }, }, { @@ -783,6 +880,7 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1}, }, }, { @@ -792,6 +890,7 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1}, }, }, { @@ -939,6 +1038,23 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, }, + // ============================================ + // ASSERT FUNCTIONS AND TYPE PREDICATES + // ============================================ + + { + Code: ` + declare function assert(a: boolean, b: unknown): asserts b; + declare function assert({ a }: { a: boolean }, b: unknown): asserts b; + declare const nullableString: string | null; + declare const boo: boolean; + assert(boo, nullableString); + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableString", Line: 6}, + }, + }, + // Symbols { Code: `if (Symbol()) {}`, @@ -1226,6 +1342,7 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { Code: `({}) && [];`, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedObjectContext", Line: 1}, + {MessageId: "unexpectedObjectContext", Line: 1}, }, }, { @@ -1236,6 +1353,7 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1}, }, }, diff --git a/patches/0001-Parallel-readDirectory-visitor.patch b/patches/0001-Parallel-readDirectory-visitor.patch index 37262808..91d8c97e 100644 --- a/patches/0001-Parallel-readDirectory-visitor.patch +++ b/patches/0001-Parallel-readDirectory-visitor.patch @@ -1,7 +1,7 @@ From beb5230504d73ec11bd5a9f746a9d4b6eb8a2e59 Mon Sep 17 00:00:00 2001 From: auvred Date: Wed, 18 Jun 2025 15:24:34 +0300 -Subject: [PATCH 1/4] Parallel readDirectory visitor +Subject: [PATCH 1/5] Parallel readDirectory visitor --- internal/vfs/utilities.go | 104 ++++++++++++++++++++++++++++---------- diff --git a/patches/0002-Adapt-project-service-for-single-run-mode.patch b/patches/0002-Adapt-project-service-for-single-run-mode.patch index 66877969..47040f71 100644 --- a/patches/0002-Adapt-project-service-for-single-run-mode.patch +++ b/patches/0002-Adapt-project-service-for-single-run-mode.patch @@ -1,7 +1,7 @@ From 70e5921f3e00d1fb3e968fc8dc5b9db1f01160b0 Mon Sep 17 00:00:00 2001 From: auvred Date: Wed, 6 Aug 2025 11:39:11 +0000 -Subject: [PATCH 2/4] Adapt project service for single run mode +Subject: [PATCH 2/5] Adapt project service for single run mode --- internal/project/checkerpool.go | 9 +++++++++ diff --git a/patches/0003-perf-tspath-use-unsafe.String-to-avoid-allocations-w.patch b/patches/0003-perf-tspath-use-unsafe.String-to-avoid-allocations-w.patch index 35bc704c..a2b8cfca 100644 --- a/patches/0003-perf-tspath-use-unsafe.String-to-avoid-allocations-w.patch +++ b/patches/0003-perf-tspath-use-unsafe.String-to-avoid-allocations-w.patch @@ -1,7 +1,7 @@ From d55ed5ae63cf53e04abe24f5de8574beb244d5cf Mon Sep 17 00:00:00 2001 From: Cameron Clark Date: Fri, 15 Aug 2025 16:06:42 +0100 -Subject: [PATCH 3/4] perf(tspath): use `unsafe.String` to avoid allocations +Subject: [PATCH 3/5] perf(tspath): use `unsafe.String` to avoid allocations when lower casing string --- diff --git a/patches/0005-feat-checker-types-add-public-getter-from-TypePredic.patch b/patches/0005-feat-checker-types-add-public-getter-from-TypePredic.patch new file mode 100644 index 00000000..b49868ca --- /dev/null +++ b/patches/0005-feat-checker-types-add-public-getter-from-TypePredic.patch @@ -0,0 +1,31 @@ +From 5f2b70a9908160df84f6b37c8480c8f94aad117d Mon Sep 17 00:00:00 2001 +From: Noel Kim +Date: Thu, 11 Sep 2025 11:46:56 +0900 +Subject: [PATCH] feat(checker/types): add public getter from `TypePredicate` + +--- + internal/checker/types.go | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/internal/checker/types.go b/internal/checker/types.go +index 5fd4b1583..c8bdf3e98 100644 +--- a/internal/checker/types.go ++++ b/internal/checker/types.go +@@ -1192,6 +1192,14 @@ type TypePredicate struct { + t *Type + } + ++func (t *TypePredicate) Kind() TypePredicateKind { return t.kind } ++ ++func (t *TypePredicate) ParameterIndex() int32 { return t.parameterIndex } ++ ++func (t *TypePredicate) ParameterName() string { return t.parameterName } ++ ++func (t *TypePredicate) Type() *Type { return t.t } ++ + // IndexInfo + + type IndexInfo struct { +-- +2.39.5 (Apple Git-154) + From cb38fa052d919c58416938a98f177a767c146da3 Mon Sep 17 00:00:00 2001 From: Noel Kim Date: Fri, 12 Sep 2025 13:55:46 +0900 Subject: [PATCH 04/27] patch subject --- ...5-feat-checker-types-add-public-getter-from-TypePredic.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patches/0005-feat-checker-types-add-public-getter-from-TypePredic.patch b/patches/0005-feat-checker-types-add-public-getter-from-TypePredic.patch index b49868ca..44202c41 100644 --- a/patches/0005-feat-checker-types-add-public-getter-from-TypePredic.patch +++ b/patches/0005-feat-checker-types-add-public-getter-from-TypePredic.patch @@ -1,7 +1,7 @@ From 5f2b70a9908160df84f6b37c8480c8f94aad117d Mon Sep 17 00:00:00 2001 From: Noel Kim Date: Thu, 11 Sep 2025 11:46:56 +0900 -Subject: [PATCH] feat(checker/types): add public getter from `TypePredicate` +Subject: [PATCH 5/5] feat(checker/types): add public getter from `TypePredicate` --- internal/checker/types.go | 8 ++++++++ From 7c9184d2949674c3a94f00d38d6f3c1d395c6fca Mon Sep 17 00:00:00 2001 From: Noel Kim Date: Fri, 12 Sep 2025 15:26:39 +0900 Subject: [PATCH 05/27] extra shim instead of patch --- .../strict_boolean_expressions.go | 9 +++--- .../0001-Parallel-readDirectory-visitor.patch | 2 +- ...-project-service-for-single-run-mode.patch | 2 +- ...unsafe.String-to-avoid-allocations-w.patch | 2 +- ...es-add-public-getter-from-TypePredic.patch | 31 ------------------- shim/checker/extra-shim.json | 5 +++ shim/checker/shim.go | 15 +++++++++ 7 files changed, 28 insertions(+), 38 deletions(-) delete mode 100644 patches/0005-feat-checker-types-add-public-getter-from-TypePredic.patch diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go index 9a287d6e..7bea5e02 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go @@ -240,14 +240,15 @@ func findTruthinessAssertedArgument(typeChecker *checker.Checker, callExpr *ast. if firstTypePredicateResult == nil { return nil } - if firstTypePredicateResult.Kind() != checker.TypePredicateKindAssertsIdentifier || - firstTypePredicateResult.Type() != nil { + if checker.TypePredicate_kind(firstTypePredicateResult) != checker.TypePredicateKindAssertsIdentifier || + checker.TypePredicate_t(firstTypePredicateResult) != nil { return nil } - if firstTypePredicateResult.ParameterIndex() >= int32(len(checkableArguments)) { + parameterIndex := checker.TypePredicate_parameterIndex(firstTypePredicateResult) + if parameterIndex >= int32(len(checkableArguments)) { return nil } - return checkableArguments[firstTypePredicateResult.ParameterIndex()] + return checkableArguments[parameterIndex] } func checkNode(ctx rule.RuleContext, node *ast.Node, opts StrictBooleanExpressionsOptions) { diff --git a/patches/0001-Parallel-readDirectory-visitor.patch b/patches/0001-Parallel-readDirectory-visitor.patch index 91d8c97e..37262808 100644 --- a/patches/0001-Parallel-readDirectory-visitor.patch +++ b/patches/0001-Parallel-readDirectory-visitor.patch @@ -1,7 +1,7 @@ From beb5230504d73ec11bd5a9f746a9d4b6eb8a2e59 Mon Sep 17 00:00:00 2001 From: auvred Date: Wed, 18 Jun 2025 15:24:34 +0300 -Subject: [PATCH 1/5] Parallel readDirectory visitor +Subject: [PATCH 1/4] Parallel readDirectory visitor --- internal/vfs/utilities.go | 104 ++++++++++++++++++++++++++++---------- diff --git a/patches/0002-Adapt-project-service-for-single-run-mode.patch b/patches/0002-Adapt-project-service-for-single-run-mode.patch index 47040f71..66877969 100644 --- a/patches/0002-Adapt-project-service-for-single-run-mode.patch +++ b/patches/0002-Adapt-project-service-for-single-run-mode.patch @@ -1,7 +1,7 @@ From 70e5921f3e00d1fb3e968fc8dc5b9db1f01160b0 Mon Sep 17 00:00:00 2001 From: auvred Date: Wed, 6 Aug 2025 11:39:11 +0000 -Subject: [PATCH 2/5] Adapt project service for single run mode +Subject: [PATCH 2/4] Adapt project service for single run mode --- internal/project/checkerpool.go | 9 +++++++++ diff --git a/patches/0003-perf-tspath-use-unsafe.String-to-avoid-allocations-w.patch b/patches/0003-perf-tspath-use-unsafe.String-to-avoid-allocations-w.patch index a2b8cfca..35bc704c 100644 --- a/patches/0003-perf-tspath-use-unsafe.String-to-avoid-allocations-w.patch +++ b/patches/0003-perf-tspath-use-unsafe.String-to-avoid-allocations-w.patch @@ -1,7 +1,7 @@ From d55ed5ae63cf53e04abe24f5de8574beb244d5cf Mon Sep 17 00:00:00 2001 From: Cameron Clark Date: Fri, 15 Aug 2025 16:06:42 +0100 -Subject: [PATCH 3/5] perf(tspath): use `unsafe.String` to avoid allocations +Subject: [PATCH 3/4] perf(tspath): use `unsafe.String` to avoid allocations when lower casing string --- diff --git a/patches/0005-feat-checker-types-add-public-getter-from-TypePredic.patch b/patches/0005-feat-checker-types-add-public-getter-from-TypePredic.patch deleted file mode 100644 index 44202c41..00000000 --- a/patches/0005-feat-checker-types-add-public-getter-from-TypePredic.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 5f2b70a9908160df84f6b37c8480c8f94aad117d Mon Sep 17 00:00:00 2001 -From: Noel Kim -Date: Thu, 11 Sep 2025 11:46:56 +0900 -Subject: [PATCH 5/5] feat(checker/types): add public getter from `TypePredicate` - ---- - internal/checker/types.go | 8 ++++++++ - 1 file changed, 8 insertions(+) - -diff --git a/internal/checker/types.go b/internal/checker/types.go -index 5fd4b1583..c8bdf3e98 100644 ---- a/internal/checker/types.go -+++ b/internal/checker/types.go -@@ -1192,6 +1192,14 @@ type TypePredicate struct { - t *Type - } - -+func (t *TypePredicate) Kind() TypePredicateKind { return t.kind } -+ -+func (t *TypePredicate) ParameterIndex() int32 { return t.parameterIndex } -+ -+func (t *TypePredicate) ParameterName() string { return t.parameterName } -+ -+func (t *TypePredicate) Type() *Type { return t.t } -+ - // IndexInfo - - type IndexInfo struct { --- -2.39.5 (Apple Git-154) - diff --git a/shim/checker/extra-shim.json b/shim/checker/extra-shim.json index a8d46d79..2be1c93d 100644 --- a/shim/checker/extra-shim.json +++ b/shim/checker/extra-shim.json @@ -53,6 +53,11 @@ ], "InterfaceType": [ "thisType" + ], + "TypePredicate": [ + "kind", + "t", + "parameterIndex" ] } } diff --git a/shim/checker/shim.go b/shim/checker/shim.go index 9502c63f..8ed0d398 100644 --- a/shim/checker/shim.go +++ b/shim/checker/shim.go @@ -1144,6 +1144,21 @@ const TypeMapperKindUnknown = checker.TypeMapperKindUnknown type TypeNodeLinks = checker.TypeNodeLinks type TypeParameter = checker.TypeParameter type TypePredicate = checker.TypePredicate +type extra_TypePredicate struct { + kind checker.TypePredicateKind + parameterIndex int32 + parameterName string + t *checker.Type +} +func TypePredicate_kind(v *checker.TypePredicate) checker.TypePredicateKind { + return ((*extra_TypePredicate)(unsafe.Pointer(v))).kind +} +func TypePredicate_t(v *checker.TypePredicate) *checker.Type { + return ((*extra_TypePredicate)(unsafe.Pointer(v))).t +} +func TypePredicate_parameterIndex(v *checker.TypePredicate) int32 { + return ((*extra_TypePredicate)(unsafe.Pointer(v))).parameterIndex +} type TypePredicateKind = checker.TypePredicateKind const TypePredicateKindAssertsIdentifier = checker.TypePredicateKindAssertsIdentifier const TypePredicateKindAssertsThis = checker.TypePredicateKindAssertsThis From 963826e00007e1bd7779a01f2c5ad79d8c8c1e0e Mon Sep 17 00:00:00 2001 From: Noel Kim Date: Mon, 29 Sep 2025 11:36:58 +0900 Subject: [PATCH 06/27] wip --- .../strict_boolean_expressions.go | 44 +- .../strict_boolean_expressions_single_test.go | 16 + .../strict_boolean_expressions_test.go | 1428 ++++++++--------- 3 files changed, 672 insertions(+), 816 deletions(-) create mode 100644 internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go index 7bea5e02..1d8d8676 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go @@ -252,7 +252,7 @@ func findTruthinessAssertedArgument(typeChecker *checker.Checker, callExpr *ast. } func checkNode(ctx rule.RuleContext, node *ast.Node, opts StrictBooleanExpressionsOptions) { - nodeType := ctx.TypeChecker.GetTypeAtLocation(node) + nodeType := utils.GetConstrainedTypeAtLocation(ctx.TypeChecker, node) checkCondition(ctx, node, nodeType, opts) } @@ -264,9 +264,8 @@ func traverseLogicalExpression(ctx rule.RuleContext, binExpr *ast.BinaryExpressi func traverseNode(ctx rule.RuleContext, node *ast.Node, opts StrictBooleanExpressionsOptions, isCondition bool) { if node.Kind == ast.KindBinaryExpression { binExpr := node.AsBinaryExpression() - switch binExpr.OperatorToken.Kind { - case ast.KindAmpersandAmpersandToken, ast.KindBarBarToken: - traverseLogicalExpression(ctx, binExpr, opts, true) + if binExpr.OperatorToken.Kind != ast.KindQuestionQuestionToken { + traverseLogicalExpression(ctx, binExpr, opts, false) } } @@ -313,28 +312,24 @@ func analyzeType(typeChecker *checker.Checker, t *checker.Type) typeInfo { info.isUnion = true parts := utils.UnionTypeParts(t) variants := make(map[typeVariant]bool) - hasNullish := false - hasEnum := false for _, part := range parts { partInfo := analyzeTypePart(typeChecker, part) variants[partInfo.variant] = true if partInfo.variant == typeVariantNullish { - hasNullish = true + info.isNullable = true } if partInfo.isEnum { - hasEnum = true + info.isEnum = true } + info.isTruthy = partInfo.isTruthy } - info.isNullable = hasNullish - info.isEnum = hasEnum - if len(variants) == 1 { for v := range variants { info.variant = v } - } else if len(variants) == 2 && hasNullish { + } else if len(variants) == 2 && info.isNullable { for v := range variants { if v != typeVariantNullish { info.variant = v @@ -350,14 +345,26 @@ func analyzeType(typeChecker *checker.Checker, t *checker.Type) typeInfo { if utils.IsIntersectionType(t) { info.isIntersection = true - info.variant = typeVariantObject + types := t.Types() + isBoolean := false + for _, t2 := range types { + if analyzeTypePart(typeChecker, t2).variant == typeVariantBoolean { + isBoolean = true + break + } + } + if isBoolean { + info.variant = typeVariantBoolean + } else { + info.variant = typeVariantObject + } return info } return analyzeTypePart(typeChecker, t) } -func analyzeTypePart(_ *checker.Checker, t *checker.Type) typeInfo { +func analyzeTypePart(typeChecker *checker.Checker, t *checker.Type) typeInfo { info := typeInfo{} flags := checker.Type_flags(t) @@ -386,7 +393,10 @@ func analyzeTypePart(_ *checker.Checker, t *checker.Type) typeInfo { return info } - if flags&(checker.TypeFlagsBoolean|checker.TypeFlagsBooleanLiteral) != 0 { + if flags&(checker.TypeFlagsBoolean|checker.TypeFlagsBooleanLiteral|checker.TypeFlagsBooleanLike) != 0 { + if flags&checker.TypeFlagsBooleanLike != 0 && t.AsIntrinsicType().IntrinsicName() == "true" { + info.isTruthy = true + } info.variant = typeVariantBoolean return info } @@ -430,9 +440,7 @@ func analyzeTypePart(_ *checker.Checker, t *checker.Type) typeInfo { return info } - if flags&checker.TypeFlagsObject != 0 || - flags&checker.TypeFlagsNonPrimitive != 0 || - utils.IsObjectType(t) { + if flags&(checker.TypeFlagsObject|checker.TypeFlagsNonPrimitive) != 0 { info.variant = typeVariantObject return info } diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go new file mode 100644 index 00000000..6180fe5b --- /dev/null +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go @@ -0,0 +1,16 @@ +// Code generated from strict-boolean-expressions.test.ts - DO NOT EDIT. + +package strict_boolean_expressions + +import ( + "testing" + + "github.com/typescript-eslint/tsgolint/internal/rule_tester" + "github.com/typescript-eslint/tsgolint/internal/rules/fixtures" +) + +func TestStrictBooleanExpressionsSingleRule(t *testing.T) { + rule_tester.RunRuleTester(fixtures.GetRootDir(), "tsconfig.json", t, &StrictBooleanExpressionsRule, []rule_tester.ValidTestCase{ + {Code: `declare const x: null | object; if (x) {}`}, + }, []rule_tester.InvalidTestCase{}) +} \ No newline at end of file diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go index f923bcf1..82007171 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go @@ -1,3 +1,5 @@ +// Code generated from strict-boolean-expressions.test.ts - DO NOT EDIT. + package strict_boolean_expressions import ( @@ -9,176 +11,59 @@ import ( ) func TestStrictBooleanExpressionsRule(t *testing.T) { - // Test with strictNullChecks enabled (in fixtures/tsconfig.json) rule_tester.RunRuleTester(fixtures.GetRootDir(), "tsconfig.json", t, &StrictBooleanExpressionsRule, []rule_tester.ValidTestCase{ - // ============================================ - // BOOLEAN TYPES - Always Valid - // ============================================ - - // Boolean literals + // ======================================== + // BOOLEAN IN BOOLEAN CONTEXT + // ======================================== {Code: `true ? 'a' : 'b';`}, - {Code: `false ? 'a' : 'b';`}, - {Code: `if (true) {}`}, {Code: `if (false) {}`}, {Code: `while (true) {}`}, - {Code: `do {} while (true);`}, - {Code: `for (; true; ) {}`}, + {Code: `for (; false; ) {}`}, {Code: `!true;`}, + {Code: `false || 123;`}, + {Code: `true && 'foo';`}, + {Code: `!(false || true);`}, + {Code: `true && false ? true : false;`}, + {Code: `(false && true) || false;`}, + {Code: `(false && true) || [];`}, + {Code: `(false && 1) || (true && 2);`}, + {Code: `declare const x: boolean; if (x) {}`}, + {Code: `(x: boolean) => !x;`}, + {Code: `(x: T) => (x ? 1 : 0);`}, + {Code: `declare const x: never; if (x) {}`}, - // Boolean variables - {Code: `const b = true; if (b) {}`}, - {Code: `const b = false; if (!b) {}`}, - {Code: `declare const b: boolean; if (b) {}`}, - - // Boolean expressions - {Code: `if (true && false) {}`}, - {Code: `if (true || false) {}`}, - {Code: `true && 'a';`}, - {Code: `false || 'a';`}, - - // Comparison operators return boolean - {Code: `1 > 2 ? 'a' : 'b';`}, - {Code: `1 < 2 ? 'a' : 'b';`}, - {Code: `1 >= 2 ? 'a' : 'b';`}, - {Code: `1 <= 2 ? 'a' : 'b';`}, - {Code: `1 == 2 ? 'a' : 'b';`}, - {Code: `1 != 2 ? 'a' : 'b';`}, - {Code: `1 === 2 ? 'a' : 'b';`}, - {Code: `1 !== 2 ? 'a' : 'b';`}, - - // Type guards - {Code: `declare const x: string | number; if (typeof x === 'string') {}`}, - {Code: `declare const x: any; if (x instanceof Error) {}`}, - {Code: `declare const x: any; if ('prop' in x) {}`}, - - // Function returning boolean - {Code: `function test(): boolean { return true; } if (test()) {}`}, - {Code: `declare function test(): boolean; if (test()) {}`}, - - // ============================================ - // STRING TYPES - Valid with Default Options - // ============================================ - - {Code: `'' ? 'a' : 'b';`}, - {Code: `'foo' ? 'a' : 'b';`}, - {Code: "`` ? 'a' : 'b';"}, - {Code: "`foo` ? 'a' : 'b';"}, - {Code: "`foo${bar}` ? 'a' : 'b';"}, + // ======================================== + // STRING IN BOOLEAN CONTEXT + // ======================================== {Code: `if ('') {}`}, - {Code: `if ('foo') {}`}, - {Code: `while ('') {}`}, - {Code: `do {} while ('foo');`}, - {Code: `for (; 'foo'; ) {}`}, - {Code: `!!'foo';`}, - {Code: `declare const s: string; if (s) {}`}, - - // String with logical operators - {Code: `'' || 'foo';`}, - {Code: `'foo' && 'bar';`}, - {Code: `declare const s: string; s || 'default';`}, - - // ============================================ - // NUMBER TYPES - Valid with Default Options - // ============================================ - - {Code: `0 ? 'a' : 'b';`}, - {Code: `1 ? 'a' : 'b';`}, - {Code: `-1 ? 'a' : 'b';`}, - {Code: `0.5 ? 'a' : 'b';`}, - {Code: `NaN ? 'a' : 'b';`}, + {Code: `while ('x') {}`}, + {Code: `for (; ''; ) {}`}, + {Code: `('' && '1') || x;`}, + {Code: `declare const x: string; if (x) {}`}, + {Code: `(x: string) => !x;`}, + {Code: `(x: T) => (x ? 1 : 0);`}, + + // ======================================== + // NUMBER IN BOOLEAN CONTEXT + // ======================================== {Code: `if (0) {}`}, - {Code: `if (1) {}`}, - {Code: `while (0) {}`}, - {Code: `do {} while (1);`}, - {Code: `for (; 1; ) {}`}, - {Code: `declare const n: number; if (n) {}`}, - - // Number with logical operators - {Code: `0 || 1;`}, - {Code: `1 && 2;`}, - {Code: `declare const n: number; n || 0;`}, - - // BigInt - {Code: `0n ? 'a' : 'b';`}, - {Code: `1n ? 'a' : 'b';`}, - {Code: `if (0n) {}`}, - {Code: `if (1n) {}`}, - {Code: `declare const b: bigint; if (b) {}`}, - - // ============================================ - // ANY TYPE - Valid with Option - // ============================================ - - { - Code: `declare const x: any; if (x) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(true), - }, - }, - { - Code: `declare const x: any; x ? 'a' : 'b';`, - Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(true), - }, - }, - { - Code: `declare const x: any; x && 'a';`, - Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(true), - }, - }, - { - Code: `declare const x: any; x || 'a';`, - Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(true), - }, - }, - { - Code: `declare const x: any; !x;`, - Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(true), - }, - }, - - // ============================================ - // UNKNOWN TYPE - Valid with Option - // ============================================ - - { - Code: `declare const x: unknown; if (x) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(true), - }, - }, - { - Code: `declare const x: unknown; x ? 'a' : 'b';`, - Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(true), - }, - }, - { - Code: `declare const x: unknown; x && 'a';`, - Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(true), - }, - }, - { - Code: `declare const x: unknown; x || 'a';`, - Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(true), - }, - }, - { - Code: `declare const x: unknown; !x;`, - Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(true), - }, - }, - - // ============================================ - // NULLABLE BOOLEAN - Valid with Option - // ============================================ - + {Code: `while (1n) {}`}, + {Code: `for (; Infinity; ) {}`}, + {Code: `(0 / 0 && 1 + 2) || x;`}, + {Code: `declare const x: number; if (x) {}`}, + {Code: `(x: bigint) => !x;`}, + {Code: `(x: T) => (x ? 1 : 0);`}, + + // ======================================== + // NULLABLE OBJECT IN BOOLEAN CONTEXT + // ======================================== + {Code: `declare const x: null | object; if (x) {}`}, + {Code: `(x?: { a: any }) => !x;`}, + {Code: `(x: T) => (x ? 1 : 0);`}, + + // ======================================== + // NULLABLE BOOLEAN IN BOOLEAN CONTEXT + // ======================================== { Code: `declare const x: boolean | null; if (x) {}`, Options: StrictBooleanExpressionsOptions{ @@ -186,40 +71,27 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, }, { - Code: `declare const x: boolean | undefined; if (x) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableBoolean: utils.Ref(true), - }, - }, - { - Code: `declare const x: boolean | null | undefined; if (x) {}`, + Code: `(x?: boolean) => !x;`, Options: StrictBooleanExpressionsOptions{ AllowNullableBoolean: utils.Ref(true), }, }, { - Code: `declare const x: true | null; if (x) {}`, + Code: `(x: T) => (x ? 1 : 0);`, Options: StrictBooleanExpressionsOptions{ AllowNullableBoolean: utils.Ref(true), }, }, { - Code: `declare const x: false | undefined; if (x) {}`, + Code: `declare const test?: boolean; if (test ?? false) {}`, Options: StrictBooleanExpressionsOptions{ AllowNullableBoolean: utils.Ref(true), }, }, - // ============================================ - // NULLABLE STRING - Valid with Option - // ============================================ - - { - Code: `declare const x: string | null; if (x) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableString: utils.Ref(true), - }, - }, + // ======================================== + // NULLABLE STRING IN BOOLEAN CONTEXT + // ======================================== { Code: `declare const x: string | undefined; if (x) {}`, Options: StrictBooleanExpressionsOptions{ @@ -227,674 +99,648 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, }, { - Code: `declare const x: string | null | undefined; if (x) {}`, + Code: `(x?: string) => !x;`, Options: StrictBooleanExpressionsOptions{ AllowNullableString: utils.Ref(true), }, }, { - Code: `declare const x: '' | null; if (x) {}`, + Code: `(x: T) => (x ? 1 : 0);`, Options: StrictBooleanExpressionsOptions{ AllowNullableString: utils.Ref(true), }, }, { - Code: `declare const x: 'foo' | undefined; if (x) {}`, + Code: `'string' != null ? 'asd' : '';`, Options: StrictBooleanExpressionsOptions{ AllowNullableString: utils.Ref(true), }, }, - // ============================================ - // NULLABLE NUMBER - Valid with Option - // ============================================ - + // ======================================== + // NULLABLE NUMBER IN BOOLEAN CONTEXT + // ======================================== { - Code: `declare const x: number | null; if (x) {}`, + Code: `declare const x: number | undefined; if (x) {}`, Options: StrictBooleanExpressionsOptions{ AllowNullableNumber: utils.Ref(true), }, }, { - Code: `declare const x: number | undefined; if (x) {}`, + Code: `(x?: number) => !x;`, Options: StrictBooleanExpressionsOptions{ AllowNullableNumber: utils.Ref(true), }, }, { - Code: `declare const x: number | null | undefined; if (x) {}`, + Code: `(x: T) => (x ? 1 : 0);`, Options: StrictBooleanExpressionsOptions{ AllowNullableNumber: utils.Ref(true), }, }, { - Code: `declare const x: 0 | null; if (x) {}`, + Code: `declare const x: bigint | undefined; if (x ?? 0n) {}`, Options: StrictBooleanExpressionsOptions{ AllowNullableNumber: utils.Ref(true), }, }, + + // ======================================== + // ANY TYPE IN BOOLEAN CONTEXT + // ======================================== { - Code: `declare const x: 1 | undefined; if (x) {}`, + Code: `declare const x: any; if (x) {}`, Options: StrictBooleanExpressionsOptions{ - AllowNullableNumber: utils.Ref(true), + AllowAny: utils.Ref(true), }, }, { - Code: `declare const x: bigint | null; if (x) {}`, + Code: `(x?: any) => !x;`, Options: StrictBooleanExpressionsOptions{ - AllowNullableNumber: utils.Ref(true), + AllowAny: utils.Ref(true), }, }, - - // ============================================ - // NULLABLE OBJECT - Valid with Option - // ============================================ - { - Code: `declare const x: object | null; if (x) {}`, + Code: `(x: T) => (x ? 1 : 0);`, Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(true), + AllowAny: utils.Ref(true), }, }, { - Code: `declare const x: object | undefined; if (x) {}`, + Code: `const foo: undefined | any = 0; if (foo) {}`, Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(true), + AllowAny: utils.Ref(true), }, }, + + // ======================================== + // UNKNOWN TYPE IN BOOLEAN CONTEXT + // ======================================== { - Code: `declare const x: object | null | undefined; if (x) {}`, + Code: `declare const x: unknown; if (x) {}`, Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(true), + AllowAny: utils.Ref(true), }, }, { - Code: `declare const x: {} | null; if (x) {}`, + Code: `(x: T) => (x ? 1 : 0);`, Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(true), + AllowAny: utils.Ref(true), }, }, + + // ======================================== + // NULLABLE ENUM IN BOOLEAN CONTEXT + // ======================================== { - Code: `declare const x: [] | undefined; if (x) {}`, + Code: ` + enum E { A, B } + declare const x: E | null; + if (x) {} + `, Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(true), + AllowNullableEnum: utils.Ref(true), }, }, { - Code: `declare const x: symbol | null; if (x) {}`, + Code: ` + enum E { A = 'a', B = 'b' } + declare const x: E | undefined; + if (x) {} + `, Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(true), + AllowNullableEnum: utils.Ref(true), }, }, - // ============================================ - // ARRAY METHOD PREDICATES - // ============================================ - - {Code: `[1, 2, 3].every(x => x > 0);`}, - {Code: `[1, 2, 3].some(x => x > 0);`}, - {Code: `[1, 2, 3].filter(x => x > 0);`}, - {Code: `declare const arr: string[]; arr.find(x => x === 'foo');`}, - {Code: `declare const arr: string[]; arr.findIndex(x => x === 'foo');`}, + // ======================================== + // ALLOW RULE TO RUN WITHOUT STRICT NULL CHECKS + // ======================================== + { + Code: `if (true) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: utils.Ref(true), + }, + }, - // With nullable predicates and options + // ======================================== + // COMPLEX BOOLEAN EXPRESSIONS + // ======================================== + {Code: `(x?: boolean) => x ?? false ? true : false;`}, + {Code: `(x: T) => x ?? false;`}, { - Code: `[1, 2, 3].filter(x => x);`, + Code: `(x?: string) => x ?? false;`, Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(true), + AllowNullableString: utils.Ref(true), }, }, { - Code: `['', 'foo'].filter(x => x);`, + Code: `(x?: number) => x ?? false;`, Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(true), + AllowNullableNumber: utils.Ref(true), }, }, - // ============================================ - // ASSERT FUNCTIONS AND TYPE PREDICATES - // ============================================ - - {Code: `function isString(x: unknown): x is string { return typeof x === 'string'; }`}, - {Code: `declare function isString(x: unknown): x is string; if (isString(value)) {}`}, - { - Code: ` + // ======================================== + // ASSERT FUNCTIONS + // ======================================== + {Code: ` + declare function assert(condition: unknown): asserts condition; + declare const x: string | null; + assert(x); + `}, + {Code: ` declare function assert(a: number, b: unknown): asserts a; declare const nullableString: string | null; declare const boo: boolean; assert(boo, nullableString); - `, - }, - { - Code: ` + `}, + {Code: ` declare function assert(a: boolean, b: unknown): asserts b is string; declare const nullableString: string | null; declare const boo: boolean; assert(boo, nullableString); - `, - }, - { - Code: ` + `}, + {Code: ` declare function assert(a: number, b: unknown): asserts b; declare const nullableString: string | null; declare const boo: boolean; assert(nullableString, boo); - `, - }, - { - Code: ` + `}, + {Code: ` declare function assert(a: number, b: unknown): asserts b; declare const nullableString: string | null; declare const boo: boolean; assert(...nullableString, nullableString); - `, - }, - { - Code: ` + `}, + {Code: ` declare function assert( - this: object, - a: number, - b?: unknown, - c?: unknown, + this: object, + a: number, + b?: unknown, + c?: unknown, ): asserts c; declare const nullableString: string | null; declare const foo: number; - const o: { assert: typeof assert } = { - assert, - }; + const o: { assert: typeof assert } = { assert }; o.assert(foo, nullableString); - `, - }, - { - Code: ` + `}, + {Code: ` declare function assert(x: unknown): x is string; declare const nullableString: string | null; assert(nullableString); - `, - }, - { - Code: ` + `}, + {Code: ` class ThisAsserter { - assertThis(this: unknown, arg2: unknown): asserts this {} + assertThis(this: unknown, arg2: unknown): asserts this {} } - declare const lol: string | number | unknown | null; - const thisAsserter: ThisAsserter = new ThisAsserter(); thisAsserter.assertThis(lol); - `, - }, - { - Code: ` + `}, + {Code: ` function assert(this: object, a: number, b: unknown): asserts b; function assert(a: bigint, b: unknown): asserts b; function assert(this: object, a: string, two: string): asserts two; function assert( - this: object, - a: string, - assertee: string, - c: bigint, - d: object, + this: object, + a: string, + assertee: string, + c: bigint, + d: object, ): asserts assertee; function assert(...args: any[]): void; - function assert(...args: any[]) { - throw new Error('lol'); + throw new Error('lol'); } - declare const nullableString: string | null; assert(3 as any, nullableString); - `, - }, - // Intentional use of `any` to test a function call with no call signatures. - { - Code: ` + `}, + {Code: ` declare const assert: any; declare const nullableString: string | null; assert(nullableString); - `, - }, - // Coverage for absent "test expression". - // Ensure that no crash or false positive occurs - { - Code: ` - for (let x = 0; ; x++) { - break; - } - `, - }, - - // ============================================ - // DOUBLE NEGATION - Always Valid - // ============================================ - - {Code: `!!true;`}, - {Code: `!!false;`}, - {Code: `!!'';`}, - {Code: `!!0;`}, - - // ============================================ - // BOOLEAN CONSTRUCTOR - Always Valid - // ============================================ - - {Code: `Boolean(true);`}, - {Code: `Boolean(false);`}, - {Code: `Boolean('');`}, - {Code: `Boolean(0);`}, - {Code: `Boolean({});`}, - {Code: `Boolean([]);`}, - {Code: `declare const x: any; Boolean(x);`}, - {Code: `declare const x: unknown; Boolean(x);`}, - - // ============================================ - // COMPLEX LOGICAL EXPRESSIONS - // ============================================ - - {Code: `true && true && true;`}, - {Code: `true || false || true;`}, - {Code: `(true && false) || (false && true);`}, - {Code: `true ? (false || true) : (true && false);`}, - - // Mixed types with default options - {Code: `'' || 0 || false;`}, - {Code: `'foo' && 1 && true;`}, - - // ============================================ + `}, + + // ======================================== + // ARRAY PREDICATE FUNCTIONS + // ======================================== + {Code: `['one', 'two', ''].some(x => x);`}, + {Code: `['one', 'two', ''].find(x => x);`}, + {Code: `['one', 'two', ''].every(x => x);`}, + {Code: `['one', 'two', ''].filter((x): boolean => x);`}, + {Code: `['one', 'two', ''].filter(x => Boolean(x));`}, + {Code: `['one', 'two', ''].filter(function (x): boolean { if (x) { return true; } });`}, + {Code: `['one', 'two', ''].filter(function (x): boolean { if (x) { return true; } throw new Error('oops'); });`}, + {Code: `declare const predicate: (string) => boolean; ['one', 'two', ''].filter(predicate);`}, + {Code: `declare function notNullish(x: T): x is NonNullable; ['one', null].filter(notNullish);`}, + {Code: `declare function predicate(x: string | null): x is string; ['one', null].filter(predicate);`}, + {Code: `declare function predicate(x: string | null): T; ['one', null].filter(predicate);`}, + {Code: `declare function f(x: number): boolean; declare function f(x: string | null): boolean; [35].filter(f);`}, + + // ======================================== // SPECIAL CASES - // ============================================ - - // Always allow boolean in right side of logical operators - {Code: `'foo' && true;`}, - {Code: `0 || false;`}, - - // Template literals - {Code: "declare const x: string; `foo${x}` ? 'a' : 'b';"}, - {Code: "declare const x: number; `foo${x}` ? 'a' : 'b';"}, - - // Parenthesized expressions - {Code: `(true) ? 'a' : 'b';`}, - {Code: `((true)) ? 'a' : 'b';`}, - {Code: `if ((true)) {}`}, - - // Comma operator - {Code: `(0, true) ? 'a' : 'b';`}, - {Code: `('', false) ? 'a' : 'b';`}, - - // Assignment expressions - {Code: `let x; (x = true) ? 'a' : 'b';`}, - {Code: `let x; if (x = false) {}`}, - - // Never type - allowed per TypeScript ESLint - {Code: `declare const x: never; if (x) {}`}, - {Code: `declare const x: never; x ? 'a' : 'b';`}, - {Code: `declare const x: never; !x;`}, + // ======================================== + {Code: `for (let x = 0; ; x++) { break; }`}, }, []rule_tester.InvalidTestCase{ - // ============================================ - // ANY TYPE - Invalid without Option - // ============================================ - + // ======================================== + // NON-BOOLEAN IN RHS OF TEST EXPRESSION + // ======================================== { - Code: `declare const x: any; if (x) {}`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedAny", Line: 1}, + Code: `if (true && 1 + 1) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(false), + AllowNumber: utils.Ref(false), + AllowString: utils.Ref(false), }, - }, - { - Code: `declare const x: any; x ? 'a' : 'b';`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedAny", Line: 1}, + { + MessageId: "unexpectedNumber", + Line: 1, + Column: 13, + }, }, }, { - Code: `declare const x: any; while (x) {}`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedAny", Line: 1}, + Code: `while (false || 'a' + 'b') {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(false), + AllowNumber: utils.Ref(false), + AllowString: utils.Ref(false), }, - }, - { - Code: `declare const x: any; do {} while (x);`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedAny", Line: 1}, + { + MessageId: "unexpectedString", + Line: 1, + Column: 17, + }, }, }, { - Code: `declare const x: any; for (; x; ) {}`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedAny", Line: 1}, + Code: `(x: object) => (true || false || x ? true : false);`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(false), + AllowNumber: utils.Ref(false), + AllowString: utils.Ref(false), }, - }, - { - Code: `declare const x: any; x && 'a';`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedAny", Line: 1}, + { + MessageId: "unexpectedObjectContext", + Line: 1, + Column: 34, + }, }, }, + + // ======================================== + // CHECK OUTERMOST OPERANDS + // ======================================== { - Code: `declare const x: any; x || 'a';`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedAny", Line: 1}, + Code: `if (('' && {}) || (0 && void 0)) { }`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(false), + AllowNumber: utils.Ref(false), + AllowString: utils.Ref(false), }, - }, - { - Code: `declare const x: any; !x;`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedAny", Line: 1}, + {MessageId: "unexpectedString", Line: 1, Column: 6}, + {MessageId: "unexpectedObjectContext", Line: 1, Column: 12}, + {MessageId: "unexpectedNumber", Line: 1, Column: 20}, + {MessageId: "unexpectedNullish", Line: 1, Column: 25}, }, }, - // ============================================ - // NULLISH VALUES - Always Invalid - // ============================================ - + // ======================================== + // ARRAY PREDICATE WITH NON-BOOLEAN + // ======================================== { - Code: `if (null) {}`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1}, + Code: `declare const array: string[]; array.some(x => x);`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableBoolean: utils.Ref(true), + AllowString: utils.Ref(false), }, - }, - { - Code: `if (undefined) {}`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1}, - }, - }, - { - Code: `null ? 'a' : 'b';`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1}, + {MessageId: "unexpectedString", Line: 1}, }, }, + + // ======================================== + // BRANDED TYPES + // ======================================== { - Code: `undefined ? 'a' : 'b';`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1}, + Code: ` + declare const foo: true & { __BRAND: 'Foo' }; + if (('' && foo) || (0 && void 0)) { } + `, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(false), + AllowNumber: utils.Ref(false), + AllowString: utils.Ref(false), }, - }, - { - Code: `while (null) {}`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1}, + {MessageId: "unexpectedString", Line: 3, Column: 6}, + {MessageId: "unexpectedNumber", Line: 3, Column: 21}, + {MessageId: "unexpectedNullish", Line: 3, Column: 26}, }, }, { - Code: `do {} while (undefined);`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1}, + Code: ` + declare const foo: false & { __BRAND: 'Foo' }; + if (('' && {}) || (foo && void 0)) { } + `, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(false), + AllowNumber: utils.Ref(false), + AllowString: utils.Ref(false), }, - }, - { - Code: `for (; null; ) {}`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1}, + {MessageId: "unexpectedString", Line: 3, Column: 6}, + {MessageId: "unexpectedObjectContext", Line: 3, Column: 12}, + {MessageId: "unexpectedNullish", Line: 3, Column: 27}, }, }, + + // ======================================== + // LOGICAL OPERANDS FOR CONTROL FLOW + // ======================================== { - Code: `!null;`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1}, + Code: `'asd' && 123 && [] && null;`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + AllowString: utils.Ref(false), }, - }, - { - Code: `!undefined;`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1}, + {MessageId: "unexpectedString", Line: 1, Column: 1}, + {MessageId: "unexpectedNumber", Line: 1, Column: 10}, + {MessageId: "unexpectedObjectContext", Line: 1, Column: 17}, }, }, { - Code: `null && 'a';`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1}, + Code: `'asd' || 123 || [] || null;`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + AllowString: utils.Ref(false), }, - }, - { - Code: `undefined || 'a';`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1}, + {MessageId: "unexpectedString", Line: 1, Column: 1}, + {MessageId: "unexpectedNumber", Line: 1, Column: 10}, + {MessageId: "unexpectedObjectContext", Line: 1, Column: 17}, }, }, - - // ============================================ - // STRING TYPE - Invalid with allowString: false - // ============================================ - { - Code: `if ('') {}`, + Code: `let x = (1 && 'a' && null) || 0 || '' || {};`, Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1, Column: 10}, + {MessageId: "unexpectedString", Line: 1, Column: 15}, + {MessageId: "unexpectedNullish", Line: 1, Column: 22}, + {MessageId: "unexpectedNumber", Line: 1, Column: 31}, + {MessageId: "unexpectedString", Line: 1, Column: 36}, }, }, { - Code: `if ('foo') {}`, + Code: `return (1 || 'a' || null) && 0 && '' && {};`, Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1, Column: 9}, + {MessageId: "unexpectedString", Line: 1, Column: 14}, + {MessageId: "unexpectedNullish", Line: 1, Column: 21}, + {MessageId: "unexpectedNumber", Line: 1, Column: 30}, + {MessageId: "unexpectedString", Line: 1, Column: 35}, }, }, { - Code: `'' ? 'a' : 'b';`, + Code: `console.log((1 && []) || ('a' && {}));`, Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1, Column: 14}, + {MessageId: "unexpectedObjectContext", Line: 1, Column: 19}, + {MessageId: "unexpectedString", Line: 1, Column: 27}, }, }, + + // ======================================== + // CONDITIONALS WITH ALL OPERANDS CHECKED + // ======================================== { - Code: `'foo' ? 'a' : 'b';`, + Code: `if ((1 && []) || ('a' && {})) void 0;`, Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1, Column: 6}, + {MessageId: "unexpectedObjectContext", Line: 1, Column: 11}, + {MessageId: "unexpectedString", Line: 1, Column: 19}, + {MessageId: "unexpectedObjectContext", Line: 1, Column: 26}, }, }, { - Code: `while ('') {}`, + Code: `let x = null || 0 || 'a' || [] ? {} : undefined;`, Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedNullish", Line: 1, Column: 9}, + {MessageId: "unexpectedNumber", Line: 1, Column: 17}, + {MessageId: "unexpectedString", Line: 1, Column: 22}, + {MessageId: "unexpectedObjectContext", Line: 1, Column: 29}, }, }, { - Code: `do {} while ('foo');`, + Code: `return !(null || 0 || 'a' || []);`, Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedNullish", Line: 1, Column: 10}, + {MessageId: "unexpectedNumber", Line: 1, Column: 18}, + {MessageId: "unexpectedString", Line: 1, Column: 23}, + {MessageId: "unexpectedObjectContext", Line: 1, Column: 30}, }, }, + + // ======================================== + // NULLISH VALUES IN BOOLEAN CONTEXT + // ======================================== { - Code: `for (; 'foo'; ) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), - }, + Code: `null || {};`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedNullish", Line: 1, Column: 1}, }, }, { - Code: `!'foo';`, - Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), - }, + Code: `undefined && [];`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedNullish", Line: 1, Column: 1}, }, }, { - Code: `'foo' && 'bar';`, - Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), - }, + Code: `declare const x: null; if (x) {}`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, - {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedNullish", Line: 1}, }, }, { - Code: `'' || 'default';`, - Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), + Code: `(x: undefined) => !x;`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullish", Line: 1, Column: 20}, }, + }, + { + Code: `(x: T) => (x ? 1 : 0);`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, - {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedNullish", Line: 1, Column: 40}, }, }, { - Code: `declare const s: string; if (s) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullish", Line: 1, Column: 28}, }, + }, + { + Code: `(x: T) => (x ? 1 : 0);`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedNullish", Line: 1, Column: 33}, }, }, - // Template literals + // ======================================== + // OBJECT IN BOOLEAN CONTEXT + // ======================================== { - Code: "`` ? 'a' : 'b';", - Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), - }, + Code: `[] || 1;`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedObjectContext", Line: 1, Column: 1}, }, }, { - Code: "`foo` ? 'a' : 'b';", - Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), - }, + Code: `({}) && 'a';`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedObjectContext", Line: 1, Column: 2}, }, }, { - Code: "declare const x: string; `foo${x}` ? 'a' : 'b';", - Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), - }, + Code: `declare const x: symbol; if (x) {}`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedObjectContext", Line: 1}, }, }, - - // ============================================ - // NUMBER TYPE - Invalid with allowNumber: false - // ============================================ - { - Code: `if (0) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - }, + Code: `(x: () => void) => !x;`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedObjectContext", Line: 1, Column: 21}, }, }, { - Code: `if (1) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - }, + Code: `(x: T) => (x ? 1 : 0);`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedObjectContext", Line: 1, Column: 30}, }, }, { - Code: `0 ? 'a' : 'b';`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - }, + Code: `(x: T) => (x ? 1 : 0);`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedObjectContext", Line: 1, Column: 41}, }, }, { - Code: `1 ? 'a' : 'b';`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1, Column: 37}, }, + }, + { + Code: ` void>(x: T) => (x ? 1 : 0);`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedObjectContext", Line: 1, Column: 34}, }, }, + + // ======================================== + // STRING IN BOOLEAN CONTEXT WITH ALLOWSTRING: FALSE + // ======================================== { - Code: `NaN ? 'a' : 'b';`, + Code: `while ('') {}`, Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), + AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedString", Line: 1, Column: 8}, }, }, { - Code: `while (0) {}`, + Code: `for (; 'foo'; ) {}`, Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), + AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedString", Line: 1, Column: 8}, }, }, { - Code: `do {} while (1);`, + Code: `declare const x: string; if (x) {}`, Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), + AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedString", Line: 1}, }, }, { - Code: `for (; 1; ) {}`, + Code: `(x: string) => !x;`, Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), + AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedString", Line: 1, Column: 17}, }, }, { - Code: `!0;`, + Code: `(x: T) => (x ? 1 : 0);`, Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), + AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedString", Line: 1, Column: 30}, }, }, + + // ======================================== + // NUMBER IN BOOLEAN CONTEXT WITH ALLOWNUMBER: FALSE + // ======================================== { - Code: `1 && 2;`, + Code: `while (0n) {}`, Options: StrictBooleanExpressionsOptions{ AllowNumber: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, - {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1, Column: 8}, }, }, { - Code: `0 || 1;`, + Code: `for (; 123; ) {}`, Options: StrictBooleanExpressionsOptions{ AllowNumber: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, - {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1, Column: 8}, }, }, { - Code: `declare const n: number; if (n) {}`, + Code: `declare const x: number; if (x) {}`, Options: StrictBooleanExpressionsOptions{ AllowNumber: utils.Ref(false), }, @@ -902,37 +748,35 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { {MessageId: "unexpectedNumber", Line: 1}, }, }, - - // BigInt { - Code: `if (0n) {}`, + Code: `(x: bigint) => !x;`, Options: StrictBooleanExpressionsOptions{ AllowNumber: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1, Column: 17}, }, }, { - Code: `if (1n) {}`, + Code: `(x: T) => (x ? 1 : 0);`, Options: StrictBooleanExpressionsOptions{ AllowNumber: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1, Column: 30}, }, }, { - Code: `0n ? 'a' : 'b';`, + Code: `![]['length'];`, Options: StrictBooleanExpressionsOptions{ AllowNumber: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1, Column: 2}, }, }, { - Code: `declare const b: bigint; if (b) {}`, + Code: `declare const a: any[] & { notLength: number }; if (a.notLength) {}`, Options: StrictBooleanExpressionsOptions{ AllowNumber: utils.Ref(false), }, @@ -941,173 +785,136 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, }, - // ============================================ - // OBJECT TYPE - Always Invalid (Always Truthy) - // ============================================ - + // ======================================== + // ARRAY.LENGTH IN BOOLEAN CONTEXT + // ======================================== { - Code: `if ({}) {}`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, - }, - }, - { - Code: `if ([]) {}`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, + Code: `if (![].length) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), }, - }, - { - Code: `({}) ? 'a' : 'b';`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1, Column: 6}, }, }, { - Code: `[] ? 'a' : 'b';`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, + Code: `(a: number[]) => a.length && '...';`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), }, - }, - { - Code: `while ({}) {}`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1, Column: 18}, }, }, { - Code: `do {} while ([]);`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, + Code: `(...a: T) => a.length || 'empty';`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), }, - }, - { - Code: `for (; {}; ) {}`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1, Column: 35}, }, }, + + // ======================================== + // MIXED STRING | NUMBER VALUE IN BOOLEAN CONTEXT + // ======================================== { - Code: `!{};`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, + Code: `declare const x: string | number; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(true), + AllowString: utils.Ref(true), }, - }, - { - Code: `![];`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, + {MessageId: "unexpectedMixedCondition", Line: 1}, }, }, { - Code: `({}) && 'a';`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, + Code: `(x: bigint | string) => !x;`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(true), + AllowString: utils.Ref(true), }, - }, - { - Code: `[] || 'a';`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, + {MessageId: "unexpectedMixedCondition", Line: 1, Column: 26}, }, }, { - Code: `declare const o: object; if (o) {}`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, + Code: `(x: T) => (x ? 1 : 0);`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(true), + AllowString: utils.Ref(true), }, - }, - { - Code: `declare const o: {}; if (o) {}`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, + {MessageId: "unexpectedMixedCondition", Line: 1, Column: 48}, }, }, - // Functions + // ======================================== + // NULLABLE BOOLEAN WITHOUT OPTION + // ======================================== { - Code: `function foo() {}; if (foo) {}`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, + Code: `declare const x: boolean | null; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableBoolean: utils.Ref(false), }, - }, - { - Code: `const foo = () => {}; if (foo) {}`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, + {MessageId: "unexpectedNullableBoolean", Line: 1}, }, }, - - // ============================================ - // ASSERT FUNCTIONS AND TYPE PREDICATES - // ============================================ - { - Code: ` - declare function assert(a: boolean, b: unknown): asserts b; - declare function assert({ a }: { a: boolean }, b: unknown): asserts b; - declare const nullableString: string | null; - declare const boo: boolean; - assert(boo, nullableString); - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 6}, + Code: `(x?: boolean) => !x;`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableBoolean: utils.Ref(false), }, - }, - - // Symbols - { - Code: `if (Symbol()) {}`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, + {MessageId: "unexpectedNullableBoolean", Line: 1, Column: 19}, }, }, { - Code: `declare const s: symbol; if (s) {}`, + Code: `(x: T) => (x ? 1 : 0);`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableBoolean: utils.Ref(false), + }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, + {MessageId: "unexpectedNullableBoolean", Line: 1, Column: 50}, }, }, - // ============================================ - // NULLABLE BOOLEAN - Invalid without Option - // ============================================ - + // ======================================== + // NULLABLE OBJECT WITHOUT OPTION + // ======================================== { - Code: `declare const x: boolean | null; if (x) {}`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableBoolean", Line: 1}, + Code: `declare const x: object | null; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(false), }, - }, - { - Code: `declare const x: boolean | undefined; if (x) {}`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableBoolean", Line: 1}, + {MessageId: "unexpectedNullableObject", Line: 1}, }, }, { - Code: `declare const x: boolean | null | undefined; if (x) {}`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableBoolean", Line: 1}, + Code: `(x?: { a: number }) => !x;`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(false), }, - }, - { - Code: `declare const x: true | null; x ? 'a' : 'b';`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableBoolean", Line: 1}, + {MessageId: "unexpectedNullableObject", Line: 1, Column: 25}, }, }, { - Code: `declare const x: false | undefined; !x;`, + Code: `(x: T) => (x ? 1 : 0);`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(false), + }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableBoolean", Line: 1}, + {MessageId: "unexpectedNullableObject", Line: 1, Column: 45}, }, }, - // ============================================ - // NULLABLE STRING - Invalid without Option - // ============================================ - + // ======================================== + // NULLABLE STRING WITHOUT OPTION + // ======================================== { Code: `declare const x: string | null; if (x) {}`, Errors: []rule_tester.InvalidTestCaseError{ @@ -1115,34 +922,27 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, }, { - Code: `declare const x: string | undefined; if (x) {}`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 1}, - }, - }, - { - Code: `declare const x: string | null | undefined; if (x) {}`, + Code: `(x?: string) => !x;`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 1}, + {MessageId: "unexpectedNullableString", Line: 1, Column: 18}, }, }, { - Code: `declare const x: '' | null; x ? 'a' : 'b';`, + Code: `(x: T) => (x ? 1 : 0);`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 1}, + {MessageId: "unexpectedNullableString", Line: 1, Column: 49}, }, }, { - Code: `declare const x: 'foo' | undefined; !x;`, + Code: `function foo(x: '' | 'bar' | null) { if (!x) {} }`, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableString", Line: 1}, }, }, - // ============================================ - // NULLABLE NUMBER - Invalid without Option - // ============================================ - + // ======================================== + // NULLABLE NUMBER WITHOUT OPTION + // ======================================== { Code: `declare const x: number | null; if (x) {}`, Errors: []rule_tester.InvalidTestCaseError{ @@ -1150,220 +950,254 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, }, { - Code: `declare const x: number | undefined; if (x) {}`, + Code: `(x?: number) => !x;`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableNumber", Line: 1}, + {MessageId: "unexpectedNullableNumber", Line: 1, Column: 18}, }, }, { - Code: `declare const x: number | null | undefined; if (x) {}`, + Code: `(x: T) => (x ? 1 : 0);`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableNumber", Line: 1}, + {MessageId: "unexpectedNullableNumber", Line: 1, Column: 49}, }, }, { - Code: `declare const x: 0 | null; x ? 'a' : 'b';`, + Code: `function foo(x: 0 | 1 | null) { if (!x) {} }`, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableNumber", Line: 1}, }, }, + + // ======================================== + // NULLABLE ENUM WITHOUT OPTION + // ======================================== { - Code: `declare const x: 1 | undefined; !x;`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableNumber", Line: 1}, + Code: ` + enum ExampleEnum { This = 0, That = 1 } + const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + if (theEnum) {} + `, + Options: StrictBooleanExpressionsOptions{ + AllowNullableEnum: utils.Ref(false), }, - }, - { - Code: `declare const x: bigint | null; if (x) {}`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableNumber", Line: 1}, + {MessageId: "unexpectedNullableNumber", Line: 4}, }, }, - - // ============================================ - // NULLABLE OBJECT - Invalid without Option - // ============================================ - { - Code: `declare const x: object | null; if (x) {}`, + Code: ` + enum ExampleEnum { This = 0, That = 1 } + const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + if (!theEnum) {} + `, + Options: StrictBooleanExpressionsOptions{ + AllowNullableEnum: utils.Ref(false), + }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableObject", Line: 1}, + {MessageId: "unexpectedNullableNumber", Line: 4}, }, }, { - Code: `declare const x: object | undefined; if (x) {}`, + Code: ` + enum ExampleEnum { This, That } + const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + if (!theEnum) {} + `, + Options: StrictBooleanExpressionsOptions{ + AllowNullableEnum: utils.Ref(false), + }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableObject", Line: 1}, + {MessageId: "unexpectedNullableNumber", Line: 4}, }, }, { - Code: `declare const x: object | null | undefined; if (x) {}`, + Code: ` + enum ExampleEnum { This = '', That = 'a' } + const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + if (!theEnum) {} + `, + Options: StrictBooleanExpressionsOptions{ + AllowNullableEnum: utils.Ref(false), + }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableObject", Line: 1}, + {MessageId: "unexpectedNullableString", Line: 4}, }, }, { - Code: `declare const x: {} | null; x ? 'a' : 'b';`, + Code: ` + enum ExampleEnum { This = '', That = 0 } + const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + if (!theEnum) {} + `, + Options: StrictBooleanExpressionsOptions{ + AllowNullableEnum: utils.Ref(false), + }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableObject", Line: 1}, + {MessageId: "unexpectedMixedCondition", Line: 4}, }, }, { - Code: `declare const x: [] | undefined; !x;`, + Code: ` + enum ExampleEnum { This = 'one', That = 'two' } + const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + if (!theEnum) {} + `, + Options: StrictBooleanExpressionsOptions{ + AllowNullableEnum: utils.Ref(false), + }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableObject", Line: 1}, + {MessageId: "unexpectedNullableString", Line: 4}, }, }, { - Code: `declare const x: symbol | null; if (x) {}`, + Code: ` + enum ExampleEnum { This = 1, That = 2 } + const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + if (!theEnum) {} + `, + Options: StrictBooleanExpressionsOptions{ + AllowNullableEnum: utils.Ref(false), + }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableObject", Line: 1}, + {MessageId: "unexpectedNullableNumber", Line: 4}, }, }, - // ============================================ - // MIXED TYPES - Invalid - // ============================================ - + // ======================================== + // NULLABLE MIXED ENUM WITHOUT OPTION + // ======================================== { - Code: `declare const x: string | number; if (x) {}`, + Code: ` + enum ExampleEnum { This = 0, That = 'one' } + (value?: ExampleEnum) => (value ? 1 : 0); + `, Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), - AllowNumber: utils.Ref(false), + AllowNullableEnum: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedMixedCondition", Line: 1}, + {MessageId: "unexpectedMixedCondition", Line: 3}, }, }, { - Code: `declare const x: string | boolean; if (x) {}`, + Code: ` + enum ExampleEnum { This = '', That = 1 } + (value?: ExampleEnum) => (!value ? 1 : 0); + `, Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), + AllowNullableEnum: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedMixedCondition", Line: 1}, + {MessageId: "unexpectedMixedCondition", Line: 3}, }, }, { - Code: `declare const x: number | boolean; if (x) {}`, + Code: ` + enum ExampleEnum { This = 'this', That = 1 } + (value?: ExampleEnum) => (value ? 1 : 0); + `, Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), + AllowNullableEnum: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedMixedCondition", Line: 1}, + {MessageId: "unexpectedMixedCondition", Line: 3}, }, }, + + // ======================================== + // ANY WITHOUT OPTION + // ======================================== { - Code: `declare const x: string | number | boolean; if (x) {}`, + Code: `if ((Boolean(x) || {}) || (typeof x === 'string' && x)) {}`, Options: StrictBooleanExpressionsOptions{ AllowString: utils.Ref(false), - AllowNumber: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedMixedCondition", Line: 1}, + {MessageId: "unexpectedObjectContext", Line: 1, Column: 20}, + {MessageId: "unexpectedString", Line: 1, Column: 53}, }, }, - - // ============================================ - // ENUM TYPES - // ============================================ - { - Code: `enum E { A = 0, B = 1 } declare const x: E; if (x) {}`, + Code: `if (1) {}`, Options: StrictBooleanExpressionsOptions{ AllowNumber: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1, Column: 5}, }, }, + + // ======================================== + // ASSERT FUNCTIONS + // ======================================== { - Code: `enum E { A = '', B = 'foo' } declare const x: E; if (x) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), - }, + Code: ` + declare function assert(a: boolean, b: unknown): asserts b; + declare function assert({ a }: { a: boolean }, b: unknown): asserts b; + declare const nullableString: string | null; + declare const boo: boolean; + assert(boo, nullableString); + `, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedNullableString", Line: 6}, }, }, - // ============================================ - // ARRAY METHOD PREDICATES - Invalid - // ============================================ - + // ======================================== + // ARRAY FILTER PREDICATES + // ======================================== { - Code: `[1, 2, 3].filter(x => x);`, + Code: `declare const nullOrBool: boolean | null; [true, false, null].filter(x => nullOrBool);`, Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), + AllowNullableBoolean: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedNullableBoolean", Line: 1}, }, }, { - Code: `['', 'foo'].filter(x => x);`, - Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), - }, + Code: `declare const nullOrString: string | null; ['', 'foo', null].filter(x => nullOrString);`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedNullableString", Line: 1}, }, }, { - Code: `[{}, []].filter(x => x);`, + Code: `declare const nullOrNumber: number | null; [0, null].filter(x => nullOrNumber);`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, + {MessageId: "unexpectedNullableNumber", Line: 1}, }, }, - - // ============================================ - // COMPLEX LOGICAL EXPRESSIONS - Invalid - // ============================================ - { - Code: `'foo' && 1;`, - Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), - }, + Code: `const objectValue: object = {}; [{ a: 0 }, {}].filter(x => objectValue);`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedObjectContext", Line: 1}, }, }, { - Code: `0 || 'bar';`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - }, + Code: `const objectValue: object = {}; [{ a: 0 }, {}].filter(x => { return objectValue; });`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedObjectContext", Line: 1}, }, }, { - Code: `({}) && [];`, + Code: `declare const nullOrObject: object | null; [{ a: 0 }, null].filter(x => nullOrObject);`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(false), + }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, - {MessageId: "unexpectedObjectContext", Line: 1}, + {MessageId: "unexpectedNullableObject", Line: 1}, }, }, { - Code: `'' || 0;`, + Code: `const numbers: number[] = [1]; [1, 2].filter(x => numbers.length);`, Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), AllowNumber: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, {MessageId: "unexpectedNumber", Line: 1}, }, }, - - // ============================================ - // SPECIAL CASES - Invalid - // ============================================ - - // Array.length { - Code: `declare const arr: string[]; if (arr.length) {}`, + Code: `const numberValue: number = 1; [1, 2].filter(x => numberValue);`, Options: StrictBooleanExpressionsOptions{ AllowNumber: utils.Ref(false), }, @@ -1371,10 +1205,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { {MessageId: "unexpectedNumber", Line: 1}, }, }, - - // Function calls returning non-boolean { - Code: `declare function getString(): string; if (getString()) {}`, + Code: `const stringValue: string = 'hoge'; ['hoge', 'foo'].filter(x => stringValue);`, Options: StrictBooleanExpressionsOptions{ AllowString: utils.Ref(false), }, @@ -1382,54 +1214,54 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { {MessageId: "unexpectedString", Line: 1}, }, }, + + // ======================================== + // UNKNOWN TYPE WITHOUT OPTION + // ======================================== { - Code: `declare function getNumber(): number; if (getNumber()) {}`, + Code: `declare const x: unknown; if (x) {}`, Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), + AllowAny: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedAny", Line: 1}, }, }, { - Code: `declare function getObject(): object; if (getObject()) {}`, + Code: `declare const x: unknown; x ? 'a' : 'b';`, + Options: StrictBooleanExpressionsOptions{ + AllowAny: utils.Ref(false), + }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, + {MessageId: "unexpectedAny", Line: 1}, }, }, - - // Property access { - Code: `declare const obj: { prop: string }; if (obj.prop) {}`, + Code: `declare const x: unknown; x && 'a';`, Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), + AllowAny: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedAny", Line: 1}, }, }, { - Code: `declare const obj: { prop: number }; if (obj.prop) {}`, + Code: `declare const x: unknown; x || 'a';`, Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), + AllowAny: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedAny", Line: 1}, }, }, - - // Void type { - Code: `declare const x: void; if (x) {}`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1}, + Code: `declare const x: unknown; !x;`, + Options: StrictBooleanExpressionsOptions{ + AllowAny: utils.Ref(false), }, - }, - { - Code: `void 0 ? 'a' : 'b';`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1}, + {MessageId: "unexpectedAny", Line: 1}, }, }, }) -} +} \ No newline at end of file From ed1e5f467dbeea212bc005cd8927c8c5a7ae9674 Mon Sep 17 00:00:00 2001 From: Noel Kim Date: Mon, 29 Sep 2025 12:18:12 +0900 Subject: [PATCH 07/27] wip --- .../strict_boolean_expressions.go | 25 ++++++++++++------- .../strict_boolean_expressions_single_test.go | 2 +- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go index 1d8d8676..3f66225f 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go @@ -97,6 +97,8 @@ func buildNoStrictNullCheck() rule.RuleMessage { } } +var traversedNodes = utils.Set[*ast.Node]{} + var StrictBooleanExpressionsRule = rule.Rule{ Name: "strict-boolean-expressions", Run: func(ctx rule.RuleContext, options any) rule.RuleListeners { @@ -121,7 +123,7 @@ var StrictBooleanExpressionsRule = rule.Rule{ opts.AllowNullableEnum = utils.Ref(false) } if opts.AllowNullableObject == nil { - opts.AllowNullableObject = utils.Ref(false) + opts.AllowNullableObject = utils.Ref(true) } if opts.AllowString == nil { opts.AllowString = utils.Ref(true) @@ -167,7 +169,10 @@ var StrictBooleanExpressionsRule = rule.Rule{ traverseNode(ctx, condExpr.Condition, opts, true) }, ast.KindBinaryExpression: func(node *ast.Node) { - traverseNode(ctx, node, opts, false) + binExpr := node.AsBinaryExpression() + if binExpr.OperatorToken.Kind != ast.KindQuestionQuestionToken { + traverseLogicalExpression(ctx, binExpr, opts, false) + } }, ast.KindCallExpression: func(node *ast.Node) { callExpr := node.AsCallExpression() @@ -262,10 +267,16 @@ func traverseLogicalExpression(ctx rule.RuleContext, binExpr *ast.BinaryExpressi } func traverseNode(ctx rule.RuleContext, node *ast.Node, opts StrictBooleanExpressionsOptions, isCondition bool) { + if traversedNodes.Has(node) { + return + } + traversedNodes.Add(node) + if node.Kind == ast.KindBinaryExpression { binExpr := node.AsBinaryExpression() if binExpr.OperatorToken.Kind != ast.KindQuestionQuestionToken { - traverseLogicalExpression(ctx, binExpr, opts, false) + traverseLogicalExpression(ctx, binExpr, opts, isCondition) + return } } @@ -364,7 +375,7 @@ func analyzeType(typeChecker *checker.Checker, t *checker.Type) typeInfo { return analyzeTypePart(typeChecker, t) } -func analyzeTypePart(typeChecker *checker.Checker, t *checker.Type) typeInfo { +func analyzeTypePart(_ *checker.Checker, t *checker.Type) typeInfo { info := typeInfo{} flags := checker.Type_flags(t) @@ -394,7 +405,7 @@ func analyzeTypePart(typeChecker *checker.Checker, t *checker.Type) typeInfo { } if flags&(checker.TypeFlagsBoolean|checker.TypeFlagsBooleanLiteral|checker.TypeFlagsBooleanLike) != 0 { - if flags&checker.TypeFlagsBooleanLike != 0 && t.AsIntrinsicType().IntrinsicName() == "true" { + if utils.IsTrueLiteralType(t) { info.isTruthy = true } info.variant = typeVariantBoolean @@ -450,10 +461,6 @@ func analyzeTypePart(typeChecker *checker.Checker, t *checker.Type) typeInfo { } func checkCondition(ctx rule.RuleContext, node *ast.Node, t *checker.Type, opts StrictBooleanExpressionsOptions) { - if isBooleanType(t) { - return - } - info := analyzeType(ctx.TypeChecker, t) switch info.variant { diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go index 6180fe5b..69822971 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go @@ -11,6 +11,6 @@ import ( func TestStrictBooleanExpressionsSingleRule(t *testing.T) { rule_tester.RunRuleTester(fixtures.GetRootDir(), "tsconfig.json", t, &StrictBooleanExpressionsRule, []rule_tester.ValidTestCase{ - {Code: `declare const x: null | object; if (x) {}`}, + {Code: `declare const test?: boolean; if (test ?? false) {}`}, }, []rule_tester.InvalidTestCase{}) } \ No newline at end of file From 1a09f8191bf39658693462d573cd4e32e5e31462 Mon Sep 17 00:00:00 2001 From: Noel Kim Date: Mon, 29 Sep 2025 12:23:18 +0900 Subject: [PATCH 08/27] pass valid cases --- .../strict_boolean_expressions_single_test.go | 20 +++++++++++++++++-- .../strict_boolean_expressions_test.go | 2 +- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go index 69822971..1af4738e 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go @@ -7,10 +7,26 @@ import ( "github.com/typescript-eslint/tsgolint/internal/rule_tester" "github.com/typescript-eslint/tsgolint/internal/rules/fixtures" + "github.com/typescript-eslint/tsgolint/internal/utils" ) func TestStrictBooleanExpressionsSingleRule(t *testing.T) { rule_tester.RunRuleTester(fixtures.GetRootDir(), "tsconfig.json", t, &StrictBooleanExpressionsRule, []rule_tester.ValidTestCase{ - {Code: `declare const test?: boolean; if (test ?? false) {}`}, - }, []rule_tester.InvalidTestCase{}) + }, []rule_tester.InvalidTestCase{ + { + Code: `if (true && 1 + 1) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(false), + AllowNumber: utils.Ref(false), + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "unexpectedNumber", + Line: 1, + Column: 13, + }, + }, + }, + }) } \ No newline at end of file diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go index 82007171..479b713a 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go @@ -83,7 +83,7 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, }, { - Code: `declare const test?: boolean; if (test ?? false) {}`, + Code: `const a: (undefined | boolean | null)[] = [true, undefined, null]; a.some(x => x);`, Options: StrictBooleanExpressionsOptions{ AllowNullableBoolean: utils.Ref(true), }, From b45d6e7c48013a3fcdc7f3ed04791922a85c2694 Mon Sep 17 00:00:00 2001 From: Noel Kim Date: Mon, 29 Sep 2025 12:52:31 +0900 Subject: [PATCH 09/27] pass valid cases --- .../strict_boolean_expressions.go | 64 +- .../strict_boolean_expressions_single_test.go | 11 +- .../strict_boolean_expressions_test.go | 2119 +++++++++-------- 3 files changed, 1108 insertions(+), 1086 deletions(-) diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go index 3f66225f..7b42c383 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go @@ -97,6 +97,13 @@ func buildNoStrictNullCheck() rule.RuleMessage { } } +func buildPredicateCannotBeAsync() rule.RuleMessage { + return rule.RuleMessage{ + Id: "predicateCannotBeAsync", + Description: "Predicate function should not be 'async'; expected a boolean return type.", + } +} + var traversedNodes = utils.Set[*ast.Node]{} var StrictBooleanExpressionsRule = rule.Rule{ @@ -170,7 +177,7 @@ var StrictBooleanExpressionsRule = rule.Rule{ }, ast.KindBinaryExpression: func(node *ast.Node) { binExpr := node.AsBinaryExpression() - if binExpr.OperatorToken.Kind != ast.KindQuestionQuestionToken { + if ast.IsLogicalExpression(node) && binExpr.OperatorToken.Kind != ast.KindQuestionQuestionToken { traverseLogicalExpression(ctx, binExpr, opts, false) } }, @@ -182,33 +189,28 @@ var StrictBooleanExpressionsRule = rule.Rule{ traverseNode(ctx, assertedArgument, opts, true) } - if callExpr.Expression != nil && callExpr.Expression.Kind == ast.KindPropertyAccessExpression { - propAccess := callExpr.Expression.AsPropertyAccessExpression() - if propAccess == nil { - return - } - methodName := propAccess.Name().Text() - - switch methodName { - case "filter", "find", "some", "every", "findIndex", "findLast", "findLastIndex": - if callExpr.Arguments != nil && len(callExpr.Arguments.Nodes) > 0 { - arg := callExpr.Arguments.Nodes[0] - if arg != nil && (arg.Kind == ast.KindArrowFunction || arg.Kind == ast.KindFunctionExpression) { - funcType := ctx.TypeChecker.GetTypeAtLocation(arg) - signatures := ctx.TypeChecker.GetCallSignatures(funcType) - for _, signature := range signatures { - returnType := ctx.TypeChecker.GetReturnTypeOfSignature(signature) - typeFlags := checker.Type_flags(returnType) - if typeFlags&checker.TypeFlagsTypeParameter != 0 { - constraint := ctx.TypeChecker.GetConstraintOfTypeParameter(returnType) - if constraint != nil { - returnType = constraint - } + if utils.IsArrayMethodCallWithPredicate(ctx.TypeChecker, callExpr) { + if callExpr.Arguments != nil && len(callExpr.Arguments.Nodes) > 0 { + arg := callExpr.Arguments.Nodes[0] + if arg != nil && (arg.Kind == ast.KindArrowFunction || arg.Kind == ast.KindFunctionExpression || arg.Kind == ast.KindFunctionDeclaration) { + if checker.GetFunctionFlags(arg)&checker.FunctionFlagsAsync != 0 { + ctx.ReportNode(arg, buildPredicateCannotBeAsync()) + return + } + funcType := ctx.TypeChecker.GetTypeAtLocation(arg) + signatures := ctx.TypeChecker.GetCallSignatures(funcType) + for _, signature := range signatures { + returnType := ctx.TypeChecker.GetReturnTypeOfSignature(signature) + typeFlags := checker.Type_flags(returnType) + if typeFlags&checker.TypeFlagsTypeParameter != 0 { + constraint := ctx.TypeChecker.GetConstraintOfTypeParameter(returnType) + if constraint != nil { + returnType = constraint } + } - if returnType != nil && !isBooleanType(returnType) { - checkCondition(ctx, node, returnType, opts) - } + if returnType != nil && !isBooleanType(returnType) { + checkCondition(ctx, node, returnType, opts) } } } @@ -229,7 +231,7 @@ func findTruthinessAssertedArgument(typeChecker *checker.Checker, callExpr *ast. var checkableArguments []*ast.Node for _, argument := range callExpr.Arguments.Nodes { if argument.Kind == ast.KindSpreadElement { - continue + break } checkableArguments = append(checkableArguments, argument) } @@ -245,12 +247,12 @@ func findTruthinessAssertedArgument(typeChecker *checker.Checker, callExpr *ast. if firstTypePredicateResult == nil { return nil } - if checker.TypePredicate_kind(firstTypePredicateResult) != checker.TypePredicateKindAssertsIdentifier || - checker.TypePredicate_t(firstTypePredicateResult) != nil { + if !(checker.TypePredicate_kind(firstTypePredicateResult) == checker.TypePredicateKindAssertsIdentifier && + checker.TypePredicate_t(firstTypePredicateResult) == nil) { return nil } parameterIndex := checker.TypePredicate_parameterIndex(firstTypePredicateResult) - if parameterIndex >= int32(len(checkableArguments)) { + if int(parameterIndex) >= len(checkableArguments) { return nil } return checkableArguments[parameterIndex] @@ -274,7 +276,7 @@ func traverseNode(ctx rule.RuleContext, node *ast.Node, opts StrictBooleanExpres if node.Kind == ast.KindBinaryExpression { binExpr := node.AsBinaryExpression() - if binExpr.OperatorToken.Kind != ast.KindQuestionQuestionToken { + if ast.IsLogicalExpression(node) && binExpr.OperatorToken.Kind != ast.KindQuestionQuestionToken { traverseLogicalExpression(ctx, binExpr, opts, isCondition) return } diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go index 1af4738e..998d3e9b 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go @@ -14,18 +14,17 @@ func TestStrictBooleanExpressionsSingleRule(t *testing.T) { rule_tester.RunRuleTester(fixtures.GetRootDir(), "tsconfig.json", t, &StrictBooleanExpressionsRule, []rule_tester.ValidTestCase{ }, []rule_tester.InvalidTestCase{ { - Code: `if (true && 1 + 1) {}`, + Code: `if (('' && {}) || (0 && void 0)) { }`, Options: StrictBooleanExpressionsOptions{ AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "unexpectedNumber", - Line: 1, - Column: 13, - }, + {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedObjectContext", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedNullish", Line: 1}, }, }, }) diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go index 479b713a..dd471e91 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go @@ -1,5 +1,3 @@ -// Code generated from strict-boolean-expressions.test.ts - DO NOT EDIT. - package strict_boolean_expressions import ( @@ -11,1257 +9,1280 @@ import ( ) func TestStrictBooleanExpressionsRule(t *testing.T) { - rule_tester.RunRuleTester(fixtures.GetRootDir(), "tsconfig.json", t, &StrictBooleanExpressionsRule, []rule_tester.ValidTestCase{ - // ======================================== - // BOOLEAN IN BOOLEAN CONTEXT - // ======================================== - {Code: `true ? 'a' : 'b';`}, - {Code: `if (false) {}`}, - {Code: `while (true) {}`}, - {Code: `for (; false; ) {}`}, - {Code: `!true;`}, - {Code: `false || 123;`}, - {Code: `true && 'foo';`}, - {Code: `!(false || true);`}, - {Code: `true && false ? true : false;`}, - {Code: `(false && true) || false;`}, - {Code: `(false && true) || [];`}, - {Code: `(false && 1) || (true && 2);`}, - {Code: `declare const x: boolean; if (x) {}`}, - {Code: `(x: boolean) => !x;`}, - {Code: `(x: T) => (x ? 1 : 0);`}, - {Code: `declare const x: never; if (x) {}`}, + rule_tester.RunRuleTester( + fixtures.GetRootDir(), + "tsconfig.json", + t, + &StrictBooleanExpressionsRule, + []rule_tester.ValidTestCase{ + // ======================================== + // BOOLEAN IN BOOLEAN CONTEXT + // ======================================== + {Code: `true ? 'a' : 'b';`}, + {Code: `if (false) {}`}, + {Code: `while (true) {}`}, + {Code: `for (; false; ) {}`}, + {Code: `!true;`}, + {Code: `false || 123;`}, + {Code: `true && 'foo';`}, + {Code: `!(false || true);`}, + {Code: `true && false ? true : false;`}, + {Code: `(false && true) || false;`}, + {Code: `(false && true) || [];`}, + {Code: `(false && 1) || (true && 2);`}, + {Code: `declare const x: boolean; if (x) {}`}, + {Code: `(x: boolean) => !x;`}, + {Code: `(x: T) => (x ? 1 : 0);`}, + {Code: `declare const x: never; if (x) {}`}, - // ======================================== - // STRING IN BOOLEAN CONTEXT - // ======================================== - {Code: `if ('') {}`}, - {Code: `while ('x') {}`}, - {Code: `for (; ''; ) {}`}, - {Code: `('' && '1') || x;`}, - {Code: `declare const x: string; if (x) {}`}, - {Code: `(x: string) => !x;`}, - {Code: `(x: T) => (x ? 1 : 0);`}, + // ======================================== + // STRING IN BOOLEAN CONTEXT + // ======================================== + {Code: `if ('') {}`}, + {Code: `while ('x') {}`}, + {Code: `for (; ''; ) {}`}, + {Code: `('' && '1') || x;`}, + {Code: `declare const x: string; if (x) {}`}, + {Code: `(x: string) => !x;`}, + {Code: `(x: T) => (x ? 1 : 0);`}, - // ======================================== - // NUMBER IN BOOLEAN CONTEXT - // ======================================== - {Code: `if (0) {}`}, - {Code: `while (1n) {}`}, - {Code: `for (; Infinity; ) {}`}, - {Code: `(0 / 0 && 1 + 2) || x;`}, - {Code: `declare const x: number; if (x) {}`}, - {Code: `(x: bigint) => !x;`}, - {Code: `(x: T) => (x ? 1 : 0);`}, + // ======================================== + // NUMBER IN BOOLEAN CONTEXT + // ======================================== + {Code: `if (0) {}`}, + {Code: `while (1n) {}`}, + {Code: `for (; Infinity; ) {}`}, + {Code: `(0 / 0 && 1 + 2) || x;`}, + {Code: `declare const x: number; if (x) {}`}, + {Code: `(x: bigint) => !x;`}, + {Code: `(x: T) => (x ? 1 : 0);`}, - // ======================================== - // NULLABLE OBJECT IN BOOLEAN CONTEXT - // ======================================== - {Code: `declare const x: null | object; if (x) {}`}, - {Code: `(x?: { a: any }) => !x;`}, - {Code: `(x: T) => (x ? 1 : 0);`}, + // ======================================== + // NULLABLE OBJECT IN BOOLEAN CONTEXT + // ======================================== + {Code: `declare const x: null | object; if (x) {}`}, + {Code: `(x?: { a: any }) => !x;`}, + {Code: `(x: T) => (x ? 1 : 0);`}, - // ======================================== - // NULLABLE BOOLEAN IN BOOLEAN CONTEXT - // ======================================== - { - Code: `declare const x: boolean | null; if (x) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableBoolean: utils.Ref(true), + // ======================================== + // NULLABLE BOOLEAN IN BOOLEAN CONTEXT + // ======================================== + { + Code: `declare const x: boolean | null; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableBoolean: utils.Ref(true), + }, }, - }, - { - Code: `(x?: boolean) => !x;`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableBoolean: utils.Ref(true), + { + Code: `(x?: boolean) => !x;`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableBoolean: utils.Ref(true), + }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableBoolean: utils.Ref(true), + { + Code: `(x: T) => (x ? 1 : 0);`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableBoolean: utils.Ref(true), + }, }, - }, - { - Code: `const a: (undefined | boolean | null)[] = [true, undefined, null]; a.some(x => x);`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableBoolean: utils.Ref(true), + { + Code: `const a: (undefined | boolean | null)[] = [true, undefined, null]; a.some(x => x);`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableBoolean: utils.Ref(true), + }, }, - }, - // ======================================== - // NULLABLE STRING IN BOOLEAN CONTEXT - // ======================================== - { - Code: `declare const x: string | undefined; if (x) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableString: utils.Ref(true), + // ======================================== + // NULLABLE STRING IN BOOLEAN CONTEXT + // ======================================== + { + Code: `declare const x: string | undefined; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableString: utils.Ref(true), + }, }, - }, - { - Code: `(x?: string) => !x;`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableString: utils.Ref(true), + { + Code: `(x?: string) => !x;`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableString: utils.Ref(true), + }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableString: utils.Ref(true), + { + Code: `(x: T) => (x ? 1 : 0);`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableString: utils.Ref(true), + }, }, - }, - { - Code: `'string' != null ? 'asd' : '';`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableString: utils.Ref(true), + { + Code: `'string' != null ? 'asd' : '';`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableString: utils.Ref(true), + }, }, - }, - // ======================================== - // NULLABLE NUMBER IN BOOLEAN CONTEXT - // ======================================== - { - Code: `declare const x: number | undefined; if (x) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableNumber: utils.Ref(true), + // ======================================== + // NULLABLE NUMBER IN BOOLEAN CONTEXT + // ======================================== + { + Code: `declare const x: number | undefined; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableNumber: utils.Ref(true), + }, }, - }, - { - Code: `(x?: number) => !x;`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableNumber: utils.Ref(true), + { + Code: `(x?: number) => !x;`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableNumber: utils.Ref(true), + }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableNumber: utils.Ref(true), + { + Code: `(x: T) => (x ? 1 : 0);`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableNumber: utils.Ref(true), + }, }, - }, - { - Code: `declare const x: bigint | undefined; if (x ?? 0n) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableNumber: utils.Ref(true), + { + Code: `declare const x: bigint | undefined; if (x ?? 0n) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableNumber: utils.Ref(true), + }, }, - }, - // ======================================== - // ANY TYPE IN BOOLEAN CONTEXT - // ======================================== - { - Code: `declare const x: any; if (x) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(true), + // ======================================== + // ANY TYPE IN BOOLEAN CONTEXT + // ======================================== + { + Code: `declare const x: any; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowAny: utils.Ref(true), + }, }, - }, - { - Code: `(x?: any) => !x;`, - Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(true), + { + Code: `(x?: any) => !x;`, + Options: StrictBooleanExpressionsOptions{ + AllowAny: utils.Ref(true), + }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(true), + { + Code: `(x: T) => (x ? 1 : 0);`, + Options: StrictBooleanExpressionsOptions{ + AllowAny: utils.Ref(true), + }, }, - }, - { - Code: `const foo: undefined | any = 0; if (foo) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(true), + { + Code: `const foo: undefined | any = 0; if (foo) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowAny: utils.Ref(true), + }, }, - }, - // ======================================== - // UNKNOWN TYPE IN BOOLEAN CONTEXT - // ======================================== - { - Code: `declare const x: unknown; if (x) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(true), + // ======================================== + // UNKNOWN TYPE IN BOOLEAN CONTEXT + // ======================================== + { + Code: `declare const x: unknown; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowAny: utils.Ref(true), + }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(true), + { + Code: `(x: T) => (x ? 1 : 0);`, + Options: StrictBooleanExpressionsOptions{ + AllowAny: utils.Ref(true), + }, }, - }, - // ======================================== - // NULLABLE ENUM IN BOOLEAN CONTEXT - // ======================================== - { - Code: ` + // ======================================== + // NULLABLE ENUM IN BOOLEAN CONTEXT + // ======================================== + { + Code: ` enum E { A, B } declare const x: E | null; if (x) {} `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(true), + Options: StrictBooleanExpressionsOptions{ + AllowNullableEnum: utils.Ref(true), + }, }, - }, - { - Code: ` + { + Code: ` enum E { A = 'a', B = 'b' } declare const x: E | undefined; if (x) {} `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(true), + Options: StrictBooleanExpressionsOptions{ + AllowNullableEnum: utils.Ref(true), + }, }, - }, - // ======================================== - // ALLOW RULE TO RUN WITHOUT STRICT NULL CHECKS - // ======================================== - { - Code: `if (true) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: utils.Ref(true), + // ======================================== + // ALLOW RULE TO RUN WITHOUT STRICT NULL CHECKS + // ======================================== + { + Code: `if (true) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: utils.Ref(true), + }, }, - }, - // ======================================== - // COMPLEX BOOLEAN EXPRESSIONS - // ======================================== - {Code: `(x?: boolean) => x ?? false ? true : false;`}, - {Code: `(x: T) => x ?? false;`}, - { - Code: `(x?: string) => x ?? false;`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableString: utils.Ref(true), + // ======================================== + // COMPLEX BOOLEAN EXPRESSIONS + // ======================================== + {Code: `(x?: boolean) => x ?? false ? true : false;`}, + {Code: `(x: T) => x ?? false;`}, + { + Code: `(x?: string) => x ?? false;`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableString: utils.Ref(true), + }, }, - }, - { - Code: `(x?: number) => x ?? false;`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableNumber: utils.Ref(true), + { + Code: `(x?: number) => x ?? false;`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableNumber: utils.Ref(true), + }, }, - }, - // ======================================== - // ASSERT FUNCTIONS - // ======================================== - {Code: ` - declare function assert(condition: unknown): asserts condition; - declare const x: string | null; - assert(x); - `}, - {Code: ` - declare function assert(a: number, b: unknown): asserts a; - declare const nullableString: string | null; - declare const boo: boolean; - assert(boo, nullableString); - `}, - {Code: ` - declare function assert(a: boolean, b: unknown): asserts b is string; - declare const nullableString: string | null; - declare const boo: boolean; - assert(boo, nullableString); - `}, - {Code: ` - declare function assert(a: number, b: unknown): asserts b; - declare const nullableString: string | null; - declare const boo: boolean; - assert(nullableString, boo); - `}, - {Code: ` - declare function assert(a: number, b: unknown): asserts b; - declare const nullableString: string | null; - declare const boo: boolean; - assert(...nullableString, nullableString); - `}, - {Code: ` - declare function assert( - this: object, - a: number, - b?: unknown, - c?: unknown, - ): asserts c; - declare const nullableString: string | null; - declare const foo: number; - const o: { assert: typeof assert } = { assert }; - o.assert(foo, nullableString); - `}, - {Code: ` - declare function assert(x: unknown): x is string; - declare const nullableString: string | null; - assert(nullableString); - `}, - {Code: ` - class ThisAsserter { - assertThis(this: unknown, arg2: unknown): asserts this {} - } - declare const lol: string | number | unknown | null; - const thisAsserter: ThisAsserter = new ThisAsserter(); - thisAsserter.assertThis(lol); - `}, - {Code: ` - function assert(this: object, a: number, b: unknown): asserts b; - function assert(a: bigint, b: unknown): asserts b; - function assert(this: object, a: string, two: string): asserts two; - function assert( - this: object, - a: string, - assertee: string, - c: bigint, - d: object, - ): asserts assertee; - function assert(...args: any[]): void; - function assert(...args: any[]) { - throw new Error('lol'); - } - declare const nullableString: string | null; - assert(3 as any, nullableString); - `}, - {Code: ` - declare const assert: any; - declare const nullableString: string | null; - assert(nullableString); - `}, + // ======================================== + // ASSERT FUNCTIONS + // ======================================== + { + Code: ` +declare function assert(a: number, b: unknown): asserts a; +declare const nullableString: string | null; +declare const boo: boolean; +assert(boo, nullableString); + `, + }, + { + Code: ` +declare function assert(a: boolean, b: unknown): asserts b is string; +declare const nullableString: string | null; +declare const boo: boolean; +assert(boo, nullableString); + `, + }, + { + Code: ` +declare function assert(a: number, b: unknown): asserts b; +declare const nullableString: string | null; +declare const boo: boolean; +assert(nullableString, boo); + `, + }, + { + Code: ` +declare function assert(a: number, b: unknown): asserts b; +declare const nullableString: string | null; +declare const boo: boolean; +assert(...nullableString, nullableString); + `, + }, + { + Code: ` +declare function assert( + this: object, + a: number, + b?: unknown, + c?: unknown, +): asserts c; +declare const nullableString: string | null; +declare const foo: number; +const o: { assert: typeof assert } = { + assert, +}; +o.assert(foo, nullableString); + `, + }, + { + Code: ` +declare function assert(x: unknown): x is string; +declare const nullableString: string | null; +assert(nullableString); + `, + }, + { + Code: ` +class ThisAsserter { + assertThis(this: unknown, arg2: unknown): asserts this {} +} - // ======================================== - // ARRAY PREDICATE FUNCTIONS - // ======================================== - {Code: `['one', 'two', ''].some(x => x);`}, - {Code: `['one', 'two', ''].find(x => x);`}, - {Code: `['one', 'two', ''].every(x => x);`}, - {Code: `['one', 'two', ''].filter((x): boolean => x);`}, - {Code: `['one', 'two', ''].filter(x => Boolean(x));`}, - {Code: `['one', 'two', ''].filter(function (x): boolean { if (x) { return true; } });`}, - {Code: `['one', 'two', ''].filter(function (x): boolean { if (x) { return true; } throw new Error('oops'); });`}, - {Code: `declare const predicate: (string) => boolean; ['one', 'two', ''].filter(predicate);`}, - {Code: `declare function notNullish(x: T): x is NonNullable; ['one', null].filter(notNullish);`}, - {Code: `declare function predicate(x: string | null): x is string; ['one', null].filter(predicate);`}, - {Code: `declare function predicate(x: string | null): T; ['one', null].filter(predicate);`}, - {Code: `declare function f(x: number): boolean; declare function f(x: string | null): boolean; [35].filter(f);`}, +declare const lol: string | number | unknown | null; - // ======================================== - // SPECIAL CASES - // ======================================== - {Code: `for (let x = 0; ; x++) { break; }`}, - }, []rule_tester.InvalidTestCase{ - // ======================================== - // NON-BOOLEAN IN RHS OF TEST EXPRESSION - // ======================================== - { - Code: `if (true && 1 + 1) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "unexpectedNumber", - Line: 1, - Column: 13, - }, - }, - }, - { - Code: `while (false || 'a' + 'b') {}`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), +const thisAsserter: ThisAsserter = new ThisAsserter(); +thisAsserter.assertThis(lol); + `, + }, + { + Code: ` +function assert(this: object, a: number, b: unknown): asserts b; +function assert(a: bigint, b: unknown): asserts b; +function assert(this: object, a: string, two: string): asserts two; +function assert( + this: object, + a: string, + assertee: string, + c: bigint, + d: object, +): asserts assertee; +function assert(...args: any[]): void; + +function assert(...args: any[]) { + throw new Error('lol'); +} + +declare const nullableString: string | null; +assert(3 as any, nullableString); + `, + }, + // Intentional use of `any` to test a function call with no call signatures. + { + Code: ` +declare const assert: any; +declare const nullableString: string | null; +assert(nullableString); + `, }, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "unexpectedString", - Line: 1, - Column: 17, + + // ======================================== + // ARRAY PREDICATE FUNCTIONS + // ======================================== + {Code: `['one', 'two', ''].some(x => x);`}, + {Code: `['one', 'two', ''].find(x => x);`}, + {Code: `['one', 'two', ''].every(x => x);`}, + {Code: `['one', 'two', ''].filter((x): boolean => x);`}, + {Code: `['one', 'two', ''].filter(x => Boolean(x));`}, + {Code: `['one', 'two', ''].filter(function (x): boolean { if (x) { return true; } });`}, + {Code: `['one', 'two', ''].filter(function (x): boolean { if (x) { return true; } throw new Error('oops'); });`}, + {Code: `declare const predicate: (string) => boolean; ['one', 'two', ''].filter(predicate);`}, + {Code: `declare function notNullish(x: T): x is NonNullable; ['one', null].filter(notNullish);`}, + {Code: `declare function predicate(x: string | null): x is string; ['one', null].filter(predicate);`}, + {Code: `declare function predicate(x: string | null): T; ['one', null].filter(predicate);`}, + {Code: `declare function f(x: number): boolean; declare function f(x: string | null): boolean; [35].filter(f);`}, + + // ======================================== + // SPECIAL CASES + // ======================================== + {Code: `for (let x = 0; ; x++) { break; }`}, + }, + []rule_tester.InvalidTestCase{ + // ======================================== + // NON-BOOLEAN IN RHS OF TEST EXPRESSION + // ======================================== + { + Code: `if (true && 1 + 1) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(false), + AllowNumber: utils.Ref(false), + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "unexpectedNumber", + Line: 1, + }, }, }, - }, - { - Code: `(x: object) => (true || false || x ? true : false);`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), + { + Code: `while (false || 'a' + 'b') {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(false), + AllowNumber: utils.Ref(false), + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "unexpectedString", + Line: 1, + }, + }, }, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "unexpectedObjectContext", - Line: 1, - Column: 34, + { + Code: `(x: object) => (true || false || x ? true : false);`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(false), + AllowNumber: utils.Ref(false), + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "unexpectedObjectContext", + Line: 1, + }, }, }, - }, - // ======================================== - // CHECK OUTERMOST OPERANDS - // ======================================== - { - Code: `if (('' && {}) || (0 && void 0)) { }`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1, Column: 6}, - {MessageId: "unexpectedObjectContext", Line: 1, Column: 12}, - {MessageId: "unexpectedNumber", Line: 1, Column: 20}, - {MessageId: "unexpectedNullish", Line: 1, Column: 25}, + // ======================================== + // CHECK OUTERMOST OPERANDS + // ======================================== + { + Code: `if (('' && {}) || (0 && void 0)) { }`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(false), + AllowNumber: utils.Ref(false), + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedObjectContext", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedNullish", Line: 1}, + }, }, - }, - // ======================================== - // ARRAY PREDICATE WITH NON-BOOLEAN - // ======================================== - { - Code: `declare const array: string[]; array.some(x => x);`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableBoolean: utils.Ref(true), - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, + // ======================================== + // ARRAY PREDICATE WITH NON-BOOLEAN + // ======================================== + { + Code: `declare const array: string[]; array.some(x => x);`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableBoolean: utils.Ref(true), + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + }, }, - }, - // ======================================== - // BRANDED TYPES - // ======================================== - { - Code: ` + // ======================================== + // BRANDED TYPES + // ======================================== + { + Code: ` declare const foo: true & { __BRAND: 'Foo' }; if (('' && foo) || (0 && void 0)) { } `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 3, Column: 6}, - {MessageId: "unexpectedNumber", Line: 3, Column: 21}, - {MessageId: "unexpectedNullish", Line: 3, Column: 26}, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(false), + AllowNumber: utils.Ref(false), + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 3}, + {MessageId: "unexpectedNumber", Line: 3}, + {MessageId: "unexpectedNullish", Line: 3}, + }, }, - }, - { - Code: ` + { + Code: ` declare const foo: false & { __BRAND: 'Foo' }; if (('' && {}) || (foo && void 0)) { } `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 3, Column: 6}, - {MessageId: "unexpectedObjectContext", Line: 3, Column: 12}, - {MessageId: "unexpectedNullish", Line: 3, Column: 27}, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(false), + AllowNumber: utils.Ref(false), + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 3}, + {MessageId: "unexpectedObjectContext", Line: 3}, + {MessageId: "unexpectedNullish", Line: 3}, + }, }, - }, - // ======================================== - // LOGICAL OPERANDS FOR CONTROL FLOW - // ======================================== - { - Code: `'asd' && 123 && [] && null;`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1, Column: 1}, - {MessageId: "unexpectedNumber", Line: 1, Column: 10}, - {MessageId: "unexpectedObjectContext", Line: 1, Column: 17}, - }, - }, - { - Code: `'asd' || 123 || [] || null;`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1, Column: 1}, - {MessageId: "unexpectedNumber", Line: 1, Column: 10}, - {MessageId: "unexpectedObjectContext", Line: 1, Column: 17}, + // ======================================== + // LOGICAL OPERANDS FOR CONTROL FLOW + // ======================================== + { + Code: `'asd' && 123 && [] && null;`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedObjectContext", Line: 1}, + }, }, - }, - { - Code: `let x = (1 && 'a' && null) || 0 || '' || {};`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1, Column: 10}, - {MessageId: "unexpectedString", Line: 1, Column: 15}, - {MessageId: "unexpectedNullish", Line: 1, Column: 22}, - {MessageId: "unexpectedNumber", Line: 1, Column: 31}, - {MessageId: "unexpectedString", Line: 1, Column: 36}, + { + Code: `'asd' || 123 || [] || null;`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedObjectContext", Line: 1}, + }, }, - }, - { - Code: `return (1 || 'a' || null) && 0 && '' && {};`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1, Column: 9}, - {MessageId: "unexpectedString", Line: 1, Column: 14}, - {MessageId: "unexpectedNullish", Line: 1, Column: 21}, - {MessageId: "unexpectedNumber", Line: 1, Column: 30}, - {MessageId: "unexpectedString", Line: 1, Column: 35}, + { + Code: `let x = (1 && 'a' && null) || 0 || '' || {};`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedNullish", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedString", Line: 1}, + }, }, - }, - { - Code: `console.log((1 && []) || ('a' && {}));`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), + { + Code: `return (1 || 'a' || null) && 0 && '' && {};`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedNullish", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedString", Line: 1}, + }, }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1, Column: 14}, - {MessageId: "unexpectedObjectContext", Line: 1, Column: 19}, - {MessageId: "unexpectedString", Line: 1, Column: 27}, + { + Code: `console.log((1 && []) || ('a' && {}));`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedObjectContext", Line: 1}, + {MessageId: "unexpectedString", Line: 1}, + }, }, - }, - // ======================================== - // CONDITIONALS WITH ALL OPERANDS CHECKED - // ======================================== - { - Code: `if ((1 && []) || ('a' && {})) void 0;`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1, Column: 6}, - {MessageId: "unexpectedObjectContext", Line: 1, Column: 11}, - {MessageId: "unexpectedString", Line: 1, Column: 19}, - {MessageId: "unexpectedObjectContext", Line: 1, Column: 26}, + // ======================================== + // CONDITIONALS WITH ALL OPERANDS CHECKED + // ======================================== + { + Code: `if ((1 && []) || ('a' && {})) void 0;`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedObjectContext", Line: 1}, + {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedObjectContext", Line: 1}, + }, }, - }, - { - Code: `let x = null || 0 || 'a' || [] ? {} : undefined;`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1, Column: 9}, - {MessageId: "unexpectedNumber", Line: 1, Column: 17}, - {MessageId: "unexpectedString", Line: 1, Column: 22}, - {MessageId: "unexpectedObjectContext", Line: 1, Column: 29}, + { + Code: `let x = null || 0 || 'a' || [] ? {} : undefined;`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullish", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedObjectContext", Line: 1}, + }, }, - }, - { - Code: `return !(null || 0 || 'a' || []);`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1, Column: 10}, - {MessageId: "unexpectedNumber", Line: 1, Column: 18}, - {MessageId: "unexpectedString", Line: 1, Column: 23}, - {MessageId: "unexpectedObjectContext", Line: 1, Column: 30}, + { + Code: `return !(null || 0 || 'a' || []);`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullish", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedObjectContext", Line: 1}, + }, }, - }, - // ======================================== - // NULLISH VALUES IN BOOLEAN CONTEXT - // ======================================== - { - Code: `null || {};`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1, Column: 1}, + // ======================================== + // NULLISH VALUES IN BOOLEAN CONTEXT + // ======================================== + { + Code: `null || {};`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullish", Line: 1}, + }, }, - }, - { - Code: `undefined && [];`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1, Column: 1}, + { + Code: `undefined && [];`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullish", Line: 1}, + }, }, - }, - { - Code: `declare const x: null; if (x) {}`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1}, + { + Code: `declare const x: null; if (x) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullish", Line: 1}, + }, }, - }, - { - Code: `(x: undefined) => !x;`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1, Column: 20}, + { + Code: `(x: undefined) => !x;`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullish", Line: 1}, + }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1, Column: 40}, + { + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullish", Line: 1}, + }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1, Column: 28}, + { + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullish", Line: 1}, + }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1, Column: 33}, + { + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullish", Line: 1}, + }, }, - }, - // ======================================== - // OBJECT IN BOOLEAN CONTEXT - // ======================================== - { - Code: `[] || 1;`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1, Column: 1}, + // ======================================== + // OBJECT IN BOOLEAN CONTEXT + // ======================================== + { + Code: `[] || 1;`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, }, - }, - { - Code: `({}) && 'a';`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1, Column: 2}, + { + Code: `({}) && 'a';`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, }, - }, - { - Code: `declare const x: symbol; if (x) {}`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, + { + Code: `declare const x: symbol; if (x) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, }, - }, - { - Code: `(x: () => void) => !x;`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1, Column: 21}, + { + Code: `(x: () => void) => !x;`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1, Column: 30}, + { + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1, Column: 41}, + { + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1, Column: 37}, + { + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, }, - }, - { - Code: ` void>(x: T) => (x ? 1 : 0);`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1, Column: 34}, + { + Code: ` void>(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, }, - }, - // ======================================== - // STRING IN BOOLEAN CONTEXT WITH ALLOWSTRING: FALSE - // ======================================== - { - Code: `while ('') {}`, - Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1, Column: 8}, - }, - }, - { - Code: `for (; 'foo'; ) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1, Column: 8}, - }, - }, - { - Code: `declare const x: string; if (x) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, + // ======================================== + // STRING IN BOOLEAN CONTEXT WITH ALLOWSTRING: FALSE + // ======================================== + { + Code: `while ('') {}`, + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + }, }, - }, - { - Code: `(x: string) => !x;`, - Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), + { + Code: `for (; 'foo'; ) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + }, }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1, Column: 17}, + { + Code: `declare const x: string; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), + { + Code: `(x: string) => !x;`, + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + }, }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1, Column: 30}, + { + Code: `(x: T) => (x ? 1 : 0);`, + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + }, }, - }, - // ======================================== - // NUMBER IN BOOLEAN CONTEXT WITH ALLOWNUMBER: FALSE - // ======================================== - { - Code: `while (0n) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1, Column: 8}, - }, - }, - { - Code: `for (; 123; ) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1, Column: 8}, - }, - }, - { - Code: `declare const x: number; if (x) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, - }, - }, - { - Code: `(x: bigint) => !x;`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1, Column: 17}, + // ======================================== + // NUMBER IN BOOLEAN CONTEXT WITH ALLOWNUMBER: FALSE + // ======================================== + { + Code: `while (0n) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), + { + Code: `for (; 123; ) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1, Column: 30}, + { + Code: `declare const x: number; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, }, - }, - { - Code: `![]['length'];`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), + { + Code: `(x: bigint) => !x;`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1, Column: 2}, + { + Code: `(x: T) => (x ? 1 : 0);`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, }, - }, - { - Code: `declare const a: any[] & { notLength: number }; if (a.notLength) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), + { + Code: `![]['length'];`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, + { + Code: `declare const a: any[] & { notLength: number }; if (a.notLength) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, }, - }, - // ======================================== - // ARRAY.LENGTH IN BOOLEAN CONTEXT - // ======================================== - { - Code: `if (![].length) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1, Column: 6}, - }, - }, - { - Code: `(a: number[]) => a.length && '...';`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1, Column: 18}, + // ======================================== + // ARRAY.LENGTH IN BOOLEAN CONTEXT + // ======================================== + { + Code: `if (![].length) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, }, - }, - { - Code: `(...a: T) => a.length || 'empty';`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), + { + Code: `(a: number[]) => a.length && '...';`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1, Column: 35}, + { + Code: `(...a: T) => a.length || 'empty';`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, }, - }, - // ======================================== - // MIXED STRING | NUMBER VALUE IN BOOLEAN CONTEXT - // ======================================== - { - Code: `declare const x: string | number; if (x) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(true), - AllowString: utils.Ref(true), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedMixedCondition", Line: 1}, - }, - }, - { - Code: `(x: bigint | string) => !x;`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(true), - AllowString: utils.Ref(true), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedMixedCondition", Line: 1, Column: 26}, + // ======================================== + // MIXED STRING | NUMBER VALUE IN BOOLEAN CONTEXT + // ======================================== + { + Code: `declare const x: string | number; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(true), + AllowString: utils.Ref(true), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedMixedCondition", Line: 1}, + }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(true), - AllowString: utils.Ref(true), + { + Code: `(x: bigint | string) => !x;`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(true), + AllowString: utils.Ref(true), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedMixedCondition", Line: 1}, + }, }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedMixedCondition", Line: 1, Column: 48}, + { + Code: `(x: T) => (x ? 1 : 0);`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(true), + AllowString: utils.Ref(true), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedMixedCondition", Line: 1}, + }, }, - }, - // ======================================== - // NULLABLE BOOLEAN WITHOUT OPTION - // ======================================== - { - Code: `declare const x: boolean | null; if (x) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableBoolean: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableBoolean", Line: 1}, - }, - }, - { - Code: `(x?: boolean) => !x;`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableBoolean: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableBoolean", Line: 1, Column: 19}, + // ======================================== + // NULLABLE BOOLEAN WITHOUT OPTION + // ======================================== + { + Code: `declare const x: boolean | null; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableBoolean: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableBoolean", Line: 1}, + }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableBoolean: utils.Ref(false), + { + Code: `(x?: boolean) => !x;`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableBoolean: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableBoolean", Line: 1}, + }, }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableBoolean", Line: 1, Column: 50}, + { + Code: `(x: T) => (x ? 1 : 0);`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableBoolean: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableBoolean", Line: 1}, + }, }, - }, - // ======================================== - // NULLABLE OBJECT WITHOUT OPTION - // ======================================== - { - Code: `declare const x: object | null; if (x) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableObject", Line: 1}, - }, - }, - { - Code: `(x?: { a: number }) => !x;`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableObject", Line: 1, Column: 25}, + // ======================================== + // NULLABLE OBJECT WITHOUT OPTION + // ======================================== + { + Code: `declare const x: object | null; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableObject", Line: 1}, + }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), + { + Code: `(x?: { a: number }) => !x;`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableObject", Line: 1}, + }, }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableObject", Line: 1, Column: 45}, + { + Code: `(x: T) => (x ? 1 : 0);`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableObject", Line: 1}, + }, }, - }, - // ======================================== - // NULLABLE STRING WITHOUT OPTION - // ======================================== - { - Code: `declare const x: string | null; if (x) {}`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 1}, + // ======================================== + // NULLABLE STRING WITHOUT OPTION + // ======================================== + { + Code: `declare const x: string | null; if (x) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableString", Line: 1}, + }, }, - }, - { - Code: `(x?: string) => !x;`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 1, Column: 18}, + { + Code: `(x?: string) => !x;`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableString", Line: 1}, + }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 1, Column: 49}, + { + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableString", Line: 1}, + }, }, - }, - { - Code: `function foo(x: '' | 'bar' | null) { if (!x) {} }`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 1}, + { + Code: `function foo(x: '' | 'bar' | null) { if (!x) {} }`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableString", Line: 1}, + }, }, - }, - // ======================================== - // NULLABLE NUMBER WITHOUT OPTION - // ======================================== - { - Code: `declare const x: number | null; if (x) {}`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableNumber", Line: 1}, + // ======================================== + // NULLABLE NUMBER WITHOUT OPTION + // ======================================== + { + Code: `declare const x: number | null; if (x) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableNumber", Line: 1}, + }, }, - }, - { - Code: `(x?: number) => !x;`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableNumber", Line: 1, Column: 18}, + { + Code: `(x?: number) => !x;`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableNumber", Line: 1}, + }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableNumber", Line: 1, Column: 49}, + { + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableNumber", Line: 1}, + }, }, - }, - { - Code: `function foo(x: 0 | 1 | null) { if (!x) {} }`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableNumber", Line: 1}, + { + Code: `function foo(x: 0 | 1 | null) { if (!x) {} }`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableNumber", Line: 1}, + }, }, - }, - // ======================================== - // NULLABLE ENUM WITHOUT OPTION - // ======================================== - { - Code: ` + // ======================================== + // NULLABLE ENUM WITHOUT OPTION + // ======================================== + { + Code: ` enum ExampleEnum { This = 0, That = 1 } const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; if (theEnum) {} `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableNumber", Line: 4}, + Options: StrictBooleanExpressionsOptions{ + AllowNullableEnum: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableNumber", Line: 4}, + }, }, - }, - { - Code: ` + { + Code: ` enum ExampleEnum { This = 0, That = 1 } const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; if (!theEnum) {} `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableNumber", Line: 4}, + Options: StrictBooleanExpressionsOptions{ + AllowNullableEnum: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableNumber", Line: 4}, + }, }, - }, - { - Code: ` + { + Code: ` enum ExampleEnum { This, That } const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; if (!theEnum) {} `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableNumber", Line: 4}, + Options: StrictBooleanExpressionsOptions{ + AllowNullableEnum: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableNumber", Line: 4}, + }, }, - }, - { - Code: ` + { + Code: ` enum ExampleEnum { This = '', That = 'a' } const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; if (!theEnum) {} `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 4}, + Options: StrictBooleanExpressionsOptions{ + AllowNullableEnum: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableString", Line: 4}, + }, }, - }, - { - Code: ` + { + Code: ` enum ExampleEnum { This = '', That = 0 } const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; if (!theEnum) {} `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedMixedCondition", Line: 4}, + Options: StrictBooleanExpressionsOptions{ + AllowNullableEnum: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedMixedCondition", Line: 4}, + }, }, - }, - { - Code: ` + { + Code: ` enum ExampleEnum { This = 'one', That = 'two' } const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; if (!theEnum) {} `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 4}, + Options: StrictBooleanExpressionsOptions{ + AllowNullableEnum: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableString", Line: 4}, + }, }, - }, - { - Code: ` + { + Code: ` enum ExampleEnum { This = 1, That = 2 } const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; if (!theEnum) {} `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableNumber", Line: 4}, + Options: StrictBooleanExpressionsOptions{ + AllowNullableEnum: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableNumber", Line: 4}, + }, }, - }, - // ======================================== - // NULLABLE MIXED ENUM WITHOUT OPTION - // ======================================== - { - Code: ` + // ======================================== + // NULLABLE MIXED ENUM WITHOUT OPTION + // ======================================== + { + Code: ` enum ExampleEnum { This = 0, That = 'one' } (value?: ExampleEnum) => (value ? 1 : 0); `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedMixedCondition", Line: 3}, + Options: StrictBooleanExpressionsOptions{ + AllowNullableEnum: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedMixedCondition", Line: 3}, + }, }, - }, - { - Code: ` + { + Code: ` enum ExampleEnum { This = '', That = 1 } (value?: ExampleEnum) => (!value ? 1 : 0); `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedMixedCondition", Line: 3}, + Options: StrictBooleanExpressionsOptions{ + AllowNullableEnum: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedMixedCondition", Line: 3}, + }, }, - }, - { - Code: ` + { + Code: ` enum ExampleEnum { This = 'this', That = 1 } (value?: ExampleEnum) => (value ? 1 : 0); `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedMixedCondition", Line: 3}, + Options: StrictBooleanExpressionsOptions{ + AllowNullableEnum: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedMixedCondition", Line: 3}, + }, }, - }, - // ======================================== - // ANY WITHOUT OPTION - // ======================================== - { - Code: `if ((Boolean(x) || {}) || (typeof x === 'string' && x)) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1, Column: 20}, - {MessageId: "unexpectedString", Line: 1, Column: 53}, - }, - }, - { - Code: `if (1) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), + // ======================================== + // ANY WITHOUT OPTION + // ======================================== + { + Code: `if ((Boolean(x) || {}) || (typeof x === 'string' && x)) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + {MessageId: "unexpectedString", Line: 1}, + }, }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1, Column: 5}, + { + Code: `if (1) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, }, - }, - // ======================================== - // ASSERT FUNCTIONS - // ======================================== - { - Code: ` + // ======================================== + // ASSERT FUNCTIONS + // ======================================== + { + Code: ` declare function assert(a: boolean, b: unknown): asserts b; declare function assert({ a }: { a: boolean }, b: unknown): asserts b; declare const nullableString: string | null; declare const boo: boolean; assert(boo, nullableString); `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 6}, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableString", Line: 6}, + }, }, - }, - // ======================================== - // ARRAY FILTER PREDICATES - // ======================================== - { - Code: `declare const nullOrBool: boolean | null; [true, false, null].filter(x => nullOrBool);`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableBoolean: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableBoolean", Line: 1}, - }, - }, - { - Code: `declare const nullOrString: string | null; ['', 'foo', null].filter(x => nullOrString);`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 1}, - }, - }, - { - Code: `declare const nullOrNumber: number | null; [0, null].filter(x => nullOrNumber);`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableNumber", Line: 1}, - }, - }, - { - Code: `const objectValue: object = {}; [{ a: 0 }, {}].filter(x => objectValue);`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, - }, - }, - { - Code: `const objectValue: object = {}; [{ a: 0 }, {}].filter(x => { return objectValue; });`, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, + // ======================================== + // ARRAY FILTER PREDICATES + // ======================================== + { + Code: `declare const nullOrBool: boolean | null; [true, false, null].filter(x => nullOrBool);`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableBoolean: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableBoolean", Line: 1}, + }, }, - }, - { - Code: `declare const nullOrObject: object | null; [{ a: 0 }, null].filter(x => nullOrObject);`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), + { + Code: `declare const nullOrString: string | null; ['', 'foo', null].filter(x => nullOrString);`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableString", Line: 1}, + }, }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableObject", Line: 1}, + { + Code: `declare const nullOrNumber: number | null; [0, null].filter(x => nullOrNumber);`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableNumber", Line: 1}, + }, }, - }, - { - Code: `const numbers: number[] = [1]; [1, 2].filter(x => numbers.length);`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), + { + Code: `const objectValue: object = {}; [{ a: 0 }, {}].filter(x => objectValue);`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, + { + Code: `const objectValue: object = {}; [{ a: 0 }, {}].filter(x => { return objectValue; });`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObjectContext", Line: 1}, + }, }, - }, - { - Code: `const numberValue: number = 1; [1, 2].filter(x => numberValue);`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), + { + Code: `declare const nullOrObject: object | null; [{ a: 0 }, null].filter(x => nullOrObject);`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableObject", Line: 1}, + }, }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, + { + Code: `const numbers: number[] = [1]; [1, 2].filter(x => numbers.length);`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, }, - }, - { - Code: `const stringValue: string = 'hoge'; ['hoge', 'foo'].filter(x => stringValue);`, - Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), + { + Code: `const numberValue: number = 1; [1, 2].filter(x => numberValue);`, + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, + }, }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, + { + Code: `const stringValue: string = 'hoge'; ['hoge', 'foo'].filter(x => stringValue);`, + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, + }, }, - }, - // ======================================== - // UNKNOWN TYPE WITHOUT OPTION - // ======================================== - { - Code: `declare const x: unknown; if (x) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedAny", Line: 1}, - }, - }, - { - Code: `declare const x: unknown; x ? 'a' : 'b';`, - Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedAny", Line: 1}, - }, - }, - { - Code: `declare const x: unknown; x && 'a';`, - Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedAny", Line: 1}, + // ======================================== + // UNKNOWN TYPE WITHOUT OPTION + // ======================================== + { + Code: `declare const x: unknown; if (x) {}`, + Options: StrictBooleanExpressionsOptions{ + AllowAny: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedAny", Line: 1}, + }, }, - }, - { - Code: `declare const x: unknown; x || 'a';`, - Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(false), + { + Code: `declare const x: unknown; x ? 'a' : 'b';`, + Options: StrictBooleanExpressionsOptions{ + AllowAny: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedAny", Line: 1}, + }, }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedAny", Line: 1}, + { + Code: `declare const x: unknown; x && 'a';`, + Options: StrictBooleanExpressionsOptions{ + AllowAny: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedAny", Line: 1}, + }, }, - }, - { - Code: `declare const x: unknown; !x;`, - Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(false), + { + Code: `declare const x: unknown; x || 'a';`, + Options: StrictBooleanExpressionsOptions{ + AllowAny: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedAny", Line: 1}, + }, }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedAny", Line: 1}, + { + Code: `declare const x: unknown; !x;`, + Options: StrictBooleanExpressionsOptions{ + AllowAny: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedAny", Line: 1}, + }, }, - }, - }) -} \ No newline at end of file + }) +} From 9b3bd8431e8a9598ad3dc353cf394626a4133db1 Mon Sep 17 00:00:00 2001 From: Noel Kim Date: Mon, 29 Sep 2025 12:52:51 +0900 Subject: [PATCH 10/27] format --- .../strict_boolean_expressions_single_test.go | 5 ++--- internal/rules/unbound_method/unbound_method.go | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go index 998d3e9b..7595194d 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go @@ -11,8 +11,7 @@ import ( ) func TestStrictBooleanExpressionsSingleRule(t *testing.T) { - rule_tester.RunRuleTester(fixtures.GetRootDir(), "tsconfig.json", t, &StrictBooleanExpressionsRule, []rule_tester.ValidTestCase{ - }, []rule_tester.InvalidTestCase{ + rule_tester.RunRuleTester(fixtures.GetRootDir(), "tsconfig.json", t, &StrictBooleanExpressionsRule, []rule_tester.ValidTestCase{}, []rule_tester.InvalidTestCase{ { Code: `if (('' && {}) || (0 && void 0)) { }`, Options: StrictBooleanExpressionsOptions{ @@ -28,4 +27,4 @@ func TestStrictBooleanExpressionsSingleRule(t *testing.T) { }, }, }) -} \ No newline at end of file +} diff --git a/internal/rules/unbound_method/unbound_method.go b/internal/rules/unbound_method/unbound_method.go index bbd82e5e..e181c43f 100644 --- a/internal/rules/unbound_method/unbound_method.go +++ b/internal/rules/unbound_method/unbound_method.go @@ -241,7 +241,7 @@ var UnboundMethodRule = rule.Rule{ if ast.IsComputedPropertyName(propertyName) { return } - + if initNode != nil { if !isNativelyBound(initNode, propertyName) { reported := checkIfMethodAndReport(propertyName, checker.Checker_getPropertyOfType(ctx.TypeChecker, ctx.TypeChecker.GetTypeAtLocation(initNode), propertyName.Text())) From ea4b3a5764b8036d1e2d9a41d2a2a02b3a988478 Mon Sep 17 00:00:00 2001 From: Noel Kim Date: Mon, 29 Sep 2025 13:10:10 +0900 Subject: [PATCH 11/27] enum --- .../strict_boolean_expressions.go | 21 +++--- .../strict_boolean_expressions_single_test.go | 13 +--- .../strict_boolean_expressions_test.go | 68 ++++++++++++++++--- 3 files changed, 72 insertions(+), 30 deletions(-) diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go index 7b42c383..16fba6b5 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go @@ -335,7 +335,6 @@ func analyzeType(typeChecker *checker.Checker, t *checker.Type) typeInfo { if partInfo.isEnum { info.isEnum = true } - info.isTruthy = partInfo.isTruthy } if len(variants) == 1 { @@ -414,6 +413,16 @@ func analyzeTypePart(_ *checker.Checker, t *checker.Type) typeInfo { return info } + if flags&(checker.TypeFlagsEnum|checker.TypeFlagsEnumLiteral|checker.TypeFlagsEnumLike) != 0 { + if flags&checker.TypeFlagsStringLiteral != 0 { + info.variant = typeVariantString + } else { + info.variant = typeVariantNumber + } + info.isEnum = true + return info + } + if flags&(checker.TypeFlagsString|checker.TypeFlagsStringLiteral) != 0 { info.variant = typeVariantString if flags&checker.TypeFlagsStringLiteral != 0 && t.AsLiteralType().Value() != "" { @@ -430,16 +439,6 @@ func analyzeTypePart(_ *checker.Checker, t *checker.Type) typeInfo { return info } - if flags&(checker.TypeFlagsEnum|checker.TypeFlagsEnumLiteral) != 0 { - if flags&checker.TypeFlagsStringLiteral != 0 { - info.variant = typeVariantString - } else { - info.variant = typeVariantNumber - } - info.isEnum = true - return info - } - if flags&(checker.TypeFlagsBigInt|checker.TypeFlagsBigIntLiteral) != 0 { info.variant = typeVariantBigInt if flags&checker.TypeFlagsBigIntLiteral != 0 && t.AsLiteralType().Value() != 0 { diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go index 7595194d..9ef166f6 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go @@ -7,23 +7,14 @@ import ( "github.com/typescript-eslint/tsgolint/internal/rule_tester" "github.com/typescript-eslint/tsgolint/internal/rules/fixtures" - "github.com/typescript-eslint/tsgolint/internal/utils" ) func TestStrictBooleanExpressionsSingleRule(t *testing.T) { rule_tester.RunRuleTester(fixtures.GetRootDir(), "tsconfig.json", t, &StrictBooleanExpressionsRule, []rule_tester.ValidTestCase{}, []rule_tester.InvalidTestCase{ { - Code: `if (('' && {}) || (0 && void 0)) { }`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), - }, + Code: `function foo(x: 0 | 1 | null) { if (!x) {} }`, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, - {MessageId: "unexpectedObjectContext", Line: 1}, - {MessageId: "unexpectedNumber", Line: 1}, - {MessageId: "unexpectedNullish", Line: 1}, + {MessageId: "unexpectedNullableNumber", Line: 1}, }, }, }) diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go index dd471e91..168ae1ac 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go @@ -197,20 +197,72 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { // ======================================== { Code: ` - enum E { A, B } - declare const x: E | null; - if (x) {} - `, + enum ExampleEnum { + This = 0, + That = 1, + } + const rand = Math.random(); + let theEnum: ExampleEnum | null = null; + if (rand < 0.3) { + theEnum = ExampleEnum.This; + } + if (theEnum) { + } + `, Options: StrictBooleanExpressionsOptions{ AllowNullableEnum: utils.Ref(true), }, }, { Code: ` - enum E { A = 'a', B = 'b' } - declare const x: E | undefined; - if (x) {} - `, + enum ExampleEnum { + This = 0, + That = 1, + } + const rand = Math.random(); + let theEnum: ExampleEnum | null = null; + if (rand < 0.3) { + theEnum = ExampleEnum.This; + } + if (!theEnum) { + } + `, + Options: StrictBooleanExpressionsOptions{ + AllowNullableEnum: utils.Ref(true), + }, + }, + { + Code: ` + enum ExampleEnum { + This = 1, + That = 2, + } + const rand = Math.random(); + let theEnum: ExampleEnum | null = null; + if (rand < 0.3) { + theEnum = ExampleEnum.This; + } + if (!theEnum) { + } + `, + Options: StrictBooleanExpressionsOptions{ + AllowNullableEnum: utils.Ref(true), + }, + }, + { + Code: ` + enum ExampleEnum { + This = 'one', + That = 'two', + } + const rand = Math.random(); + let theEnum: ExampleEnum | null = null; + if (rand < 0.3) { + theEnum = ExampleEnum.This; + } + if (!theEnum) { + } + `, Options: StrictBooleanExpressionsOptions{ AllowNullableEnum: utils.Ref(true), }, From cfd450bf8f09816d9d7f63e9bbc73af96faf6ae0 Mon Sep 17 00:00:00 2001 From: Noel Kim Date: Mon, 29 Sep 2025 13:24:39 +0900 Subject: [PATCH 12/27] parentheses --- .../strict_boolean_expressions.go | 5 +++++ .../strict_boolean_expressions_single_test.go | 13 +++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go index 16fba6b5..a29f9422 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go @@ -274,6 +274,11 @@ func traverseNode(ctx rule.RuleContext, node *ast.Node, opts StrictBooleanExpres } traversedNodes.Add(node) + if node.Kind == ast.KindParenthesizedExpression { + traverseNode(ctx, node.AsParenthesizedExpression().Expression, opts, isCondition) + return + } + if node.Kind == ast.KindBinaryExpression { binExpr := node.AsBinaryExpression() if ast.IsLogicalExpression(node) && binExpr.OperatorToken.Kind != ast.KindQuestionQuestionToken { diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go index 9ef166f6..7595194d 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go @@ -7,14 +7,23 @@ import ( "github.com/typescript-eslint/tsgolint/internal/rule_tester" "github.com/typescript-eslint/tsgolint/internal/rules/fixtures" + "github.com/typescript-eslint/tsgolint/internal/utils" ) func TestStrictBooleanExpressionsSingleRule(t *testing.T) { rule_tester.RunRuleTester(fixtures.GetRootDir(), "tsconfig.json", t, &StrictBooleanExpressionsRule, []rule_tester.ValidTestCase{}, []rule_tester.InvalidTestCase{ { - Code: `function foo(x: 0 | 1 | null) { if (!x) {} }`, + Code: `if (('' && {}) || (0 && void 0)) { }`, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(false), + AllowNumber: utils.Ref(false), + AllowString: utils.Ref(false), + }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableNumber", Line: 1}, + {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedObjectContext", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedNullish", Line: 1}, }, }, }) From 73dfa58491a0da1406b2ba34feb5f4f40a8e5ffe Mon Sep 17 00:00:00 2001 From: Noel Kim Date: Mon, 29 Sep 2025 15:26:10 +0900 Subject: [PATCH 13/27] gen tests --- .../strict_boolean_expressions.go | 48 +- .../strict_boolean_expressions_single_test.go | 24 +- .../strict_boolean_expressions_test.go | 2143 ++++++++++------- 3 files changed, 1379 insertions(+), 836 deletions(-) diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go index a29f9422..ebdbf7f0 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go @@ -76,9 +76,9 @@ func buildUnexpectedNumber() rule.RuleMessage { } } -func buildUnexpectedObjectContext() rule.RuleMessage { +func buildUnexpectedObject() rule.RuleMessage { return rule.RuleMessage{ - Id: "unexpectedObjectContext", + Id: "unexpectedObject", Description: "Unexpected object value in conditional. The condition is always true.", } } @@ -331,6 +331,8 @@ func analyzeType(typeChecker *checker.Checker, t *checker.Type) typeInfo { parts := utils.UnionTypeParts(t) variants := make(map[typeVariant]bool) + metNotTruthy := false + for _, part := range parts { partInfo := analyzeTypePart(typeChecker, part) variants[partInfo.variant] = true @@ -340,6 +342,13 @@ func analyzeType(typeChecker *checker.Checker, t *checker.Type) typeInfo { if partInfo.isEnum { info.isEnum = true } + if partInfo.variant == typeVariantNumber || partInfo.variant == typeVariantString { + if metNotTruthy { + continue + } + info.isTruthy = partInfo.isTruthy + metNotTruthy = !partInfo.isTruthy + } } if len(variants) == 1 { @@ -428,26 +437,36 @@ func analyzeTypePart(_ *checker.Checker, t *checker.Type) typeInfo { return info } - if flags&(checker.TypeFlagsString|checker.TypeFlagsStringLiteral) != 0 { + if flags&(checker.TypeFlagsString|checker.TypeFlagsStringLiteral|checker.TypeFlagsStringLike) != 0 { info.variant = typeVariantString - if flags&checker.TypeFlagsStringLiteral != 0 && t.AsLiteralType().Value() != "" { - info.isTruthy = true + if t.IsStringLiteral() { + literal := t.AsLiteralType() + if literal != nil && literal.Value() != "" { + println(literal.Value()) + info.isTruthy = true + } } return info } - if flags&(checker.TypeFlagsNumber|checker.TypeFlagsNumberLiteral) != 0 { + if flags&(checker.TypeFlagsNumber|checker.TypeFlagsNumberLiteral|checker.TypeFlagsNumberLike) != 0 { info.variant = typeVariantNumber - if flags&checker.TypeFlagsNumberLiteral != 0 && t.AsLiteralType().Value() != 0 { - info.isTruthy = true + if t.IsNumberLiteral() { + literal := t.AsLiteralType() + if literal != nil && literal.String() != "0" { + info.isTruthy = true + } } return info } if flags&(checker.TypeFlagsBigInt|checker.TypeFlagsBigIntLiteral) != 0 { info.variant = typeVariantBigInt - if flags&checker.TypeFlagsBigIntLiteral != 0 && t.AsLiteralType().Value() != 0 { - info.isTruthy = true + if t.IsBigIntLiteral() { + literal := t.AsLiteralType() + if literal != nil && literal.String() != "0" { + info.isTruthy = true + } } return info } @@ -499,6 +518,7 @@ func checkCondition(ctx rule.RuleContext, node *ast.Node, t *checker.Type, opts ctx.ReportNode(node, buildUnexpectedString()) } case typeVariantNumber: + // Known edge case: truthy primitives and nullish values are always valid boolean expressions if *opts.AllowNumber && info.isTruthy { return } @@ -524,9 +544,15 @@ func checkCondition(ctx rule.RuleContext, node *ast.Node, t *checker.Type, opts if info.isNullable && !*opts.AllowNullableObject { ctx.ReportNode(node, buildUnexpectedNullableObject()) } else if !info.isNullable { - ctx.ReportNode(node, buildUnexpectedObjectContext()) + ctx.ReportNode(node, buildUnexpectedObject()) } case typeVariantMixed: + if info.isEnum { + if info.isNullable && !*opts.AllowNullableEnum { + ctx.ReportNode(node, buildUnexpectedNullableNumber()) + } + return + } ctx.ReportNode(node, buildUnexpectedMixedCondition()) case typeVariantBigInt: if info.isNullable && !*opts.AllowNullableNumber { diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go index 7595194d..fe67b8e4 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go @@ -7,24 +7,22 @@ import ( "github.com/typescript-eslint/tsgolint/internal/rule_tester" "github.com/typescript-eslint/tsgolint/internal/rules/fixtures" - "github.com/typescript-eslint/tsgolint/internal/utils" ) func TestStrictBooleanExpressionsSingleRule(t *testing.T) { - rule_tester.RunRuleTester(fixtures.GetRootDir(), "tsconfig.json", t, &StrictBooleanExpressionsRule, []rule_tester.ValidTestCase{}, []rule_tester.InvalidTestCase{ + rule_tester.RunRuleTester(fixtures.GetRootDir(), "tsconfig.json", t, &StrictBooleanExpressionsRule, []rule_tester.ValidTestCase{ + + }, []rule_tester.InvalidTestCase{ { - Code: `if (('' && {}) || (0 && void 0)) { }`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), - }, + Code: ` + function foo(x: 0 | 1 | null) { + if (!x) { + } + } + `, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, - {MessageId: "unexpectedObjectContext", Line: 1}, - {MessageId: "unexpectedNumber", Line: 1}, - {MessageId: "unexpectedNullish", Line: 1}, - }, + {MessageId: "unexpectedNullableNumber", Line: 3}, + }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultZero, conditionFixCastBoolean */ }, }) } diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go index 168ae1ac..048cf0c5 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go @@ -8,1333 +8,1852 @@ import ( "github.com/typescript-eslint/tsgolint/internal/utils" ) -func TestStrictBooleanExpressionsRule(t *testing.T) { +func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { rule_tester.RunRuleTester( fixtures.GetRootDir(), "tsconfig.json", t, &StrictBooleanExpressionsRule, []rule_tester.ValidTestCase{ - // ======================================== - // BOOLEAN IN BOOLEAN CONTEXT - // ======================================== - {Code: `true ? 'a' : 'b';`}, - {Code: `if (false) {}`}, - {Code: `while (true) {}`}, - {Code: `for (; false; ) {}`}, - {Code: `!true;`}, - {Code: `false || 123;`}, - {Code: `true && 'foo';`}, - {Code: `!(false || true);`}, - {Code: `true && false ? true : false;`}, - {Code: `(false && true) || false;`}, - {Code: `(false && true) || [];`}, - {Code: `(false && 1) || (true && 2);`}, - {Code: `declare const x: boolean; if (x) {}`}, - {Code: `(x: boolean) => !x;`}, - {Code: `(x: T) => (x ? 1 : 0);`}, - {Code: `declare const x: never; if (x) {}`}, - - // ======================================== - // STRING IN BOOLEAN CONTEXT - // ======================================== - {Code: `if ('') {}`}, - {Code: `while ('x') {}`}, - {Code: `for (; ''; ) {}`}, - {Code: `('' && '1') || x;`}, - {Code: `declare const x: string; if (x) {}`}, - {Code: `(x: string) => !x;`}, - {Code: `(x: T) => (x ? 1 : 0);`}, - - // ======================================== - // NUMBER IN BOOLEAN CONTEXT - // ======================================== - {Code: `if (0) {}`}, - {Code: `while (1n) {}`}, - {Code: `for (; Infinity; ) {}`}, - {Code: `(0 / 0 && 1 + 2) || x;`}, - {Code: `declare const x: number; if (x) {}`}, - {Code: `(x: bigint) => !x;`}, - {Code: `(x: T) => (x ? 1 : 0);`}, - - // ======================================== - // NULLABLE OBJECT IN BOOLEAN CONTEXT - // ======================================== - {Code: `declare const x: null | object; if (x) {}`}, - {Code: `(x?: { a: any }) => !x;`}, - {Code: `(x: T) => (x ? 1 : 0);`}, - - // ======================================== - // NULLABLE BOOLEAN IN BOOLEAN CONTEXT - // ======================================== { - Code: `declare const x: boolean | null; if (x) {}`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableBoolean: utils.Ref(true), - }, + Code: "true ? 'a' : 'b';", + }, + { + Code: ` + if (false) { + } + `, + }, + { + Code: "while (true) {}", + }, + { + Code: "for (; false; ) {}", + }, + { + Code: "!true;", + }, + { + Code: "false || 123;", + }, + { + Code: "true && 'foo';", + }, + { + Code: "!(false || true);", + }, + { + Code: "true && false ? true : false;", + }, + { + Code: "(false && true) || false;", + }, + { + Code: "(false && true) || [];", + }, + { + Code: "(false && 1) || (true && 2);", + }, + { + Code: ` + declare const x: boolean; + if (x) { + } + `, + }, + { + Code: "(x: boolean) => !x;", + }, + { + Code: "(x: T) => (x ? 1 : 0);", + }, + { + Code: ` + declare const x: never; + if (x) { + } + `, + }, + { + Code: ` + if ('') { + } + `, + }, + { + Code: "while ('x') {}"}, + { + Code: "for (; ''; ) {}"}, + { + Code: "('' && '1') || x;"}, + { + Code: ` + declare const x: string; + if (x) { + } + `, + }, + { + Code: "(x: string) => !x;"}, + { + Code: "(x: T) => (x ? 1 : 0);"}, + { + Code: ` + if (0) { + } + `, + }, + { + Code: "while (1n) {}"}, + { + Code: "for (; Infinity; ) {}"}, + { + Code: "(0 / 0 && 1 + 2) || x;"}, + { + Code: ` + declare const x: number; + if (x) { + } + `, + }, + { + Code: "(x: bigint) => !x;"}, + { + Code: "(x: T) => (x ? 1 : 0);"}, + { + Code: ` + declare const x: null | object; + if (x) { + } + `, }, { - Code: `(x?: boolean) => !x;`, + Code: "(x?: { a: any }) => !x;"}, + { + Code: "(x: T) => (x ? 1 : 0);"}, + { + Code: ` + declare const x: boolean | null; + if (x) { + } + `, Options: StrictBooleanExpressionsOptions{ + AllowNullableBoolean: utils.Ref(true), }, }, { - Code: `(x: T) => (x ? 1 : 0);`, + Code: ` + (x?: boolean) => !x; + `, Options: StrictBooleanExpressionsOptions{ + AllowNullableBoolean: utils.Ref(true), }, }, { - Code: `const a: (undefined | boolean | null)[] = [true, undefined, null]; a.some(x => x);`, + Code: ` + (x: T) => (x ? 1 : 0); + `, Options: StrictBooleanExpressionsOptions{ AllowNullableBoolean: utils.Ref(true), }, }, - - // ======================================== - // NULLABLE STRING IN BOOLEAN CONTEXT - // ======================================== { - Code: `declare const x: string | undefined; if (x) {}`, + Code: ` + const a: (undefined | boolean | null)[] = [true, undefined, null]; + a.some(x => x); + `, Options: StrictBooleanExpressionsOptions{ - AllowNullableString: utils.Ref(true), + AllowNullableBoolean: utils.Ref(true), }, }, { - Code: `(x?: string) => !x;`, + Code: ` + declare const x: string | null; + if (x) { + } + `, Options: StrictBooleanExpressionsOptions{ AllowNullableString: utils.Ref(true), }, }, { - Code: `(x: T) => (x ? 1 : 0);`, + Code: ` + (x?: string) => !x; + `, Options: StrictBooleanExpressionsOptions{ AllowNullableString: utils.Ref(true), }, }, { - Code: `'string' != null ? 'asd' : '';`, + Code: ` + (x: T) => (x ? 1 : 0); + `, Options: StrictBooleanExpressionsOptions{ AllowNullableString: utils.Ref(true), }, }, - - // ======================================== - // NULLABLE NUMBER IN BOOLEAN CONTEXT - // ======================================== { - Code: `declare const x: number | undefined; if (x) {}`, + Code: ` + declare const x: number | null; + if (x) { + } + `, Options: StrictBooleanExpressionsOptions{ AllowNullableNumber: utils.Ref(true), }, }, { - Code: `(x?: number) => !x;`, + Code: ` + (x?: number) => !x; + `, Options: StrictBooleanExpressionsOptions{ AllowNullableNumber: utils.Ref(true), }, }, { - Code: `(x: T) => (x ? 1 : 0);`, + Code: ` + (x: T) => (x ? 1 : 0); + `, Options: StrictBooleanExpressionsOptions{ AllowNullableNumber: utils.Ref(true), }, }, { - Code: `declare const x: bigint | undefined; if (x ?? 0n) {}`, + Code: ` + declare const arrayOfArrays: (null | unknown[])[]; + const isAnyNonEmptyArray1 = arrayOfArrays.some(array => array?.length); + `, Options: StrictBooleanExpressionsOptions{ AllowNullableNumber: utils.Ref(true), }, }, - - // ======================================== - // ANY TYPE IN BOOLEAN CONTEXT - // ======================================== { - Code: `declare const x: any; if (x) {}`, + Code: ` + declare const x: any; + if (x) { + } + `, Options: StrictBooleanExpressionsOptions{ AllowAny: utils.Ref(true), }, }, { - Code: `(x?: any) => !x;`, + Code: ` + x => !x; + `, Options: StrictBooleanExpressionsOptions{ AllowAny: utils.Ref(true), }, }, { - Code: `(x: T) => (x ? 1 : 0);`, + Code: ` + (x: T) => (x ? 1 : 0); + `, Options: StrictBooleanExpressionsOptions{ AllowAny: utils.Ref(true), }, }, { - Code: `const foo: undefined | any = 0; if (foo) {}`, + Code: ` + declare const arrayOfArrays: any[]; + const isAnyNonEmptyArray1 = arrayOfArrays.some(array => array); + `, Options: StrictBooleanExpressionsOptions{ AllowAny: utils.Ref(true), }, }, - - // ======================================== - // UNKNOWN TYPE IN BOOLEAN CONTEXT - // ======================================== { - Code: `declare const x: unknown; if (x) {}`, + Code: ` + 1 && true && 'x' && {}; + `, Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(true), + AllowNumber: utils.Ref(true), AllowString: utils.Ref(true), }, }, { - Code: `(x: T) => (x ? 1 : 0);`, - Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(true), - }, - }, - - // ======================================== - // NULLABLE ENUM IN BOOLEAN CONTEXT - // ======================================== - { - Code: ` - enum ExampleEnum { - This = 0, - That = 1, - } - const rand = Math.random(); - let theEnum: ExampleEnum | null = null; - if (rand < 0.3) { - theEnum = ExampleEnum.This; - } - if (theEnum) { - } + Code: ` + let x = 0 || false || '' || null; `, Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(true), + AllowNumber: utils.Ref(true), AllowString: utils.Ref(true), }, }, { Code: ` - enum ExampleEnum { - This = 0, - That = 1, - } - const rand = Math.random(); - let theEnum: ExampleEnum | null = null; - if (rand < 0.3) { - theEnum = ExampleEnum.This; - } - if (!theEnum) { - } + if (1 && true && 'x') void 0; `, Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(true), + AllowNumber: utils.Ref(true), AllowString: utils.Ref(true), }, }, { Code: ` - enum ExampleEnum { - This = 1, - That = 2, - } - const rand = Math.random(); - let theEnum: ExampleEnum | null = null; - if (rand < 0.3) { - theEnum = ExampleEnum.This; - } - if (!theEnum) { - } + if (0 || false || '') void 0; `, Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(true), + AllowNumber: utils.Ref(true), AllowString: utils.Ref(true), }, }, { Code: ` - enum ExampleEnum { - This = 'one', - That = 'two', - } - const rand = Math.random(); - let theEnum: ExampleEnum | null = null; - if (rand < 0.3) { - theEnum = ExampleEnum.This; - } - if (!theEnum) { - } - `, + 1 && true && 'x' ? {} : null; + `, Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(true), + AllowNumber: utils.Ref(true), AllowString: utils.Ref(true), }, }, - - // ======================================== - // ALLOW RULE TO RUN WITHOUT STRICT NULL CHECKS - // ======================================== { - Code: `if (true) {}`, + Code: ` + 0 || false || '' ? null : {}; + `, Options: StrictBooleanExpressionsOptions{ - AllowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: utils.Ref(true), + AllowNumber: utils.Ref(true), AllowString: utils.Ref(true), }, }, - - // ======================================== - // COMPLEX BOOLEAN EXPRESSIONS - // ======================================== - {Code: `(x?: boolean) => x ?? false ? true : false;`}, - {Code: `(x: T) => x ?? false;`}, { - Code: `(x?: string) => x ?? false;`, + Code: ` + declare const arrayOfArrays: string[]; + const isAnyNonEmptyArray1 = arrayOfArrays.some(array => array); + `, Options: StrictBooleanExpressionsOptions{ - AllowNullableString: utils.Ref(true), + AllowString: utils.Ref(true), }, }, { - Code: `(x?: number) => x ?? false;`, + Code: ` + declare const arrayOfArrays: number[]; + const isAnyNonEmptyArray1 = arrayOfArrays.some(array => array); + `, Options: StrictBooleanExpressionsOptions{ - AllowNullableNumber: utils.Ref(true), + AllowNumber: utils.Ref(true), }, }, - - // ======================================== - // ASSERT FUNCTIONS - // ======================================== { Code: ` -declare function assert(a: number, b: unknown): asserts a; -declare const nullableString: string | null; -declare const boo: boolean; -assert(boo, nullableString); + declare const arrayOfArrays: (null | object)[]; + const isAnyNonEmptyArray1 = arrayOfArrays.some(array => array); `, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(true), + }, }, { Code: ` -declare function assert(a: boolean, b: unknown): asserts b is string; -declare const nullableString: string | null; -declare const boo: boolean; -assert(boo, nullableString); + enum ExampleEnum { + This = 0, + That = 1, + } + const rand = Math.random(); + let theEnum: ExampleEnum | null = null; + if (rand < 0.3) { + theEnum = ExampleEnum.This; + } + if (theEnum) { + } `, + Options: StrictBooleanExpressionsOptions{ + AllowNullableEnum: utils.Ref(true), + }, }, { Code: ` -declare function assert(a: number, b: unknown): asserts b; -declare const nullableString: string | null; -declare const boo: boolean; -assert(nullableString, boo); + enum ExampleEnum { + This = 0, + That = 1, + } + const rand = Math.random(); + let theEnum: ExampleEnum | null = null; + if (rand < 0.3) { + theEnum = ExampleEnum.This; + } + if (!theEnum) { + } `, + Options: StrictBooleanExpressionsOptions{ + AllowNullableEnum: utils.Ref(true), + }, }, { Code: ` -declare function assert(a: number, b: unknown): asserts b; -declare const nullableString: string | null; -declare const boo: boolean; -assert(...nullableString, nullableString); + enum ExampleEnum { + This = 1, + That = 2, + } + const rand = Math.random(); + let theEnum: ExampleEnum | null = null; + if (rand < 0.3) { + theEnum = ExampleEnum.This; + } + if (!theEnum) { + } `, + Options: StrictBooleanExpressionsOptions{ + AllowNullableEnum: utils.Ref(true), + }, }, { Code: ` -declare function assert( - this: object, - a: number, - b?: unknown, - c?: unknown, -): asserts c; -declare const nullableString: string | null; -declare const foo: number; -const o: { assert: typeof assert } = { - assert, -}; -o.assert(foo, nullableString); + enum ExampleEnum { + This = 'one', + That = 'two', + } + const rand = Math.random(); + let theEnum: ExampleEnum | null = null; + if (rand < 0.3) { + theEnum = ExampleEnum.This; + } + if (!theEnum) { + } `, + Options: StrictBooleanExpressionsOptions{ + AllowNullableEnum: utils.Ref(true), + }, }, { Code: ` -declare function assert(x: unknown): x is string; -declare const nullableString: string | null; -assert(nullableString); - `, - }, - { - Code: ` -class ThisAsserter { - assertThis(this: unknown, arg2: unknown): asserts this {} -} - -declare const lol: string | number | unknown | null; - -const thisAsserter: ThisAsserter = new ThisAsserter(); -thisAsserter.assertThis(lol); - `, - }, - { - Code: ` -function assert(this: object, a: number, b: unknown): asserts b; -function assert(a: bigint, b: unknown): asserts b; -function assert(this: object, a: string, two: string): asserts two; -function assert( - this: object, - a: string, - assertee: string, - c: bigint, - d: object, -): asserts assertee; -function assert(...args: any[]): void; - -function assert(...args: any[]) { - throw new Error('lol'); -} - -declare const nullableString: string | null; -assert(3 as any, nullableString); - `, - }, - // Intentional use of `any` to test a function call with no call signatures. - { - Code: ` -declare const assert: any; -declare const nullableString: string | null; -assert(nullableString); + enum ExampleEnum { + This = 0, + That = 'one', + } + (value?: ExampleEnum) => (value ? 1 : 0); `, - }, - - // ======================================== - // ARRAY PREDICATE FUNCTIONS - // ======================================== - {Code: `['one', 'two', ''].some(x => x);`}, - {Code: `['one', 'two', ''].find(x => x);`}, - {Code: `['one', 'two', ''].every(x => x);`}, - {Code: `['one', 'two', ''].filter((x): boolean => x);`}, - {Code: `['one', 'two', ''].filter(x => Boolean(x));`}, - {Code: `['one', 'two', ''].filter(function (x): boolean { if (x) { return true; } });`}, - {Code: `['one', 'two', ''].filter(function (x): boolean { if (x) { return true; } throw new Error('oops'); });`}, - {Code: `declare const predicate: (string) => boolean; ['one', 'two', ''].filter(predicate);`}, - {Code: `declare function notNullish(x: T): x is NonNullable; ['one', null].filter(notNullish);`}, - {Code: `declare function predicate(x: string | null): x is string; ['one', null].filter(predicate);`}, - {Code: `declare function predicate(x: string | null): T; ['one', null].filter(predicate);`}, - {Code: `declare function f(x: number): boolean; declare function f(x: string | null): boolean; [35].filter(f);`}, - - // ======================================== - // SPECIAL CASES - // ======================================== - {Code: `for (let x = 0; ; x++) { break; }`}, - }, - []rule_tester.InvalidTestCase{ - // ======================================== - // NON-BOOLEAN IN RHS OF TEST EXPRESSION - // ======================================== - { - Code: `if (true && 1 + 1) {}`, Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "unexpectedNumber", - Line: 1, - }, + AllowNullableEnum: utils.Ref(true), }, }, { - Code: `while (false || 'a' + 'b') {}`, + Code: ` + enum ExampleEnum { + This = '', + That = 1, + } + (value?: ExampleEnum) => (!value ? 1 : 0); + `, Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "unexpectedString", - Line: 1, - }, + AllowNullableEnum: utils.Ref(true), }, }, { - Code: `(x: object) => (true || false || x ? true : false);`, + Code: ` + enum ExampleEnum { + This = 'this', + That = 1, + } + (value?: ExampleEnum) => (!value ? 1 : 0); + `, Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "unexpectedObjectContext", - Line: 1, - }, + AllowNullableEnum: utils.Ref(true), }, }, - - // ======================================== - // CHECK OUTERMOST OPERANDS - // ======================================== { - Code: `if (('' && {}) || (0 && void 0)) { }`, + Code: ` + enum ExampleEnum { + This = '', + That = 0, + } + (value?: ExampleEnum) => (!value ? 1 : 0); + `, Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, - {MessageId: "unexpectedObjectContext", Line: 1}, - {MessageId: "unexpectedNumber", Line: 1}, - {MessageId: "unexpectedNullish", Line: 1}, + AllowNullableEnum: utils.Ref(true), }, }, - - // ======================================== - // ARRAY PREDICATE WITH NON-BOOLEAN - // ======================================== { - Code: `declare const array: string[]; array.some(x => x);`, + Code: ` + enum ExampleEnum { + This = '', + That = 0, + } + declare const arrayOfArrays: (ExampleEnum | null)[]; + const isAnyNonEmptyArray1 = arrayOfArrays.some(array => array); + `, Options: StrictBooleanExpressionsOptions{ - AllowNullableBoolean: utils.Ref(true), - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, + AllowNullableEnum: utils.Ref(true), }, }, - - // ======================================== - // BRANDED TYPES - // ======================================== { Code: ` - declare const foo: true & { __BRAND: 'Foo' }; - if (('' && foo) || (0 && void 0)) { } - `, + declare const x: string[] | null; + // eslint-disable-next-line + if (x) { + } + `, + TSConfig: "tsconfig.unstrict.json", Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 3}, - {MessageId: "unexpectedNumber", Line: 3}, - {MessageId: "unexpectedNullish", Line: 3}, + AllowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: utils.Ref(true), }, }, { Code: ` - declare const foo: false & { __BRAND: 'Foo' }; - if (('' && {}) || (foo && void 0)) { } - `, + function f(arg: 'a' | null) { + if (arg) console.log(arg); + } + `}, + { + Code: ` + function f(arg: 'a' | 'b' | null) { + if (arg) console.log(arg); + } + `}, + { + Code: ` + declare const x: 1 | null; + declare const y: 1; + if (x) { + } + if (y) { + } + `, Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 3}, - {MessageId: "unexpectedObjectContext", Line: 3}, - {MessageId: "unexpectedNullish", Line: 3}, + AllowNumber: utils.Ref(true), }, }, + { + Code: ` + function f(arg: 1 | null) { + if (arg) console.log(arg); + } + `}, + { + Code: ` + function f(arg: 1 | 2 | null) { + if (arg) console.log(arg); + } + `}, + { + Code: ` + interface Options { + readonly enableSomething?: true; + } - // ======================================== - // LOGICAL OPERANDS FOR CONTROL FLOW - // ======================================== + function f(opts: Options): void { + if (opts.enableSomething) console.log('Do something'); + } + `}, { - Code: `'asd' && 123 && [] && null;`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, - {MessageId: "unexpectedNumber", Line: 1}, - {MessageId: "unexpectedObjectContext", Line: 1}, - }, - }, + Code: ` + declare const x: true | null; + if (x) { + } + `}, { - Code: `'asd' || 123 || [] || null;`, + Code: ` + declare const x: 'a' | null; + declare const y: 'a'; + if (x) { + } + if (y) { + } + `, Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, - {MessageId: "unexpectedNumber", Line: 1}, - {MessageId: "unexpectedObjectContext", Line: 1}, + AllowString: utils.Ref(true), }, }, { - Code: `let x = (1 && 'a' && null) || 0 || '' || {};`, + Code: ` + declare const foo: boolean & { __BRAND: 'Foo' }; + if (foo) { + } + `}, + { + Code: ` + declare const foo: true & { __BRAND: 'Foo' }; + if (foo) { + } + `}, + { + Code: ` + declare const foo: false & { __BRAND: 'Foo' }; + if (foo) { + } + `}, + { + Code: ` + declare function assert(a: number, b: unknown): asserts a; + declare const nullableString: string | null; + declare const boo: boolean; + assert(boo, nullableString); + `}, + { + Code: ` + declare function assert(a: boolean, b: unknown): asserts b is string; + declare const nullableString: string | null; + declare const boo: boolean; + assert(boo, nullableString); + `}, + { + Code: ` + declare function assert(a: number, b: unknown): asserts b; + declare const nullableString: string | null; + declare const boo: boolean; + assert(nullableString, boo); + `}, + { + Code: ` + declare function assert(a: number, b: unknown): asserts b; + declare const nullableString: string | null; + declare const boo: boolean; + assert(...nullableString, nullableString); + `}, + { + Code: ` + declare function assert( + this: object, + a: number, + b?: unknown, + c?: unknown, + ): asserts c; + declare const nullableString: string | null; + declare const foo: number; + const o: { assert: typeof assert } = { + assert, + }; + o.assert(foo, nullableString); + `}, + { + Code: ` + declare function assert(x: unknown): x is string; + declare const nullableString: string | null; + assert(nullableString); + `}, + { + Code: ` + class ThisAsserter { + assertThis(this: unknown, arg2: unknown): asserts this {} + } + + declare const lol: string | number | unknown | null; + + const thisAsserter: ThisAsserter = new ThisAsserter(); + thisAsserter.assertThis(lol); + `}, + { + Code: ` + function assert(this: object, a: number, b: unknown): asserts b; + function assert(a: bigint, b: unknown): asserts b; + function assert(this: object, a: string, two: string): asserts two; + function assert( + this: object, + a: string, + assertee: string, + c: bigint, + d: object, + ): asserts assertee; + function assert(...args: any[]): void; + + function assert(...args: any[]) { + throw new Error('lol'); + } + + declare const nullableString: string | null; + assert(3 as any, nullableString); + `}, + { + Code: ` + declare const assert: any; + declare const nullableString: string | null; + assert(nullableString); + `}, + { + Code: ` + for (let x = 0; ; x++) { + break; + } + `}, + { + Code: ` + [true, false].some(function (x) { + return x; + }); + `}, + { + Code: ` + [true, false].some(function check(x) { + return x; + }); + `}, + { + Code: ` + [true, false].some(x => { + return x; + }); + `}, + { + Code: ` + [1, null].filter(function (x) { + return x != null; + }); + `}, + { + Code: ` + ['one', 'two', ''].filter(function (x) { + return !!x; + }); + `}, + { + Code: ` + ['one', 'two', ''].filter(function (x): boolean { + return !!x; + }); + `}, + { + Code: ` + ['one', 'two', ''].filter(function (x): boolean { + if (x) { + return true; + } + }); + `}, + { + Code: ` + ['one', 'two', ''].filter(function (x): boolean { + if (x) { + return true; + } + + throw new Error('oops'); + }); + `}, + { + Code: ` + declare const predicate: (string) => boolean; + ['one', 'two', ''].filter(predicate); + `}, + { + Code: ` + declare function notNullish(x: T): x is NonNullable; + ['one', null].filter(notNullish); + `}, + { + Code: ` + declare function predicate(x: string | null): x is string; + ['one', null].filter(predicate); + `}, + { + Code: ` + declare function predicate(x: string | null): T; + ['one', null].filter(predicate); + `}, + { + Code: ` + declare function f(x: number): boolean; + declare function f(x: string | null): boolean; + + [35].filter(f); + `}, + }, []rule_tester.InvalidTestCase{ + { + Code: ` + if (true && 1 + 1) { + } + `, Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), + AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), + }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 2}, + } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */}, + { + Code: "while (false || 'a' + 'b') {}", + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, - {MessageId: "unexpectedString", Line: 1}, - {MessageId: "unexpectedNullish", Line: 1}, - {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedString", Line: 1}, + } /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */}, + { + Code: "(x: object) => (true || false || x ? true : false);", + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, - }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObject", Line: 1}, + }}, { - Code: `return (1 || 'a' || null) && 0 && '' && {};`, + Code: "if (('' && {}) || (0 && void 0)) { }", Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), + AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, - {MessageId: "unexpectedString", Line: 1}, - {MessageId: "unexpectedNullish", Line: 1}, - {MessageId: "unexpectedNumber", Line: 1}, - {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedString", Line: 1}, {MessageId: "unexpectedObject", Line: 1}, {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedNullish", Line: 1}, + } /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */}, + { + Code: ` + declare const array: string[]; + array.some(x => x); + `, + Options: StrictBooleanExpressionsOptions{ + AllowNullableBoolean: utils.Ref(true), AllowString: utils.Ref(false), }, - }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString"}, + } /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, explicitBooleanReturnType */}, { - Code: `console.log((1 && []) || ('a' && {}));`, + Code: ` + declare const foo: true & { __BRAND: 'Foo' }; + if (('' && foo) || (0 && void 0)) { } + `, Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), + AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, - {MessageId: "unexpectedObjectContext", Line: 1}, - {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedString", Line: 3}, {MessageId: "unexpectedNumber", Line: 3}, {MessageId: "unexpectedNullish", Line: 3}, + } /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */}, + { + Code: ` + declare const foo: false & { __BRAND: 'Foo' }; + if (('' && {}) || (foo && void 0)) { } + `, + Options: StrictBooleanExpressionsOptions{ + AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, - }, - - // ======================================== - // CONDITIONALS WITH ALL OPERANDS CHECKED - // ======================================== + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 3}, {MessageId: "unexpectedObject", Line: 3}, {MessageId: "unexpectedNullish", Line: 3}, + } /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */}, { - Code: `if ((1 && []) || ('a' && {})) void 0;`, + Code: "'asd' && 123 && [] && null;", Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), + AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, - {MessageId: "unexpectedObjectContext", Line: 1}, - {MessageId: "unexpectedString", Line: 1}, - {MessageId: "unexpectedObjectContext", Line: 1}, + {MessageId: "unexpectedString", Line: 1}, {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedObject", Line: 1}, + } /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */}, + { + Code: "'asd' || 123 || [] || null;", + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, - }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 1}, {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedObject", Line: 1}, + } /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */}, { - Code: `let x = null || 0 || 'a' || [] ? {} : undefined;`, + Code: "let x = (1 && 'a' && null) || 0 || '' || {};", Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), + AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1}, - {MessageId: "unexpectedNumber", Line: 1}, - {MessageId: "unexpectedString", Line: 1}, - {MessageId: "unexpectedObjectContext", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedString", Line: 1}, {MessageId: "unexpectedNullish", Line: 1}, {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedString", Line: 1}, + } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */}, + { + Code: "return (1 || 'a' || null) && 0 && '' && {};", + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, - }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedString", Line: 1}, {MessageId: "unexpectedNullish", Line: 1}, {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedString", Line: 1}, + } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */}, { - Code: `return !(null || 0 || 'a' || []);`, + Code: "console.log((1 && []) || ('a' && {}));", Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - AllowString: utils.Ref(false), + AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1}, - {MessageId: "unexpectedNumber", Line: 1}, - {MessageId: "unexpectedString", Line: 1}, - {MessageId: "unexpectedObjectContext", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedObject", Line: 1}, {MessageId: "unexpectedString", Line: 1}, + } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */}, + { + Code: "if ((1 && []) || ('a' && {})) void 0;", + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, - }, - - // ======================================== - // NULLISH VALUES IN BOOLEAN CONTEXT - // ======================================== + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedObject", Line: 1}, {MessageId: "unexpectedString", Line: 1}, {MessageId: "unexpectedObject", Line: 1}, + } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */}, { - Code: `null || {};`, + Code: "let x = null || 0 || 'a' || [] ? {} : undefined;", + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), + }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1}, + {MessageId: "unexpectedNullish", Line: 1}, {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedString", Line: 1}, {MessageId: "unexpectedObject", Line: 1}, + } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */}, + { + Code: "return !(null || 0 || 'a' || []);", + Options: StrictBooleanExpressionsOptions{ + AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, - }, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullish", Line: 1}, {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedString", Line: 1}, {MessageId: "unexpectedObject", Line: 1}, + } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */}, { - Code: `undefined && [];`, + Code: "null || {};", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullish", Line: 1}, - }, - }, + }}, { - Code: `declare const x: null; if (x) {}`, + Code: "undefined && [];", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullish", Line: 1}, - }, - }, + }}, + { + Code: ` + declare const x: null; + if (x) { + } + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullish", Line: 3}, + }}, { - Code: `(x: undefined) => !x;`, + Code: "(x: undefined) => !x;", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullish", Line: 1}, - }, - }, + }}, { - Code: `(x: T) => (x ? 1 : 0);`, + Code: "(x: T) => (x ? 1 : 0);", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullish", Line: 1}, - }, - }, + }}, { - Code: `(x: T) => (x ? 1 : 0);`, + Code: "(x: T) => (x ? 1 : 0);", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullish", Line: 1}, - }, - }, + }}, { - Code: `(x: T) => (x ? 1 : 0);`, + Code: "(x: T) => (x ? 1 : 0);", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullish", Line: 1}, - }, - }, - - // ======================================== - // OBJECT IN BOOLEAN CONTEXT - // ======================================== + }}, { - Code: `[] || 1;`, + Code: "[] || 1;", Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, - }, - }, + {MessageId: "unexpectedObject", Line: 1}, + }}, { - Code: `({}) && 'a';`, + Code: "({}) && 'a';", Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, - }, - }, + {MessageId: "unexpectedObject", Line: 1}, + }}, { - Code: `declare const x: symbol; if (x) {}`, + Code: ` + declare const x: symbol; + if (x) { + } + `, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, - }, - }, + {MessageId: "unexpectedObject", Line: 3}, + }}, { - Code: `(x: () => void) => !x;`, + Code: "(x: () => void) => !x;", Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, - }, - }, + {MessageId: "unexpectedObject", Line: 1}, + }}, { - Code: `(x: T) => (x ? 1 : 0);`, + Code: "(x: T) => (x ? 1 : 0);", Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, - }, - }, + {MessageId: "unexpectedObject", Line: 1}, + }}, { - Code: `(x: T) => (x ? 1 : 0);`, + Code: "(x: T) => (x ? 1 : 0);", Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, - }, - }, + {MessageId: "unexpectedObject", Line: 1}, + }}, { - Code: `(x: T) => (x ? 1 : 0);`, + Code: "(x: T) => (x ? 1 : 0);", Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, - }, - }, + {MessageId: "unexpectedObject", Line: 1}, + }}, { - Code: ` void>(x: T) => (x ? 1 : 0);`, + Code: " void>(x: T) => (x ? 1 : 0);", Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, - }, - }, - - // ======================================== - // STRING IN BOOLEAN CONTEXT WITH ALLOWSTRING: FALSE - // ======================================== + {MessageId: "unexpectedObject", Line: 1}, + }}, { - Code: `while ('') {}`, + Code: "while ('') {}", Options: StrictBooleanExpressionsOptions{ AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedString", Line: 1}, - }, - }, + } /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */}, { - Code: `for (; 'foo'; ) {}`, + Code: "for (; 'foo'; ) {}", Options: StrictBooleanExpressionsOptions{ AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedString", Line: 1}, - }, - }, + } /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */}, { - Code: `declare const x: string; if (x) {}`, + Code: ` + declare const x: string; + if (x) { + } + `, Options: StrictBooleanExpressionsOptions{ AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, - }, - }, + {MessageId: "unexpectedString", Line: 3}, + } /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */}, { - Code: `(x: string) => !x;`, + Code: "(x: string) => !x;", Options: StrictBooleanExpressionsOptions{ AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedString", Line: 1}, - }, - }, + } /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */}, { - Code: `(x: T) => (x ? 1 : 0);`, + Code: "(x: T) => (x ? 1 : 0);", Options: StrictBooleanExpressionsOptions{ AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedString", Line: 1}, - }, - }, - - // ======================================== - // NUMBER IN BOOLEAN CONTEXT WITH ALLOWNUMBER: FALSE - // ======================================== + } /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */}, { - Code: `while (0n) {}`, + Code: "while (0n) {}", Options: StrictBooleanExpressionsOptions{ AllowNumber: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNumber", Line: 1}, - }, - }, + } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */}, { - Code: `for (; 123; ) {}`, + Code: "for (; 123; ) {}", Options: StrictBooleanExpressionsOptions{ AllowNumber: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNumber", Line: 1}, - }, - }, + } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */}, { - Code: `declare const x: number; if (x) {}`, + Code: ` + declare const x: number; + if (x) { + } + `, Options: StrictBooleanExpressionsOptions{ AllowNumber: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, - }, - }, + {MessageId: "unexpectedNumber", Line: 3}, + } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */}, { - Code: `(x: bigint) => !x;`, + Code: "(x: bigint) => !x;", Options: StrictBooleanExpressionsOptions{ AllowNumber: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNumber", Line: 1}, - }, - }, + } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */}, { - Code: `(x: T) => (x ? 1 : 0);`, + Code: "(x: T) => (x ? 1 : 0);", Options: StrictBooleanExpressionsOptions{ AllowNumber: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNumber", Line: 1}, - }, - }, + } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */}, { - Code: `![]['length'];`, + Code: "![]['length']; // doesn't count as array.length when computed", Options: StrictBooleanExpressionsOptions{ AllowNumber: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNumber", Line: 1}, - }, - }, + } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */}, { - Code: `declare const a: any[] & { notLength: number }; if (a.notLength) {}`, + Code: ` + declare const a: any[] & { notLength: number }; + if (a.notLength) { + } + `, Options: StrictBooleanExpressionsOptions{ AllowNumber: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, - }, - }, - - // ======================================== - // ARRAY.LENGTH IN BOOLEAN CONTEXT - // ======================================== + {MessageId: "unexpectedNumber", Line: 3}, + } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */}, { - Code: `if (![].length) {}`, + Code: ` + if (![].length) { + } + `, Options: StrictBooleanExpressionsOptions{ AllowNumber: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, - }, - }, + {MessageId: "unexpectedNumber", Line: 2}, + } /* Suggestions: conditionFixCompareArrayLengthZero */}, { - Code: `(a: number[]) => a.length && '...';`, + Code: ` + (a: number[]) => a.length && '...'; + `, Options: StrictBooleanExpressionsOptions{ AllowNumber: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, - }, - }, + {MessageId: "unexpectedNumber", Line: 2}, + } /* Suggestions: conditionFixCompareArrayLengthNonzero */}, { - Code: `(...a: T) => a.length || 'empty';`, + Code: ` + (...a: T) => a.length || 'empty'; + `, Options: StrictBooleanExpressionsOptions{ AllowNumber: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, - }, - }, - - // ======================================== - // MIXED STRING | NUMBER VALUE IN BOOLEAN CONTEXT - // ======================================== + {MessageId: "unexpectedNumber", Line: 2}, + } /* Suggestions: conditionFixCompareArrayLengthNonzero */}, { - Code: `declare const x: string | number; if (x) {}`, + Code: ` + declare const x: string | number; + if (x) { + } + `, Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(true), - AllowString: utils.Ref(true), + AllowNumber: utils.Ref(true), AllowString: utils.Ref(true), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedMixedCondition", Line: 1}, - }, - }, + {MessageId: "unexpectedMixedCondition", Line: 3}, + }}, { - Code: `(x: bigint | string) => !x;`, + Code: "(x: bigint | string) => !x;", Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(true), - AllowString: utils.Ref(true), + AllowNumber: utils.Ref(true), AllowString: utils.Ref(true), }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedMixedCondition", Line: 1}, - }, - }, + }}, { - Code: `(x: T) => (x ? 1 : 0);`, + Code: "(x: T) => (x ? 1 : 0);", Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(true), - AllowString: utils.Ref(true), + AllowNumber: utils.Ref(true), AllowString: utils.Ref(true), }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedMixedCondition", Line: 1}, - }, - }, - - // ======================================== - // NULLABLE BOOLEAN WITHOUT OPTION - // ======================================== + }}, { - Code: `declare const x: boolean | null; if (x) {}`, + Code: ` + declare const x: boolean | null; + if (x) { + } + `, Options: StrictBooleanExpressionsOptions{ AllowNullableBoolean: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableBoolean", Line: 1}, - }, - }, + {MessageId: "unexpectedNullableBoolean", Line: 3}, + } /* Suggestions: conditionFixDefaultFalse, conditionFixCompareTrue */}, { - Code: `(x?: boolean) => !x;`, + Code: "(x?: boolean) => !x;", Options: StrictBooleanExpressionsOptions{ AllowNullableBoolean: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableBoolean", Line: 1}, - }, - }, + } /* Suggestions: conditionFixDefaultFalse, conditionFixCompareFalse */}, { - Code: `(x: T) => (x ? 1 : 0);`, + Code: "(x: T) => (x ? 1 : 0);", Options: StrictBooleanExpressionsOptions{ AllowNullableBoolean: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableBoolean", Line: 1}, - }, - }, - - // ======================================== - // NULLABLE OBJECT WITHOUT OPTION - // ======================================== + } /* Suggestions: conditionFixDefaultFalse, conditionFixCompareTrue */}, { - Code: `declare const x: object | null; if (x) {}`, + Code: ` + declare const x: object | null; + if (x) { + } + `, Options: StrictBooleanExpressionsOptions{ AllowNullableObject: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableObject", Line: 1}, - }, - }, + {MessageId: "unexpectedNullableObject", Line: 3}, + } /* Suggestions: conditionFixCompareNullish */}, { - Code: `(x?: { a: number }) => !x;`, + Code: "(x?: { a: number }) => !x;", Options: StrictBooleanExpressionsOptions{ AllowNullableObject: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableObject", Line: 1}, - }, - }, + } /* Suggestions: conditionFixCompareNullish */}, { - Code: `(x: T) => (x ? 1 : 0);`, + Code: "(x: T) => (x ? 1 : 0);", Options: StrictBooleanExpressionsOptions{ AllowNullableObject: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableObject", Line: 1}, - }, - }, - - // ======================================== - // NULLABLE STRING WITHOUT OPTION - // ======================================== + } /* Suggestions: conditionFixCompareNullish */}, { - Code: `declare const x: string | null; if (x) {}`, + Code: ` + declare const x: string | null; + if (x) { + } + `, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 1}, - }, - }, + {MessageId: "unexpectedNullableString", Line: 3}, + } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */}, { - Code: `(x?: string) => !x;`, + Code: "(x?: string) => !x;", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableString", Line: 1}, - }, - }, + } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */}, { - Code: `(x: T) => (x ? 1 : 0);`, + Code: "(x: T) => (x ? 1 : 0);", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableString", Line: 1}, - }, - }, + } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */}, { - Code: `function foo(x: '' | 'bar' | null) { if (!x) {} }`, + Code: ` + function foo(x: '' | 'bar' | null) { + if (!x) { + } + } + `, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 1}, - }, - }, - - // ======================================== - // NULLABLE NUMBER WITHOUT OPTION - // ======================================== + {MessageId: "unexpectedNullableString", Line: 3}, + } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */}, { - Code: `declare const x: number | null; if (x) {}`, + Code: ` + declare const x: number | null; + if (x) { + } + `, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableNumber", Line: 1}, - }, - }, + {MessageId: "unexpectedNullableNumber", Line: 3}, + } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultZero, conditionFixCastBoolean */}, { - Code: `(x?: number) => !x;`, + Code: "(x?: number) => !x;", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableNumber", Line: 1}, - }, - }, + } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultZero, conditionFixCastBoolean */}, { - Code: `(x: T) => (x ? 1 : 0);`, + Code: "(x: T) => (x ? 1 : 0);", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableNumber", Line: 1}, - }, - }, + } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultZero, conditionFixCastBoolean */}, { - Code: `function foo(x: 0 | 1 | null) { if (!x) {} }`, + Code: ` + function foo(x: 0 | 1 | null) { + if (!x) { + } + } + `, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableNumber", Line: 1}, - }, + {MessageId: "unexpectedNullableNumber", Line: 3}, + }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultZero, conditionFixCastBoolean */ }, - - // ======================================== - // NULLABLE ENUM WITHOUT OPTION - // ======================================== { Code: ` - enum ExampleEnum { This = 0, That = 1 } - const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; - if (theEnum) {} - `, + enum ExampleEnum { + This = 0, + That = 1, + } + const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + if (theEnum) { + } + `, Options: StrictBooleanExpressionsOptions{ AllowNullableEnum: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableNumber", Line: 4}, - }, + {MessageId: "unexpectedNullableEnum", Line: 7}, + }, /* Suggestions: conditionFixCompareNullish */ }, { Code: ` - enum ExampleEnum { This = 0, That = 1 } - const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; - if (!theEnum) {} - `, + enum ExampleEnum { + This = 0, + That = 1, + } + const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + if (!theEnum) { + } + `, Options: StrictBooleanExpressionsOptions{ AllowNullableEnum: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableNumber", Line: 4}, - }, - }, + {MessageId: "unexpectedNullableEnum", Line: 7}, + } /* Suggestions: conditionFixCompareNullish */}, { Code: ` - enum ExampleEnum { This, That } - const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; - if (!theEnum) {} - `, + enum ExampleEnum { + This, + That, + } + const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + if (!theEnum) { + } + `, Options: StrictBooleanExpressionsOptions{ AllowNullableEnum: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableNumber", Line: 4}, - }, - }, + {MessageId: "unexpectedNullableEnum", Line: 7}, + } /* Suggestions: conditionFixCompareNullish */}, { Code: ` - enum ExampleEnum { This = '', That = 'a' } - const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; - if (!theEnum) {} - `, + enum ExampleEnum { + This = '', + That = 'a', + } + const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + if (!theEnum) { + } + `, Options: StrictBooleanExpressionsOptions{ AllowNullableEnum: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 4}, - }, - }, + {MessageId: "unexpectedNullableEnum", Line: 7}, + } /* Suggestions: conditionFixCompareNullish */}, { Code: ` - enum ExampleEnum { This = '', That = 0 } - const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; - if (!theEnum) {} - `, + enum ExampleEnum { + This = '', + That = 0, + } + const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + if (!theEnum) { + } + `, Options: StrictBooleanExpressionsOptions{ AllowNullableEnum: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedMixedCondition", Line: 4}, - }, - }, + {MessageId: "unexpectedNullableEnum", Line: 7}, + } /* Suggestions: conditionFixCompareNullish */}, { Code: ` - enum ExampleEnum { This = 'one', That = 'two' } - const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; - if (!theEnum) {} - `, + enum ExampleEnum { + This = 'one', + That = 'two', + } + const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + if (!theEnum) { + } + `, Options: StrictBooleanExpressionsOptions{ AllowNullableEnum: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 4}, - }, - }, + {MessageId: "unexpectedNullableEnum", Line: 7}, + } /* Suggestions: conditionFixCompareNullish */}, { Code: ` - enum ExampleEnum { This = 1, That = 2 } - const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; - if (!theEnum) {} - `, + enum ExampleEnum { + This = 1, + That = 2, + } + const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + if (!theEnum) { + } + `, Options: StrictBooleanExpressionsOptions{ AllowNullableEnum: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableNumber", Line: 4}, - }, - }, - - // ======================================== - // NULLABLE MIXED ENUM WITHOUT OPTION - // ======================================== + {MessageId: "unexpectedNullableEnum", Line: 7}, + } /* Suggestions: conditionFixCompareNullish */}, { Code: ` - enum ExampleEnum { This = 0, That = 'one' } - (value?: ExampleEnum) => (value ? 1 : 0); - `, + enum ExampleEnum { + This = 0, + That = 'one', + } + (value?: ExampleEnum) => (value ? 1 : 0); + `, Options: StrictBooleanExpressionsOptions{ AllowNullableEnum: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedMixedCondition", Line: 3}, - }, - }, + {MessageId: "unexpectedNullableEnum", Line: 6}, + } /* Suggestions: conditionFixCompareNullish */}, { Code: ` - enum ExampleEnum { This = '', That = 1 } - (value?: ExampleEnum) => (!value ? 1 : 0); - `, + enum ExampleEnum { + This = '', + That = 1, + } + (value?: ExampleEnum) => (!value ? 1 : 0); + `, Options: StrictBooleanExpressionsOptions{ AllowNullableEnum: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedMixedCondition", Line: 3}, - }, - }, + {MessageId: "unexpectedNullableEnum", Line: 6}, + } /* Suggestions: conditionFixCompareNullish */}, { Code: ` - enum ExampleEnum { This = 'this', That = 1 } - (value?: ExampleEnum) => (value ? 1 : 0); - `, + enum ExampleEnum { + This = 'this', + That = 1, + } + (value?: ExampleEnum) => (!value ? 1 : 0); + `, Options: StrictBooleanExpressionsOptions{ AllowNullableEnum: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedMixedCondition", Line: 3}, - }, - }, - - // ======================================== - // ANY WITHOUT OPTION - // ======================================== + {MessageId: "unexpectedNullableEnum", Line: 6}, + } /* Suggestions: conditionFixCompareNullish */}, { - Code: `if ((Boolean(x) || {}) || (typeof x === 'string' && x)) {}`, + Code: ` + enum ExampleEnum { + This = '', + That = 0, + } + (value?: ExampleEnum) => (!value ? 1 : 0); + `, Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), + AllowNullableEnum: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, - {MessageId: "unexpectedString", Line: 1}, - }, - }, + {MessageId: "unexpectedNullableEnum", Line: 6}, + } /* Suggestions: conditionFixCompareNullish */}, + { + Code: ` + if (x) { + } + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedAny", Line: 2}, + } /* Suggestions: conditionFixCastBoolean */}, + { + Code: "x => !x;", + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedAny", Line: 1}, + } /* Suggestions: conditionFixCastBoolean */}, + { + Code: "(x: T) => (x ? 1 : 0);", + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedAny", Line: 1}, + } /* Suggestions: conditionFixCastBoolean */}, + { + Code: "(x: T) => (x ? 1 : 0);", + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedAny", Line: 1}, + } /* Suggestions: conditionFixCastBoolean */}, + { + Code: ` + declare const x: string[] | null; + if (x) { + } + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "noStrictNullCheck", Line: 0}, {MessageId: "unexpectedObject", Line: 3}, + }}, { - Code: `if (1) {}`, + Code: ` + declare const obj: { x: number } | null; + !obj ? 1 : 0 + !obj + obj || 0 + obj && 1 || 0 + `, Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), + AllowNullableObject: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, - }, - }, + {MessageId: "unexpectedNullableObject", Line: 3}, {MessageId: "unexpectedNullableObject", Line: 4}, {MessageId: "unexpectedNullableObject", Line: 5}, {MessageId: "unexpectedNullableObject", Line: 6}, + } /* Suggestions: conditionFixCompareNullish, conditionFixCompareNullish, conditionFixCompareNullish, conditionFixCompareNullish */}, + { + Code: ` + declare function assert(x: unknown): asserts x; + declare const nullableString: string | null; + assert(nullableString); + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableString", Line: 4}, + } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */}, + { + Code: ` + declare function assert(a: number, b: unknown): asserts b; + declare const nullableString: string | null; + assert(foo, nullableString); + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableString", Line: 4}, + } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */}, + { + Code: ` + declare function assert(a: number, b: unknown): asserts b; + declare function assert(one: number, two: unknown): asserts two; + declare const nullableString: string | null; + assert(foo, nullableString); + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableString", Line: 5}, + } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */}, + { + Code: ` + declare function assert(this: object, a: number, b: unknown): asserts b; + declare const nullableString: string | null; + assert(foo, nullableString); + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableString", Line: 4}, + } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */}, + { + Code: ` + function asserts1(x: string | number | undefined): asserts x {} + function asserts2(x: string | number | undefined): asserts x {} - // ======================================== - // ASSERT FUNCTIONS - // ======================================== + const maybeString = Math.random() ? 'string'.slice() : undefined; + + const someAssert: typeof asserts1 | typeof asserts2 = + Math.random() > 0.5 ? asserts1 : asserts2; + + someAssert(maybeString); + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableString"}, + } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */}, { Code: ` - declare function assert(a: boolean, b: unknown): asserts b; - declare function assert({ a }: { a: boolean }, b: unknown): asserts b; - declare const nullableString: string | null; - declare const boo: boolean; - assert(boo, nullableString); - `, + function assert(this: object, a: number, b: unknown): asserts b; + function assert(a: bigint, b: unknown): asserts b; + function assert(this: object, a: string, two: string): asserts two; + function assert( + this: object, + a: string, + assertee: string, + c: bigint, + d: object, + ): asserts assertee; + + function assert(...args: any[]) { + throw new Error('lol'); + } + + declare const nullableString: string | null; + assert(3 as any, nullableString); + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableString", Line: 18}, + } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */}, + { + Code: ` + function assert(this: object, a: number, b: unknown): asserts b; + function assert(a: bigint, b: unknown): asserts b; + function assert(this: object, a: string, two: string): asserts two; + function assert( + this: object, + a: string, + assertee: string, + c: bigint, + d: object, + ): asserts assertee; + function assert(a: any, two: unknown, ...rest: any[]): asserts two; + + function assert(...args: any[]) { + throw new Error('lol'); + } + + declare const nullableString: string | null; + assert(3 as any, nullableString, 'more', 'args', 'afterwards'); + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableString", Line: 19}, + } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */}, + { + Code: ` + declare function assert(a: boolean, b: unknown): asserts b; + declare function assert({ a }: { a: boolean }, b: unknown): asserts b; + declare const nullableString: string | null; + declare const boo: boolean; + assert(boo, nullableString); + `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableString", Line: 6}, + } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */}, + { + Code: ` + function assert(one: unknown): asserts one; + function assert(one: unknown, two: unknown): asserts two; + function assert(...args: unknown[]) { + throw new Error('not implemented'); + } + declare const nullableString: string | null; + assert(nullableString); + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableString", Line: 8}, + } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */}, + { + Code: ` + ['one', 'two', ''].find(x => { + return x; + }); + `, + Options: StrictBooleanExpressionsOptions{ + AllowString: utils.Ref(false), }, - }, - - // ======================================== - // ARRAY FILTER PREDICATES - // ======================================== + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedString", Line: 2}, + } /* Suggestions: explicitBooleanReturnType */}, + { + Code: ` + ['one', 'two', ''].find(x => { + return; + }); + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullish", Line: 2}, + } /* Suggestions: explicitBooleanReturnType */}, + { + Code: ` + ['one', 'two', ''].findLast(x => { + return undefined; + }); + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullish", Line: 2}, + } /* Suggestions: explicitBooleanReturnType */}, { - Code: `declare const nullOrBool: boolean | null; [true, false, null].filter(x => nullOrBool);`, + Code: ` + ['one', 'two', ''].find(x => { + if (x) { + return Math.random() > 0.5; + } + }); + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableBoolean", Line: 2}, + } /* Suggestions: explicitBooleanReturnType */}, + { + Code: ` + const predicate = (x: string) => { + if (x) { + return Math.random() > 0.5; + } + }; + + ['one', 'two', ''].find(predicate); + `, Options: StrictBooleanExpressionsOptions{ AllowNullableBoolean: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableBoolean", Line: 1}, - }, - }, + {MessageId: "unexpectedNullableBoolean", Line: 8}, + }}, { - Code: `declare const nullOrString: string | null; ['', 'foo', null].filter(x => nullOrString);`, + Code: ` + [1, null].every(async x => { + return x != null; + }); + `, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 1}, - }, - }, + {MessageId: "predicateCannotBeAsync", Line: 2}, + }}, { - Code: `declare const nullOrNumber: number | null; [0, null].filter(x => nullOrNumber);`, + Code: ` + const predicate = async x => { + return x != null; + }; + + [1, null].every(predicate); + `, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableNumber", Line: 1}, - }, - }, + {MessageId: "unexpectedObject", Line: 6}, + }}, { - Code: `const objectValue: object = {}; [{ a: 0 }, {}].filter(x => objectValue);`, + Code: ` + [1, null].every((x): boolean | number => { + return x != null; + }); + `, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, - }, - }, + {MessageId: "unexpectedMixedCondition", Line: 2}, + }}, { - Code: `const objectValue: object = {}; [{ a: 0 }, {}].filter(x => { return objectValue; });`, + Code: ` + [1, null].every((x): boolean | undefined => { + return x != null; + }); + `, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObjectContext", Line: 1}, - }, - }, + {MessageId: "unexpectedNullableBoolean", Line: 2}, + }}, { - Code: `declare const nullOrObject: object | null; [{ a: 0 }, null].filter(x => nullOrObject);`, - Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), - }, + Code: ` + [1, null].every((x, i) => {}); + `, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableObject", Line: 1}, - }, - }, + {MessageId: "unexpectedNullish", Line: 2}, + } /* Suggestions: explicitBooleanReturnType */}, { - Code: `const numbers: number[] = [1]; [1, 2].filter(x => numbers.length);`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - }, + Code: ` + [() => {}, null].every((x: () => void) => {}); + `, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, - }, - }, + {MessageId: "unexpectedNullish", Line: 2}, + } /* Suggestions: explicitBooleanReturnType */}, { - Code: `const numberValue: number = 1; [1, 2].filter(x => numberValue);`, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - }, + Code: ` + [() => {}, null].every(function (x: () => void) {}); + `, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, - }, - }, + {MessageId: "unexpectedNullish", Line: 2}, + } /* Suggestions: explicitBooleanReturnType */}, { - Code: `const stringValue: string = 'hoge'; ['hoge', 'foo'].filter(x => stringValue);`, - Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), - }, + Code: ` + [() => {}, null].every(() => {}); + `, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, - }, - }, + {MessageId: "unexpectedNullish", Line: 2}, + } /* Suggestions: explicitBooleanReturnType */}, + { + Code: ` + declare function f(x: number): string; + declare function f(x: string | null): boolean; + + [35].filter(f); + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedMixedCondition", Line: 5}, + }}, + { + Code: ` + declare function f(x: number): string; + declare function f(x: number | boolean): boolean; + declare function f(x: string | null): boolean; - // ======================================== - // UNKNOWN TYPE WITHOUT OPTION - // ======================================== + [35].filter(f); + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedMixedCondition", Line: 6}, + }}, { - Code: `declare const x: unknown; if (x) {}`, + Code: ` + declare function foo(x: number): T; + [1, null].every(foo); + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedAny", Line: 3}, + }}, + { + Code: ` + function foo(x: number): T {} + [1, null].every(foo); + `, Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(false), + AllowNumber: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedAny", Line: 1}, - }, - }, + {MessageId: "unexpectedNumber", Line: 3}, + }}, + { + Code: ` + declare const nullOrString: string | null; + ['one', null].filter(x => nullOrString); + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableString", Line: 3}, + } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean, explicitBooleanReturnType */}, + { + Code: ` + declare const nullOrString: string | null; + ['one', null].filter(x => !nullOrString); + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableString", Line: 3}, + } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */}, { - Code: `declare const x: unknown; x ? 'a' : 'b';`, + Code: ` + declare const anyValue: any; + ['one', null].filter(x => anyValue); + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedAny", Line: 3}, + } /* Suggestions: conditionFixCastBoolean, explicitBooleanReturnType */}, + { + Code: ` + declare const nullOrBoolean: boolean | null; + [true, null].filter(x => nullOrBoolean); + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableBoolean", Line: 3}, + } /* Suggestions: conditionFixDefaultFalse, conditionFixCompareTrue, explicitBooleanReturnType */}, + { + Code: ` + enum ExampleEnum { + This = 0, + That = 1, + } + const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + [0, 1].filter(x => theEnum); + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableEnum", Line: 7}, + } /* Suggestions: conditionFixCompareNullish, explicitBooleanReturnType */}, + { + Code: ` + declare const nullOrNumber: number | null; + [0, null].filter(x => nullOrNumber); + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedNullableNumber", Line: 3}, + } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultZero, conditionFixCastBoolean, explicitBooleanReturnType */}, + { + Code: ` + const objectValue: object = {}; + [{ a: 0 }, {}].filter(x => objectValue); + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObject", Line: 3}, + } /* Suggestions: explicitBooleanReturnType */}, + { + Code: ` + const objectValue: object = {}; + [{ a: 0 }, {}].filter(x => { + return objectValue; + }); + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "unexpectedObject", Line: 3}, + } /* Suggestions: explicitBooleanReturnType */}, + { + Code: ` + declare const nullOrObject: object | null; + [{ a: 0 }, null].filter(x => nullOrObject); + `, Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(false), + AllowNullableObject: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedAny", Line: 1}, - }, - }, + {MessageId: "unexpectedNullableObject", Line: 3}, + } /* Suggestions: conditionFixCompareNullish, explicitBooleanReturnType */}, { - Code: `declare const x: unknown; x && 'a';`, + Code: ` + const numbers: number[] = [1]; + [1, 2].filter(x => numbers.length); + `, Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(false), + AllowNumber: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedAny", Line: 1}, - }, - }, + {MessageId: "unexpectedNumber", Line: 3}, + } /* Suggestions: conditionFixCompareArrayLengthNonzero, explicitBooleanReturnType */}, { - Code: `declare const x: unknown; x || 'a';`, + Code: ` + const numberValue: number = 1; + [1, 2].filter(x => numberValue); + `, Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(false), + AllowNumber: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedAny", Line: 1}, - }, - }, + {MessageId: "unexpectedNumber", Line: 3}, + } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, explicitBooleanReturnType */}, { - Code: `declare const x: unknown; !x;`, + Code: ` + const stringValue: string = 'hoge'; + ['hoge', 'foo'].filter(x => stringValue); + `, Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(false), + AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedAny", Line: 1}, - }, - }, + {MessageId: "unexpectedString", Line: 3}, + } /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, explicitBooleanReturnType */}, }) } From c11fd079557c7bf43826cd8d4e2169b8e4135a3c Mon Sep 17 00:00:00 2001 From: Noel Kim Date: Mon, 29 Sep 2025 15:45:07 +0900 Subject: [PATCH 14/27] wip --- .../strict_boolean_expressions.go | 9 +- .../strict_boolean_expressions_single_test.go | 11 +-- .../strict_boolean_expressions_test.go | 96 ++++++++++++------- 3 files changed, 74 insertions(+), 42 deletions(-) diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go index ebdbf7f0..9c784cf7 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go @@ -342,7 +342,7 @@ func analyzeType(typeChecker *checker.Checker, t *checker.Type) typeInfo { if partInfo.isEnum { info.isEnum = true } - if partInfo.variant == typeVariantNumber || partInfo.variant == typeVariantString { + if partInfo.variant == typeVariantBoolean || partInfo.variant == typeVariantNumber || partInfo.variant == typeVariantString { if metNotTruthy { continue } @@ -420,7 +420,7 @@ func analyzeTypePart(_ *checker.Checker, t *checker.Type) typeInfo { } if flags&(checker.TypeFlagsBoolean|checker.TypeFlagsBooleanLiteral|checker.TypeFlagsBooleanLike) != 0 { - if utils.IsTrueLiteralType(t) { + if t.AsLiteralType().String() == "true" { info.isTruthy = true } info.variant = typeVariantBoolean @@ -537,6 +537,11 @@ func checkCondition(ctx rule.RuleContext, node *ast.Node, t *checker.Type, opts ctx.ReportNode(node, buildUnexpectedNumber()) } case typeVariantBoolean: + // Known edge case: truthy primitives and nullish values are always valid boolean expressions + if info.isTruthy { + return + } + if info.isNullable && !*opts.AllowNullableBoolean { ctx.ReportNode(node, buildUnexpectedNullableBoolean()) } diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go index fe67b8e4..42f30c7f 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go @@ -11,18 +11,13 @@ import ( func TestStrictBooleanExpressionsSingleRule(t *testing.T) { rule_tester.RunRuleTester(fixtures.GetRootDir(), "tsconfig.json", t, &StrictBooleanExpressionsRule, []rule_tester.ValidTestCase{ - - }, []rule_tester.InvalidTestCase{ { Code: ` - function foo(x: 0 | 1 | null) { - if (!x) { - } + declare const foo: boolean & { __BRAND: 'Foo' }; + if (foo) { } `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableNumber", Line: 3}, - }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultZero, conditionFixCastBoolean */ }, + }, []rule_tester.InvalidTestCase{ }) } diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go index 048cf0c5..079da9c2 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go @@ -489,13 +489,15 @@ func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { function f(arg: 'a' | null) { if (arg) console.log(arg); } - `}, + `, + }, { Code: ` function f(arg: 'a' | 'b' | null) { if (arg) console.log(arg); } - `}, + `, + }, { Code: ` declare const x: 1 | null; @@ -514,13 +516,15 @@ func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { function f(arg: 1 | null) { if (arg) console.log(arg); } - `}, + `, + }, { Code: ` function f(arg: 1 | 2 | null) { if (arg) console.log(arg); } - `}, + `, + }, { Code: ` interface Options { @@ -530,13 +534,15 @@ func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { function f(opts: Options): void { if (opts.enableSomething) console.log('Do something'); } - `}, + `, + }, { Code: ` declare const x: true | null; if (x) { } - `}, + `, + }, { Code: ` declare const x: 'a' | null; @@ -555,47 +561,54 @@ func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { declare const foo: boolean & { __BRAND: 'Foo' }; if (foo) { } - `}, + `, + }, { Code: ` declare const foo: true & { __BRAND: 'Foo' }; if (foo) { } - `}, + `, + }, { Code: ` declare const foo: false & { __BRAND: 'Foo' }; if (foo) { } - `}, + `, + }, { Code: ` declare function assert(a: number, b: unknown): asserts a; declare const nullableString: string | null; declare const boo: boolean; assert(boo, nullableString); - `}, + `, + }, { Code: ` declare function assert(a: boolean, b: unknown): asserts b is string; declare const nullableString: string | null; declare const boo: boolean; assert(boo, nullableString); - `}, + `, + }, { Code: ` declare function assert(a: number, b: unknown): asserts b; declare const nullableString: string | null; declare const boo: boolean; assert(nullableString, boo); - `}, + `, + }, { Code: ` declare function assert(a: number, b: unknown): asserts b; declare const nullableString: string | null; declare const boo: boolean; assert(...nullableString, nullableString); - `}, + `, + }, { Code: ` declare function assert( @@ -610,13 +623,15 @@ func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { assert, }; o.assert(foo, nullableString); - `}, + `, + }, { Code: ` declare function assert(x: unknown): x is string; declare const nullableString: string | null; assert(nullableString); - `}, + `, + }, { Code: ` class ThisAsserter { @@ -627,7 +642,8 @@ func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { const thisAsserter: ThisAsserter = new ThisAsserter(); thisAsserter.assertThis(lol); - `}, + `, + }, { Code: ` function assert(this: object, a: number, b: unknown): asserts b; @@ -648,55 +664,64 @@ func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { declare const nullableString: string | null; assert(3 as any, nullableString); - `}, + `, + }, { Code: ` declare const assert: any; declare const nullableString: string | null; assert(nullableString); - `}, + `, + }, { Code: ` for (let x = 0; ; x++) { break; } - `}, + `, + }, { Code: ` [true, false].some(function (x) { return x; }); - `}, + `, + }, { Code: ` [true, false].some(function check(x) { return x; }); - `}, + `, + }, { Code: ` [true, false].some(x => { return x; }); - `}, + `, + }, { Code: ` [1, null].filter(function (x) { return x != null; }); - `}, + `, + }, { Code: ` ['one', 'two', ''].filter(function (x) { return !!x; }); - `}, + `, + }, { Code: ` ['one', 'two', ''].filter(function (x): boolean { return !!x; }); - `}, + `, + }, { Code: ` ['one', 'two', ''].filter(function (x): boolean { @@ -704,7 +729,8 @@ func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { return true; } }); - `}, + `, + }, { Code: ` ['one', 'two', ''].filter(function (x): boolean { @@ -714,34 +740,40 @@ func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { throw new Error('oops'); }); - `}, + `, + }, { Code: ` declare const predicate: (string) => boolean; ['one', 'two', ''].filter(predicate); - `}, + `, + }, { Code: ` declare function notNullish(x: T): x is NonNullable; ['one', null].filter(notNullish); - `}, + `, + }, { Code: ` declare function predicate(x: string | null): x is string; ['one', null].filter(predicate); - `}, + `, + }, { Code: ` declare function predicate(x: string | null): T; ['one', null].filter(predicate); - `}, + `, + }, { Code: ` declare function f(x: number): boolean; declare function f(x: string | null): boolean; [35].filter(f); - `}, + `, + }, }, []rule_tester.InvalidTestCase{ { Code: ` From 22f59cf151274df8dea977da3e2fb5d2c2b01c33 Mon Sep 17 00:00:00 2001 From: Noel Kim Date: Mon, 29 Sep 2025 15:51:01 +0900 Subject: [PATCH 15/27] branded boolean --- .../strict_boolean_expressions.go | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go index 9c784cf7..11b49fbe 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go @@ -390,10 +390,28 @@ func analyzeType(typeChecker *checker.Checker, t *checker.Type) typeInfo { return analyzeTypePart(typeChecker, t) } -func analyzeTypePart(_ *checker.Checker, t *checker.Type) typeInfo { +func analyzeTypePart(typeChecker *checker.Checker, t *checker.Type) typeInfo { info := typeInfo{} flags := checker.Type_flags(t) + if utils.IsIntersectionType(t) { + info.isIntersection = true + types := t.Types() + isBoolean := false + for _, t2 := range types { + if analyzeTypePart(typeChecker, t2).variant == typeVariantBoolean { + isBoolean = true + break + } + } + if isBoolean { + info.variant = typeVariantBoolean + } else { + info.variant = typeVariantObject + } + return info + } + if flags&checker.TypeFlagsTypeParameter != 0 { info.variant = typeVariantGeneric return info From 3bd0078a0089fdbdc362228434bb6b82e31f1a16 Mon Sep 17 00:00:00 2001 From: Noel Kim Date: Tue, 30 Sep 2025 10:55:23 +0900 Subject: [PATCH 16/27] unstrict --- .../strict_boolean_expressions.go | 3 +- .../strict_boolean_expressions_single_test.go | 22 +++- .../strict_boolean_expressions_test.go | 122 +++++++++++------- 3 files changed, 99 insertions(+), 48 deletions(-) diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go index 11b49fbe..ee586e15 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go @@ -92,7 +92,7 @@ func buildUnexpectedMixedCondition() rule.RuleMessage { func buildNoStrictNullCheck() rule.RuleMessage { return rule.RuleMessage{ - Id: "msgNoStrictNullCheck", + Id: "noStrictNullCheck", Description: "This rule requires the `strictNullChecks` compiler option to be turned on to function correctly.", } } @@ -149,7 +149,6 @@ var StrictBooleanExpressionsRule = rule.Rule{ core.NewTextRange(0, 0), buildNoStrictNullCheck(), ) - return rule.RuleListeners{} } return rule.RuleListeners{ diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go index 42f30c7f..0f31b38c 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go @@ -7,17 +7,35 @@ import ( "github.com/typescript-eslint/tsgolint/internal/rule_tester" "github.com/typescript-eslint/tsgolint/internal/rules/fixtures" + "github.com/typescript-eslint/tsgolint/internal/utils" ) func TestStrictBooleanExpressionsSingleRule(t *testing.T) { rule_tester.RunRuleTester(fixtures.GetRootDir(), "tsconfig.json", t, &StrictBooleanExpressionsRule, []rule_tester.ValidTestCase{ { Code: ` - declare const foo: boolean & { __BRAND: 'Foo' }; - if (foo) { + declare const x: string[] | null; + // oxlint-disable-next-line + if (x) { } `, + TSConfig: "tsconfig.unstrict.json", + Options: StrictBooleanExpressionsOptions{ + AllowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: utils.Ref(true), + }, }, }, []rule_tester.InvalidTestCase{ + { + Code: ` + declare const x: string[] | null; + if (x) { + } + `, + TSConfig: "tsconfig.unstrict.json", + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "noStrictNullCheck", Line: 0}, + {MessageId: "unexpectedObject", Line: 3}, + }, + }, }) } diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go index 079da9c2..9c58362c 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go @@ -472,18 +472,19 @@ func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { AllowNullableEnum: utils.Ref(true), }, }, - { - Code: ` - declare const x: string[] | null; - // eslint-disable-next-line - if (x) { - } - `, - TSConfig: "tsconfig.unstrict.json", - Options: StrictBooleanExpressionsOptions{ - AllowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: utils.Ref(true), - }, - }, + // TODO: oxlint-disable-next-line should not check + // { + // Code: ` + // declare const x: string[] | null; + //// oxlint-disable-next-line + //if (x) { + //} + //`, + // TSConfig: "tsconfig.unstrict.json", + // Options: StrictBooleanExpressionsOptions{ + // AllowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: utils.Ref(true), + // }, + // }, { Code: ` function f(arg: 'a' | null) { @@ -801,7 +802,8 @@ func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedObject", Line: 1}, - }}, + }, + }, { Code: "if (('' && {}) || (0 && void 0)) { }", Options: StrictBooleanExpressionsOptions{ @@ -911,12 +913,14 @@ func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { Code: "null || {};", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullish", Line: 1}, - }}, + }, + }, { Code: "undefined && [];", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullish", Line: 1}, - }}, + }, + }, { Code: ` declare const x: null; @@ -925,37 +929,44 @@ func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullish", Line: 3}, - }}, + }, + }, { Code: "(x: undefined) => !x;", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullish", Line: 1}, - }}, + }, + }, { Code: "(x: T) => (x ? 1 : 0);", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullish", Line: 1}, - }}, + }, + }, { Code: "(x: T) => (x ? 1 : 0);", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullish", Line: 1}, - }}, + }, + }, { Code: "(x: T) => (x ? 1 : 0);", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullish", Line: 1}, - }}, + }, + }, { Code: "[] || 1;", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedObject", Line: 1}, - }}, + }, + }, { Code: "({}) && 'a';", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedObject", Line: 1}, - }}, + }, + }, { Code: ` declare const x: symbol; @@ -964,32 +975,38 @@ func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedObject", Line: 3}, - }}, + }, + }, { Code: "(x: () => void) => !x;", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedObject", Line: 1}, - }}, + }, + }, { Code: "(x: T) => (x ? 1 : 0);", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedObject", Line: 1}, - }}, + }, + }, { Code: "(x: T) => (x ? 1 : 0);", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedObject", Line: 1}, - }}, + }, + }, { Code: "(x: T) => (x ? 1 : 0);", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedObject", Line: 1}, - }}, + }, + }, { Code: " void>(x: T) => (x ? 1 : 0);", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedObject", Line: 1}, - }}, + }, + }, { Code: "while ('') {}", Options: StrictBooleanExpressionsOptions{ @@ -1140,7 +1157,8 @@ func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedMixedCondition", Line: 3}, - }}, + }, + }, { Code: "(x: bigint | string) => !x;", Options: StrictBooleanExpressionsOptions{ @@ -1148,7 +1166,8 @@ func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedMixedCondition", Line: 1}, - }}, + }, + }, { Code: "(x: T) => (x ? 1 : 0);", Options: StrictBooleanExpressionsOptions{ @@ -1156,7 +1175,8 @@ func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedMixedCondition", Line: 1}, - }}, + }, + }, { Code: ` declare const x: boolean | null; @@ -1448,12 +1468,14 @@ func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedAny", Line: 2}, - } /* Suggestions: conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCastBoolean */ + }, { Code: "x => !x;", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedAny", Line: 1}, - } /* Suggestions: conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCastBoolean */ + }, { Code: "(x: T) => (x ? 1 : 0);", Errors: []rule_tester.InvalidTestCaseError{ @@ -1470,9 +1492,12 @@ func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { if (x) { } `, + TSConfig: "tsconfig.unstrict.json", Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "noStrictNullCheck", Line: 0}, {MessageId: "unexpectedObject", Line: 3}, - }}, + {MessageId: "noStrictNullCheck", Line: 0}, + {MessageId: "unexpectedObject", Line: 3}, + }, + }, { Code: ` declare const obj: { x: number } | null; @@ -1666,7 +1691,8 @@ func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableBoolean", Line: 8}, - }}, + }, + }, { Code: ` [1, null].every(async x => { @@ -1675,7 +1701,8 @@ func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "predicateCannotBeAsync", Line: 2}, - }}, + }, + }, { Code: ` const predicate = async x => { @@ -1686,7 +1713,8 @@ func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedObject", Line: 6}, - }}, + }, + }, { Code: ` [1, null].every((x): boolean | number => { @@ -1695,7 +1723,8 @@ func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedMixedCondition", Line: 2}, - }}, + }, + }, { Code: ` [1, null].every((x): boolean | undefined => { @@ -1704,7 +1733,8 @@ func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableBoolean", Line: 2}, - }}, + }, + }, { Code: ` [1, null].every((x, i) => {}); @@ -1742,7 +1772,8 @@ func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedMixedCondition", Line: 5}, - }}, + }, + }, { Code: ` declare function f(x: number): string; @@ -1753,7 +1784,8 @@ func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedMixedCondition", Line: 6}, - }}, + }, + }, { Code: ` declare function foo(x: number): T; @@ -1761,7 +1793,8 @@ func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedAny", Line: 3}, - }}, + }, + }, { Code: ` function foo(x: number): T {} @@ -1772,7 +1805,8 @@ func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNumber", Line: 3}, - }}, + }, + }, { Code: ` declare const nullOrString: string | null; From afb297bcc6420560f2b5798c192e77dff73717b0 Mon Sep 17 00:00:00 2001 From: Noel Kim Date: Tue, 30 Sep 2025 11:09:53 +0900 Subject: [PATCH 17/27] function and enum --- .../strict_boolean_expressions.go | 49 +++++++++++-------- .../strict_boolean_expressions_single_test.go | 23 +++------ .../strict_boolean_expressions_test.go | 2 +- 3 files changed, 36 insertions(+), 38 deletions(-) diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go index ee586e15..9f461281 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go @@ -48,6 +48,13 @@ func buildUnexpectedNullableNumber() rule.RuleMessage { } } +func buildUnexpectedNullableEnum() rule.RuleMessage { + return rule.RuleMessage{ + Id: "unexpectedNullableEnum", + Description: "Unexpected nullable enum value in conditional. Please handle the nullish case explicitly.", + } +} + func buildUnexpectedNullableObject() rule.RuleMessage { return rule.RuleMessage{ Id: "unexpectedNullableObject", @@ -191,26 +198,28 @@ var StrictBooleanExpressionsRule = rule.Rule{ if utils.IsArrayMethodCallWithPredicate(ctx.TypeChecker, callExpr) { if callExpr.Arguments != nil && len(callExpr.Arguments.Nodes) > 0 { arg := callExpr.Arguments.Nodes[0] - if arg != nil && (arg.Kind == ast.KindArrowFunction || arg.Kind == ast.KindFunctionExpression || arg.Kind == ast.KindFunctionDeclaration) { - if checker.GetFunctionFlags(arg)&checker.FunctionFlagsAsync != 0 { - ctx.ReportNode(arg, buildPredicateCannotBeAsync()) - return - } - funcType := ctx.TypeChecker.GetTypeAtLocation(arg) - signatures := ctx.TypeChecker.GetCallSignatures(funcType) - for _, signature := range signatures { - returnType := ctx.TypeChecker.GetReturnTypeOfSignature(signature) - typeFlags := checker.Type_flags(returnType) - if typeFlags&checker.TypeFlagsTypeParameter != 0 { - constraint := ctx.TypeChecker.GetConstraintOfTypeParameter(returnType) - if constraint != nil { - returnType = constraint - } + if arg == nil { + return + } + isFunction := arg.Kind&(ast.KindArrowFunction|ast.KindFunctionExpression|ast.KindFunctionDeclaration) != 0 + if isFunction && checker.GetFunctionFlags(arg)&checker.FunctionFlagsAsync != 0 { + ctx.ReportNode(arg, buildPredicateCannotBeAsync()) + return + } + funcType := ctx.TypeChecker.GetTypeAtLocation(arg) + signatures := ctx.TypeChecker.GetCallSignatures(funcType) + for _, signature := range signatures { + returnType := ctx.TypeChecker.GetReturnTypeOfSignature(signature) + typeFlags := checker.Type_flags(returnType) + if typeFlags&checker.TypeFlagsTypeParameter != 0 { + constraint := ctx.TypeChecker.GetConstraintOfTypeParameter(returnType) + if constraint != nil { + returnType = constraint } + } - if returnType != nil && !isBooleanType(returnType) { - checkCondition(ctx, node, returnType, opts) - } + if returnType != nil && !isBooleanType(returnType) { + checkCondition(ctx, node, returnType, opts) } } } @@ -524,7 +533,7 @@ func checkCondition(ctx rule.RuleContext, node *ast.Node, t *checker.Type, opts if info.isNullable { if info.isEnum { if !*opts.AllowNullableEnum { - ctx.ReportNode(node, buildUnexpectedNullableString()) + ctx.ReportNode(node, buildUnexpectedNullableEnum()) } } else { if !*opts.AllowNullableString { @@ -543,7 +552,7 @@ func checkCondition(ctx rule.RuleContext, node *ast.Node, t *checker.Type, opts if info.isNullable { if info.isEnum { if !*opts.AllowNullableEnum { - ctx.ReportNode(node, buildUnexpectedNullableNumber()) + ctx.ReportNode(node, buildUnexpectedNullableEnum()) } } else { if !*opts.AllowNullableNumber { diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go index 0f31b38c..119c3268 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go @@ -12,29 +12,18 @@ import ( func TestStrictBooleanExpressionsSingleRule(t *testing.T) { rule_tester.RunRuleTester(fixtures.GetRootDir(), "tsconfig.json", t, &StrictBooleanExpressionsRule, []rule_tester.ValidTestCase{ + + }, []rule_tester.InvalidTestCase{ { Code: ` - declare const x: string[] | null; - // oxlint-disable-next-line - if (x) { - } + function foo(x: number): T {} + [1, null].every(foo); `, - TSConfig: "tsconfig.unstrict.json", Options: StrictBooleanExpressionsOptions{ - AllowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: utils.Ref(true), + AllowNumber: utils.Ref(false), }, - }, - }, []rule_tester.InvalidTestCase{ - { - Code: ` - declare const x: string[] | null; - if (x) { - } - `, - TSConfig: "tsconfig.unstrict.json", Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "noStrictNullCheck", Line: 0}, - {MessageId: "unexpectedObject", Line: 3}, + {MessageId: "unexpectedNumber", Line: 3}, }, }, }) diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go index 9c58362c..e5f270ee 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go @@ -8,7 +8,7 @@ import ( "github.com/typescript-eslint/tsgolint/internal/utils" ) -func TestStrictBooleanExpressionsRule_Generated(t *testing.T) { +func TestStrictBooleanExpressionsRule(t *testing.T) { rule_tester.RunRuleTester( fixtures.GetRootDir(), "tsconfig.json", From c77e99b44bb910d9fdb1b739fd26b343336969ec Mon Sep 17 00:00:00 2001 From: Noel Kim Date: Tue, 30 Sep 2025 11:18:17 +0900 Subject: [PATCH 18/27] enum more --- .../strict_boolean_expressions.go | 2 +- .../strict_boolean_expressions_single_test.go | 13 +- .../strict_boolean_expressions_test.go | 252 ++++++++++++------ 3 files changed, 177 insertions(+), 90 deletions(-) diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go index 9f461281..170b66ee 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go @@ -580,7 +580,7 @@ func checkCondition(ctx rule.RuleContext, node *ast.Node, t *checker.Type, opts case typeVariantMixed: if info.isEnum { if info.isNullable && !*opts.AllowNullableEnum { - ctx.ReportNode(node, buildUnexpectedNullableNumber()) + ctx.ReportNode(node, buildUnexpectedNullableEnum()) } return } diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go index 119c3268..7403d3f0 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go @@ -16,15 +16,18 @@ func TestStrictBooleanExpressionsSingleRule(t *testing.T) { }, []rule_tester.InvalidTestCase{ { Code: ` - function foo(x: number): T {} - [1, null].every(foo); + enum ExampleEnum { + This = 0, + That = 'one', + } + (value?: ExampleEnum) => (value ? 1 : 0); `, Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), + AllowNullableEnum: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 3}, - }, + {MessageId: "unexpectedNullableEnum", Line: 6}, + }, /* Suggestions: conditionFixCompareNullish */ }, }) } diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go index e5f270ee..4a830753 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go @@ -786,7 +786,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNumber", Line: 2}, - } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */ + }, { Code: "while (false || 'a' + 'b') {}", Options: StrictBooleanExpressionsOptions{ @@ -794,7 +795,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedString", Line: 1}, - } /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ + }, { Code: "(x: object) => (true || false || x ? true : false);", Options: StrictBooleanExpressionsOptions{ @@ -811,7 +813,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedString", Line: 1}, {MessageId: "unexpectedObject", Line: 1}, {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedNullish", Line: 1}, - } /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */ + }, { Code: ` declare const array: string[]; @@ -822,7 +825,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedString"}, - } /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, explicitBooleanReturnType */}, + }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, explicitBooleanReturnType */ + }, { Code: ` declare const foo: true & { __BRAND: 'Foo' }; @@ -833,7 +837,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedString", Line: 3}, {MessageId: "unexpectedNumber", Line: 3}, {MessageId: "unexpectedNullish", Line: 3}, - } /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */ + }, { Code: ` declare const foo: false & { __BRAND: 'Foo' }; @@ -844,7 +849,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedString", Line: 3}, {MessageId: "unexpectedObject", Line: 3}, {MessageId: "unexpectedNullish", Line: 3}, - } /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ + }, { Code: "'asd' && 123 && [] && null;", Options: StrictBooleanExpressionsOptions{ @@ -852,7 +858,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedString", Line: 1}, {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedObject", Line: 1}, - } /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */ + }, { Code: "'asd' || 123 || [] || null;", Options: StrictBooleanExpressionsOptions{ @@ -860,7 +867,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedString", Line: 1}, {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedObject", Line: 1}, - } /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */ + }, { Code: "let x = (1 && 'a' && null) || 0 || '' || {};", Options: StrictBooleanExpressionsOptions{ @@ -868,7 +876,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedString", Line: 1}, {MessageId: "unexpectedNullish", Line: 1}, {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedString", Line: 1}, - } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ + }, { Code: "return (1 || 'a' || null) && 0 && '' && {};", Options: StrictBooleanExpressionsOptions{ @@ -876,7 +885,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedString", Line: 1}, {MessageId: "unexpectedNullish", Line: 1}, {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedString", Line: 1}, - } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ + }, { Code: "console.log((1 && []) || ('a' && {}));", Options: StrictBooleanExpressionsOptions{ @@ -884,7 +894,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedObject", Line: 1}, {MessageId: "unexpectedString", Line: 1}, - } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ + }, { Code: "if ((1 && []) || ('a' && {})) void 0;", Options: StrictBooleanExpressionsOptions{ @@ -892,7 +903,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedObject", Line: 1}, {MessageId: "unexpectedString", Line: 1}, {MessageId: "unexpectedObject", Line: 1}, - } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ + }, { Code: "let x = null || 0 || 'a' || [] ? {} : undefined;", Options: StrictBooleanExpressionsOptions{ @@ -900,7 +912,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullish", Line: 1}, {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedString", Line: 1}, {MessageId: "unexpectedObject", Line: 1}, - } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ + }, { Code: "return !(null || 0 || 'a' || []);", Options: StrictBooleanExpressionsOptions{ @@ -908,7 +921,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullish", Line: 1}, {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedString", Line: 1}, {MessageId: "unexpectedObject", Line: 1}, - } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ + }, { Code: "null || {};", Errors: []rule_tester.InvalidTestCaseError{ @@ -1014,7 +1028,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedString", Line: 1}, - } /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ + }, { Code: "for (; 'foo'; ) {}", Options: StrictBooleanExpressionsOptions{ @@ -1022,7 +1037,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedString", Line: 1}, - } /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ + }, { Code: ` declare const x: string; @@ -1034,7 +1050,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedString", Line: 3}, - } /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ + }, { Code: "(x: string) => !x;", Options: StrictBooleanExpressionsOptions{ @@ -1042,7 +1059,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedString", Line: 1}, - } /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ + }, { Code: "(x: T) => (x ? 1 : 0);", Options: StrictBooleanExpressionsOptions{ @@ -1050,7 +1068,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedString", Line: 1}, - } /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ + }, { Code: "while (0n) {}", Options: StrictBooleanExpressionsOptions{ @@ -1058,7 +1077,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNumber", Line: 1}, - } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */ + }, { Code: "for (; 123; ) {}", Options: StrictBooleanExpressionsOptions{ @@ -1066,7 +1086,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNumber", Line: 1}, - } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */ + }, { Code: ` declare const x: number; @@ -1078,7 +1099,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNumber", Line: 3}, - } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */ + }, { Code: "(x: bigint) => !x;", Options: StrictBooleanExpressionsOptions{ @@ -1086,7 +1108,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNumber", Line: 1}, - } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */ + }, { Code: "(x: T) => (x ? 1 : 0);", Options: StrictBooleanExpressionsOptions{ @@ -1094,7 +1117,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNumber", Line: 1}, - } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */ + }, { Code: "![]['length']; // doesn't count as array.length when computed", Options: StrictBooleanExpressionsOptions{ @@ -1102,7 +1126,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNumber", Line: 1}, - } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */ + }, { Code: ` declare const a: any[] & { notLength: number }; @@ -1114,7 +1139,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNumber", Line: 3}, - } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */ + }, { Code: ` if (![].length) { @@ -1125,7 +1151,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNumber", Line: 2}, - } /* Suggestions: conditionFixCompareArrayLengthZero */}, + }, /* Suggestions: conditionFixCompareArrayLengthZero */ + }, { Code: ` (a: number[]) => a.length && '...'; @@ -1135,7 +1162,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNumber", Line: 2}, - } /* Suggestions: conditionFixCompareArrayLengthNonzero */}, + }, /* Suggestions: conditionFixCompareArrayLengthNonzero */ + }, { Code: ` (...a: T) => a.length || 'empty'; @@ -1145,7 +1173,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNumber", Line: 2}, - } /* Suggestions: conditionFixCompareArrayLengthNonzero */}, + }, /* Suggestions: conditionFixCompareArrayLengthNonzero */ + }, { Code: ` declare const x: string | number; @@ -1188,7 +1217,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableBoolean", Line: 3}, - } /* Suggestions: conditionFixDefaultFalse, conditionFixCompareTrue */}, + }, /* Suggestions: conditionFixDefaultFalse, conditionFixCompareTrue */ + }, { Code: "(x?: boolean) => !x;", Options: StrictBooleanExpressionsOptions{ @@ -1196,7 +1226,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableBoolean", Line: 1}, - } /* Suggestions: conditionFixDefaultFalse, conditionFixCompareFalse */}, + }, /* Suggestions: conditionFixDefaultFalse, conditionFixCompareFalse */ + }, { Code: "(x: T) => (x ? 1 : 0);", Options: StrictBooleanExpressionsOptions{ @@ -1204,7 +1235,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableBoolean", Line: 1}, - } /* Suggestions: conditionFixDefaultFalse, conditionFixCompareTrue */}, + }, /* Suggestions: conditionFixDefaultFalse, conditionFixCompareTrue */ + }, { Code: ` declare const x: object | null; @@ -1216,7 +1248,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableObject", Line: 3}, - } /* Suggestions: conditionFixCompareNullish */}, + }, /* Suggestions: conditionFixCompareNullish */ + }, { Code: "(x?: { a: number }) => !x;", Options: StrictBooleanExpressionsOptions{ @@ -1224,7 +1257,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableObject", Line: 1}, - } /* Suggestions: conditionFixCompareNullish */}, + }, /* Suggestions: conditionFixCompareNullish */ + }, { Code: "(x: T) => (x ? 1 : 0);", Options: StrictBooleanExpressionsOptions{ @@ -1232,7 +1266,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableObject", Line: 1}, - } /* Suggestions: conditionFixCompareNullish */}, + }, /* Suggestions: conditionFixCompareNullish */ + }, { Code: ` declare const x: string | null; @@ -1241,17 +1276,20 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableString", Line: 3}, - } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ + }, { Code: "(x?: string) => !x;", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableString", Line: 1}, - } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ + }, { Code: "(x: T) => (x ? 1 : 0);", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableString", Line: 1}, - } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ + }, { Code: ` function foo(x: '' | 'bar' | null) { @@ -1261,7 +1299,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableString", Line: 3}, - } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ + }, { Code: ` declare const x: number | null; @@ -1270,17 +1309,20 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableNumber", Line: 3}, - } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultZero, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultZero, conditionFixCastBoolean */ + }, { Code: "(x?: number) => !x;", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableNumber", Line: 1}, - } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultZero, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultZero, conditionFixCastBoolean */ + }, { Code: "(x: T) => (x ? 1 : 0);", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableNumber", Line: 1}, - } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultZero, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultZero, conditionFixCastBoolean */ + }, { Code: ` function foo(x: 0 | 1 | null) { @@ -1324,7 +1366,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableEnum", Line: 7}, - } /* Suggestions: conditionFixCompareNullish */}, + }, /* Suggestions: conditionFixCompareNullish */ + }, { Code: ` enum ExampleEnum { @@ -1340,7 +1383,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableEnum", Line: 7}, - } /* Suggestions: conditionFixCompareNullish */}, + }, /* Suggestions: conditionFixCompareNullish */ + }, { Code: ` enum ExampleEnum { @@ -1356,7 +1400,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableEnum", Line: 7}, - } /* Suggestions: conditionFixCompareNullish */}, + }, /* Suggestions: conditionFixCompareNullish */ + }, { Code: ` enum ExampleEnum { @@ -1372,7 +1417,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableEnum", Line: 7}, - } /* Suggestions: conditionFixCompareNullish */}, + }, /* Suggestions: conditionFixCompareNullish */ + }, { Code: ` enum ExampleEnum { @@ -1388,7 +1434,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableEnum", Line: 7}, - } /* Suggestions: conditionFixCompareNullish */}, + }, /* Suggestions: conditionFixCompareNullish */ + }, { Code: ` enum ExampleEnum { @@ -1404,7 +1451,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableEnum", Line: 7}, - } /* Suggestions: conditionFixCompareNullish */}, + }, /* Suggestions: conditionFixCompareNullish */ + }, { Code: ` enum ExampleEnum { @@ -1418,7 +1466,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableEnum", Line: 6}, - } /* Suggestions: conditionFixCompareNullish */}, + }, /* Suggestions: conditionFixCompareNullish */ + }, { Code: ` enum ExampleEnum { @@ -1432,7 +1481,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableEnum", Line: 6}, - } /* Suggestions: conditionFixCompareNullish */}, + }, /* Suggestions: conditionFixCompareNullish */ + }, { Code: ` enum ExampleEnum { @@ -1446,7 +1496,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableEnum", Line: 6}, - } /* Suggestions: conditionFixCompareNullish */}, + }, /* Suggestions: conditionFixCompareNullish */ + }, { Code: ` enum ExampleEnum { @@ -1460,7 +1511,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableEnum", Line: 6}, - } /* Suggestions: conditionFixCompareNullish */}, + }, /* Suggestions: conditionFixCompareNullish */ + }, { Code: ` if (x) { @@ -1480,12 +1532,14 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { Code: "(x: T) => (x ? 1 : 0);", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedAny", Line: 1}, - } /* Suggestions: conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCastBoolean */ + }, { Code: "(x: T) => (x ? 1 : 0);", Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedAny", Line: 1}, - } /* Suggestions: conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCastBoolean */ + }, { Code: ` declare const x: string[] | null; @@ -1511,7 +1565,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableObject", Line: 3}, {MessageId: "unexpectedNullableObject", Line: 4}, {MessageId: "unexpectedNullableObject", Line: 5}, {MessageId: "unexpectedNullableObject", Line: 6}, - } /* Suggestions: conditionFixCompareNullish, conditionFixCompareNullish, conditionFixCompareNullish, conditionFixCompareNullish */}, + }, /* Suggestions: conditionFixCompareNullish, conditionFixCompareNullish, conditionFixCompareNullish, conditionFixCompareNullish */ + }, { Code: ` declare function assert(x: unknown): asserts x; @@ -1520,7 +1575,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableString", Line: 4}, - } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ + }, { Code: ` declare function assert(a: number, b: unknown): asserts b; @@ -1529,7 +1585,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableString", Line: 4}, - } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ + }, { Code: ` declare function assert(a: number, b: unknown): asserts b; @@ -1539,7 +1596,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableString", Line: 5}, - } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ + }, { Code: ` declare function assert(this: object, a: number, b: unknown): asserts b; @@ -1548,7 +1606,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableString", Line: 4}, - } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ + }, { Code: ` function asserts1(x: string | number | undefined): asserts x {} @@ -1563,7 +1622,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableString"}, - } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ + }, { Code: ` function assert(this: object, a: number, b: unknown): asserts b; @@ -1586,7 +1646,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableString", Line: 18}, - } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ + }, { Code: ` function assert(this: object, a: number, b: unknown): asserts b; @@ -1610,7 +1671,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableString", Line: 19}, - } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ + }, { Code: ` declare function assert(a: boolean, b: unknown): asserts b; @@ -1621,7 +1683,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableString", Line: 6}, - } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ + }, { Code: ` function assert(one: unknown): asserts one; @@ -1634,7 +1697,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableString", Line: 8}, - } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ + }, { Code: ` ['one', 'two', ''].find(x => { @@ -1646,7 +1710,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedString", Line: 2}, - } /* Suggestions: explicitBooleanReturnType */}, + }, /* Suggestions: explicitBooleanReturnType */ + }, { Code: ` ['one', 'two', ''].find(x => { @@ -1655,7 +1720,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullish", Line: 2}, - } /* Suggestions: explicitBooleanReturnType */}, + }, /* Suggestions: explicitBooleanReturnType */ + }, { Code: ` ['one', 'two', ''].findLast(x => { @@ -1664,7 +1730,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullish", Line: 2}, - } /* Suggestions: explicitBooleanReturnType */}, + }, /* Suggestions: explicitBooleanReturnType */ + }, { Code: ` ['one', 'two', ''].find(x => { @@ -1675,7 +1742,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableBoolean", Line: 2}, - } /* Suggestions: explicitBooleanReturnType */}, + }, /* Suggestions: explicitBooleanReturnType */ + }, { Code: ` const predicate = (x: string) => { @@ -1741,28 +1809,32 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullish", Line: 2}, - } /* Suggestions: explicitBooleanReturnType */}, + }, /* Suggestions: explicitBooleanReturnType */ + }, { Code: ` [() => {}, null].every((x: () => void) => {}); `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullish", Line: 2}, - } /* Suggestions: explicitBooleanReturnType */}, + }, /* Suggestions: explicitBooleanReturnType */ + }, { Code: ` [() => {}, null].every(function (x: () => void) {}); `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullish", Line: 2}, - } /* Suggestions: explicitBooleanReturnType */}, + }, /* Suggestions: explicitBooleanReturnType */ + }, { Code: ` [() => {}, null].every(() => {}); `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullish", Line: 2}, - } /* Suggestions: explicitBooleanReturnType */}, + }, /* Suggestions: explicitBooleanReturnType */ + }, { Code: ` declare function f(x: number): string; @@ -1814,7 +1886,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableString", Line: 3}, - } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean, explicitBooleanReturnType */}, + }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean, explicitBooleanReturnType */ + }, { Code: ` declare const nullOrString: string | null; @@ -1822,7 +1895,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableString", Line: 3}, - } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */}, + }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ + }, { Code: ` declare const anyValue: any; @@ -1830,7 +1904,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedAny", Line: 3}, - } /* Suggestions: conditionFixCastBoolean, explicitBooleanReturnType */}, + }, /* Suggestions: conditionFixCastBoolean, explicitBooleanReturnType */ + }, { Code: ` declare const nullOrBoolean: boolean | null; @@ -1838,7 +1913,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableBoolean", Line: 3}, - } /* Suggestions: conditionFixDefaultFalse, conditionFixCompareTrue, explicitBooleanReturnType */}, + }, /* Suggestions: conditionFixDefaultFalse, conditionFixCompareTrue, explicitBooleanReturnType */ + }, { Code: ` enum ExampleEnum { @@ -1850,7 +1926,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableEnum", Line: 7}, - } /* Suggestions: conditionFixCompareNullish, explicitBooleanReturnType */}, + }, /* Suggestions: conditionFixCompareNullish, explicitBooleanReturnType */ + }, { Code: ` declare const nullOrNumber: number | null; @@ -1858,7 +1935,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableNumber", Line: 3}, - } /* Suggestions: conditionFixCompareNullish, conditionFixDefaultZero, conditionFixCastBoolean, explicitBooleanReturnType */}, + }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultZero, conditionFixCastBoolean, explicitBooleanReturnType */ + }, { Code: ` const objectValue: object = {}; @@ -1866,7 +1944,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedObject", Line: 3}, - } /* Suggestions: explicitBooleanReturnType */}, + }, /* Suggestions: explicitBooleanReturnType */ + }, { Code: ` const objectValue: object = {}; @@ -1876,7 +1955,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { `, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedObject", Line: 3}, - } /* Suggestions: explicitBooleanReturnType */}, + }, /* Suggestions: explicitBooleanReturnType */ + }, { Code: ` declare const nullOrObject: object | null; @@ -1887,7 +1967,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNullableObject", Line: 3}, - } /* Suggestions: conditionFixCompareNullish, explicitBooleanReturnType */}, + }, /* Suggestions: conditionFixCompareNullish, explicitBooleanReturnType */ + }, { Code: ` const numbers: number[] = [1]; @@ -1898,7 +1979,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNumber", Line: 3}, - } /* Suggestions: conditionFixCompareArrayLengthNonzero, explicitBooleanReturnType */}, + }, /* Suggestions: conditionFixCompareArrayLengthNonzero, explicitBooleanReturnType */ + }, { Code: ` const numberValue: number = 1; @@ -1909,7 +1991,8 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedNumber", Line: 3}, - } /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, explicitBooleanReturnType */}, + }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, explicitBooleanReturnType */ + }, { Code: ` const stringValue: string = 'hoge'; @@ -1920,6 +2003,7 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "unexpectedString", Line: 3}, - } /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, explicitBooleanReturnType */}, + }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, explicitBooleanReturnType */ + }, }) } From caee616eb5c6024805d731297b54cf50f7faa4c8 Mon Sep 17 00:00:00 2001 From: Noel Kim Date: Tue, 30 Sep 2025 11:25:41 +0900 Subject: [PATCH 19/27] remove print --- .../strict_boolean_expressions.go | 1 - .../strict_boolean_expressions_single_test.go | 23 ++++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go index 170b66ee..1521746a 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go @@ -468,7 +468,6 @@ func analyzeTypePart(typeChecker *checker.Checker, t *checker.Type) typeInfo { if t.IsStringLiteral() { literal := t.AsLiteralType() if literal != nil && literal.Value() != "" { - println(literal.Value()) info.isTruthy = true } } diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go index 7403d3f0..50c29d31 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go @@ -7,7 +7,7 @@ import ( "github.com/typescript-eslint/tsgolint/internal/rule_tester" "github.com/typescript-eslint/tsgolint/internal/rules/fixtures" - "github.com/typescript-eslint/tsgolint/internal/utils" + //"github.com/typescript-eslint/tsgolint/internal/utils" ) func TestStrictBooleanExpressionsSingleRule(t *testing.T) { @@ -16,18 +16,19 @@ func TestStrictBooleanExpressionsSingleRule(t *testing.T) { }, []rule_tester.InvalidTestCase{ { Code: ` - enum ExampleEnum { - This = 0, - That = 'one', - } - (value?: ExampleEnum) => (value ? 1 : 0); + function asserts1(x: string | number | undefined): asserts x {} + function asserts2(x: string | number | undefined): asserts x {} + + const maybeString = Math.random() ? 'string'.slice() : undefined; + + const someAssert: typeof asserts1 | typeof asserts2 = + Math.random() > 0.5 ? asserts1 : asserts2; + + someAssert(maybeString); `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(false), - }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableEnum", Line: 6}, - }, /* Suggestions: conditionFixCompareNullish */ + {MessageId: "unexpectedNullableString"}, + }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ }, }) } From 972b262d3094996008119c4ab7525cf675a01c5f Mon Sep 17 00:00:00 2001 From: Noel Kim Date: Tue, 30 Sep 2025 12:03:07 +0900 Subject: [PATCH 20/27] function overload --- .../strict_boolean_expressions.go | 94 +++++++------------ .../strict_boolean_expressions_single_test.go | 34 ------- .../strict_boolean_expressions_test.go | 6 ++ 3 files changed, 41 insertions(+), 93 deletions(-) delete mode 100644 internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go index 1521746a..14b4f68d 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go @@ -208,6 +208,7 @@ var StrictBooleanExpressionsRule = rule.Rule{ } funcType := ctx.TypeChecker.GetTypeAtLocation(arg) signatures := ctx.TypeChecker.GetCallSignatures(funcType) + var types []*checker.Type for _, signature := range signatures { returnType := ctx.TypeChecker.GetReturnTypeOfSignature(signature) typeFlags := checker.Type_flags(returnType) @@ -218,10 +219,9 @@ var StrictBooleanExpressionsRule = rule.Rule{ } } - if returnType != nil && !isBooleanType(returnType) { - checkCondition(ctx, node, returnType, opts) - } + types = append(types, utils.UnionTypeParts(returnType)...) } + checkCondition(ctx, node, types, opts) } } }, @@ -268,7 +268,7 @@ func findTruthinessAssertedArgument(typeChecker *checker.Checker, callExpr *ast. func checkNode(ctx rule.RuleContext, node *ast.Node, opts StrictBooleanExpressionsOptions) { nodeType := utils.GetConstrainedTypeAtLocation(ctx.TypeChecker, node) - checkCondition(ctx, node, nodeType, opts) + checkCondition(ctx, node, utils.UnionTypeParts(nodeType), opts) } func traverseLogicalExpression(ctx rule.RuleContext, binExpr *ast.BinaryExpression, opts StrictBooleanExpressionsOptions, isCondition bool) { @@ -329,73 +329,49 @@ type typeInfo struct { isEnum bool } -func analyzeType(typeChecker *checker.Checker, t *checker.Type) typeInfo { +func analyzeTypeParts(typeChecker *checker.Checker, types []*checker.Type) typeInfo { info := typeInfo{ - types: []*checker.Type{t}, + isUnion: len(types) > 1, + types: types, } + variants := make(map[typeVariant]bool) - if utils.IsUnionType(t) { - info.isUnion = true - parts := utils.UnionTypeParts(t) - variants := make(map[typeVariant]bool) - - metNotTruthy := false + metNotTruthy := false - for _, part := range parts { - partInfo := analyzeTypePart(typeChecker, part) - variants[partInfo.variant] = true - if partInfo.variant == typeVariantNullish { - info.isNullable = true - } - if partInfo.isEnum { - info.isEnum = true - } - if partInfo.variant == typeVariantBoolean || partInfo.variant == typeVariantNumber || partInfo.variant == typeVariantString { - if metNotTruthy { - continue - } - info.isTruthy = partInfo.isTruthy - metNotTruthy = !partInfo.isTruthy - } + for _, part := range info.types { + partInfo := analyzeTypePart(typeChecker, part) + variants[partInfo.variant] = true + if partInfo.variant == typeVariantNullish { + info.isNullable = true } - - if len(variants) == 1 { - for v := range variants { - info.variant = v - } - } else if len(variants) == 2 && info.isNullable { - for v := range variants { - if v != typeVariantNullish { - info.variant = v - break - } + if partInfo.isEnum { + info.isEnum = true + } + if partInfo.variant == typeVariantBoolean || partInfo.variant == typeVariantNumber || partInfo.variant == typeVariantString { + if metNotTruthy { + continue } - } else { - info.variant = typeVariantMixed + info.isTruthy = partInfo.isTruthy + metNotTruthy = !partInfo.isTruthy } - - return info } - if utils.IsIntersectionType(t) { - info.isIntersection = true - types := t.Types() - isBoolean := false - for _, t2 := range types { - if analyzeTypePart(typeChecker, t2).variant == typeVariantBoolean { - isBoolean = true + if len(variants) == 1 { + for v := range variants { + info.variant = v + } + } else if len(variants) == 2 && info.isNullable { + for v := range variants { + if v != typeVariantNullish { + info.variant = v break } } - if isBoolean { - info.variant = typeVariantBoolean - } else { - info.variant = typeVariantObject - } - return info + } else { + info.variant = typeVariantMixed } - return analyzeTypePart(typeChecker, t) + return info } func analyzeTypePart(typeChecker *checker.Checker, t *checker.Type) typeInfo { @@ -510,8 +486,8 @@ func analyzeTypePart(typeChecker *checker.Checker, t *checker.Type) typeInfo { return info } -func checkCondition(ctx rule.RuleContext, node *ast.Node, t *checker.Type, opts StrictBooleanExpressionsOptions) { - info := analyzeType(ctx.TypeChecker, t) +func checkCondition(ctx rule.RuleContext, node *ast.Node, types []*checker.Type, opts StrictBooleanExpressionsOptions) { + info := analyzeTypeParts(ctx.TypeChecker, types) switch info.variant { case typeVariantAny, typeVariantUnknown, typeVariantGeneric: diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go deleted file mode 100644 index 50c29d31..00000000 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_single_test.go +++ /dev/null @@ -1,34 +0,0 @@ -// Code generated from strict-boolean-expressions.test.ts - DO NOT EDIT. - -package strict_boolean_expressions - -import ( - "testing" - - "github.com/typescript-eslint/tsgolint/internal/rule_tester" - "github.com/typescript-eslint/tsgolint/internal/rules/fixtures" - //"github.com/typescript-eslint/tsgolint/internal/utils" -) - -func TestStrictBooleanExpressionsSingleRule(t *testing.T) { - rule_tester.RunRuleTester(fixtures.GetRootDir(), "tsconfig.json", t, &StrictBooleanExpressionsRule, []rule_tester.ValidTestCase{ - - }, []rule_tester.InvalidTestCase{ - { - Code: ` - function asserts1(x: string | number | undefined): asserts x {} - function asserts2(x: string | number | undefined): asserts x {} - - const maybeString = Math.random() ? 'string'.slice() : undefined; - - const someAssert: typeof asserts1 | typeof asserts2 = - Math.random() > 0.5 ? asserts1 : asserts2; - - someAssert(maybeString); - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString"}, - }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ - }, - }) -} diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go index 4a830753..9dccdb0e 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go @@ -1609,6 +1609,12 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ }, { + // This should be checkable, but the TS API doesn't currently report + // `someAssert(maybeString)` as a type predicate call, which appears to be + // a bug. + // + // See https://github.com/microsoft/TypeScript/issues/59707 + Skip: true, Code: ` function asserts1(x: string | number | undefined): asserts x {} function asserts2(x: string | number | undefined): asserts x {} From 46148f32fbaf855e3b55265d3b0341766c1039ef Mon Sep 17 00:00:00 2001 From: Noel Kim Date: Tue, 30 Sep 2025 12:07:19 +0900 Subject: [PATCH 21/27] lint --- .../strict_boolean_expressions.go | 22 ++----------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go index 14b4f68d..44638eb1 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go @@ -374,7 +374,7 @@ func analyzeTypeParts(typeChecker *checker.Checker, types []*checker.Type) typeI return info } -func analyzeTypePart(typeChecker *checker.Checker, t *checker.Type) typeInfo { +func analyzeTypePart(_typeChecker *checker.Checker, t *checker.Type) typeInfo { info := typeInfo{} flags := checker.Type_flags(t) @@ -383,7 +383,7 @@ func analyzeTypePart(typeChecker *checker.Checker, t *checker.Type) typeInfo { types := t.Types() isBoolean := false for _, t2 := range types { - if analyzeTypePart(typeChecker, t2).variant == typeVariantBoolean { + if analyzeTypePart(_typeChecker, t2).variant == typeVariantBoolean { isBoolean = true break } @@ -568,21 +568,3 @@ func checkCondition(ctx rule.RuleContext, node *ast.Node, types []*checker.Type, } } } - -func isBooleanType(t *checker.Type) bool { - flags := checker.Type_flags(t) - - if flags&(checker.TypeFlagsBoolean|checker.TypeFlagsBooleanLiteral) != 0 { - if utils.IsUnionType(t) { - for _, part := range utils.UnionTypeParts(t) { - partFlags := checker.Type_flags(part) - if partFlags&(checker.TypeFlagsNull|checker.TypeFlagsUndefined|checker.TypeFlagsVoid) != 0 { - return false - } - } - } - return true - } - - return false -} From 36624cc56d13df8636836c1ec2e3135c7c5eb268 Mon Sep 17 00:00:00 2001 From: Noel Kim Date: Tue, 30 Sep 2025 12:08:37 +0900 Subject: [PATCH 22/27] indent --- .../strict_boolean_expressions_test.go | 57 +++++++++++++++---- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go index 9dccdb0e..d83335cf 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go @@ -812,7 +812,10 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, {MessageId: "unexpectedObject", Line: 1}, {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedNullish", Line: 1}, + {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedObject", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedNullish", Line: 1}, }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */ }, { @@ -836,7 +839,9 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 3}, {MessageId: "unexpectedNumber", Line: 3}, {MessageId: "unexpectedNullish", Line: 3}, + {MessageId: "unexpectedString", Line: 3}, + {MessageId: "unexpectedNumber", Line: 3}, + {MessageId: "unexpectedNullish", Line: 3}, }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */ }, { @@ -848,7 +853,9 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 3}, {MessageId: "unexpectedObject", Line: 3}, {MessageId: "unexpectedNullish", Line: 3}, + {MessageId: "unexpectedString", Line: 3}, + {MessageId: "unexpectedObject", Line: 3}, + {MessageId: "unexpectedNullish", Line: 3}, }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ }, { @@ -857,7 +864,9 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedObject", Line: 1}, + {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedObject", Line: 1}, }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */ }, { @@ -866,7 +875,9 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedObject", Line: 1}, + {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedObject", Line: 1}, }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */ }, { @@ -875,7 +886,11 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedString", Line: 1}, {MessageId: "unexpectedNullish", Line: 1}, {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedNullish", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedString", Line: 1}, }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ }, { @@ -884,7 +899,11 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedString", Line: 1}, {MessageId: "unexpectedNullish", Line: 1}, {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedNullish", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedString", Line: 1}, }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ }, { @@ -893,7 +912,9 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedObject", Line: 1}, {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedObject", Line: 1}, + {MessageId: "unexpectedString", Line: 1}, }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ }, { @@ -902,7 +923,10 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedObject", Line: 1}, {MessageId: "unexpectedString", Line: 1}, {MessageId: "unexpectedObject", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedObject", Line: 1}, + {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedObject", Line: 1}, }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ }, { @@ -911,7 +935,10 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1}, {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedString", Line: 1}, {MessageId: "unexpectedObject", Line: 1}, + {MessageId: "unexpectedNullish", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedObject", Line: 1}, }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ }, { @@ -920,7 +947,10 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1}, {MessageId: "unexpectedNumber", Line: 1}, {MessageId: "unexpectedString", Line: 1}, {MessageId: "unexpectedObject", Line: 1}, + {MessageId: "unexpectedNullish", Line: 1}, + {MessageId: "unexpectedNumber", Line: 1}, + {MessageId: "unexpectedString", Line: 1}, + {MessageId: "unexpectedObject", Line: 1}, }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ }, { @@ -1564,7 +1594,10 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { AllowNullableObject: utils.Ref(false), }, Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableObject", Line: 3}, {MessageId: "unexpectedNullableObject", Line: 4}, {MessageId: "unexpectedNullableObject", Line: 5}, {MessageId: "unexpectedNullableObject", Line: 6}, + {MessageId: "unexpectedNullableObject", Line: 3}, + {MessageId: "unexpectedNullableObject", Line: 4}, + {MessageId: "unexpectedNullableObject", Line: 5}, + {MessageId: "unexpectedNullableObject", Line: 6}, }, /* Suggestions: conditionFixCompareNullish, conditionFixCompareNullish, conditionFixCompareNullish, conditionFixCompareNullish */ }, { From 53ca4ef40b0e3c6ce722f2e5bfd3a2ea10699274 Mon Sep 17 00:00:00 2001 From: Noel Kim Date: Tue, 30 Sep 2025 12:11:21 +0900 Subject: [PATCH 23/27] oxlint-disable-next-line --- .../strict_boolean_expressions_test.go | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go index d83335cf..a86d173f 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go @@ -472,19 +472,20 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { AllowNullableEnum: utils.Ref(true), }, }, - // TODO: oxlint-disable-next-line should not check - // { - // Code: ` - // declare const x: string[] | null; - //// oxlint-disable-next-line - //if (x) { - //} - //`, - // TSConfig: "tsconfig.unstrict.json", - // Options: StrictBooleanExpressionsOptions{ - // AllowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: utils.Ref(true), - // }, - // }, + //TODO: oxlint-disable-next-line should not check + { + Skip: true, + Code: ` + declare const x: string[] | null; + // oxlint-disable-next-line + if (x) { + } + `, + TSConfig: "tsconfig.unstrict.json", + Options: StrictBooleanExpressionsOptions{ + AllowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: utils.Ref(true), + }, + }, { Code: ` function f(arg: 'a' | null) { From e4d95fa06cf3c9555fcf0bfb585483bebc4c5ffe Mon Sep 17 00:00:00 2001 From: Cameron Clark Date: Thu, 23 Oct 2025 12:58:31 +0100 Subject: [PATCH 24/27] re-port rules --- .../strict_boolean_expressions_test.go | 5530 +++++++++++------ 1 file changed, 3693 insertions(+), 1837 deletions(-) diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go index a86d173f..f6503295 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go @@ -16,2034 +16,3890 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { &StrictBooleanExpressionsRule, []rule_tester.ValidTestCase{ { - Code: "true ? 'a' : 'b';", + Code: `true ? 'a' : 'b';`, }, { Code: ` - if (false) { - } + if (false) { + } `, }, { - Code: "while (true) {}", + Code: `while (true) {}`, }, { - Code: "for (; false; ) {}", + Code: `for (; false; ) {}`, }, { - Code: "!true;", + Code: `!true;`, }, { - Code: "false || 123;", + Code: `false || 123;`, }, { - Code: "true && 'foo';", + Code: `true && 'foo';`, }, { - Code: "!(false || true);", + Code: `!(false || true);`, }, { - Code: "true && false ? true : false;", + Code: `true && false ? true : false;`, }, { - Code: "(false && true) || false;", + Code: `(false && true) || false;`, }, { - Code: "(false && true) || [];", + Code: `(false && true) || [];`, }, { - Code: "(false && 1) || (true && 2);", + Code: `(false && 1) || (true && 2);`, }, { - Code: ` - declare const x: boolean; - if (x) { - } - `, - }, - { - Code: "(x: boolean) => !x;", - }, - { - Code: "(x: T) => (x ? 1 : 0);", - }, - { - Code: ` - declare const x: never; - if (x) { - } - `, - }, - { - Code: ` - if ('') { - } - `, - }, - { - Code: "while ('x') {}"}, - { - Code: "for (; ''; ) {}"}, - { - Code: "('' && '1') || x;"}, - { - Code: ` - declare const x: string; - if (x) { - } - `, - }, - { - Code: "(x: string) => !x;"}, - { - Code: "(x: T) => (x ? 1 : 0);"}, - { - Code: ` - if (0) { - } + Code: + ` +declare const x: boolean; +if (x) { +} `, }, - { - Code: "while (1n) {}"}, - { - Code: "for (; Infinity; ) {}"}, - { - Code: "(0 / 0 && 1 + 2) || x;"}, - { - Code: ` - declare const x: number; - if (x) { - } - `, + { + Code: `(x: boolean) => !x;`, }, - { - Code: "(x: bigint) => !x;"}, - { - Code: "(x: T) => (x ? 1 : 0);"}, - { - Code: ` - declare const x: null | object; - if (x) { - } - `, + { + Code: `(x: T) => (x ? 1 : 0);`, }, - { - Code: "(x?: { a: any }) => !x;"}, - { - Code: "(x: T) => (x ? 1 : 0);"}, - { + { Code: ` - declare const x: boolean | null; - if (x) { - } +declare const x: never; +if (x) { +} `, - Options: StrictBooleanExpressionsOptions{ - - AllowNullableBoolean: utils.Ref(true), - }, - }, - { - Code: ` - (x?: boolean) => !x; - `, - Options: StrictBooleanExpressionsOptions{ - - AllowNullableBoolean: utils.Ref(true), - }, - }, - { - Code: ` - (x: T) => (x ? 1 : 0); - `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableBoolean: utils.Ref(true), - }, }, - { + { Code: ` - const a: (undefined | boolean | null)[] = [true, undefined, null]; - a.some(x => x); +if ('') { +} `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableBoolean: utils.Ref(true), - }, }, - { - Code: ` - declare const x: string | null; - if (x) { - } - `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableString: utils.Ref(true), - }, + { + Code: `while ('x') {}`, }, - { - Code: ` - (x?: string) => !x; - `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableString: utils.Ref(true), - }, + { + Code: `for (; ''; ) {}`, }, - { - Code: ` - (x: T) => (x ? 1 : 0); - `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableString: utils.Ref(true), - }, + { + Code: `('' && '1') || x;`, }, - { + { Code: ` - declare const x: number | null; - if (x) { - } +declare const x: string; +if (x) { +} `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableNumber: utils.Ref(true), - }, - }, - { - Code: ` - (x?: number) => !x; - `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableNumber: utils.Ref(true), - }, }, - { - Code: ` - (x: T) => (x ? 1 : 0); - `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableNumber: utils.Ref(true), - }, + { + Code: `(x: string) => !x;`, }, - { - Code: ` - declare const arrayOfArrays: (null | unknown[])[]; - const isAnyNonEmptyArray1 = arrayOfArrays.some(array => array?.length); - `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableNumber: utils.Ref(true), - }, + { + Code: `(x: T) => (x ? 1 : 0);`, }, - { + { Code: ` - declare const x: any; - if (x) { - } +if (0) { +} `, - Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(true), - }, }, - { - Code: ` - x => !x; - `, - Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(true), - }, + { + Code: `while (1n) {}`, }, - { - Code: ` - (x: T) => (x ? 1 : 0); - `, - Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(true), - }, + { + Code: `for (; Infinity; ) {}`, }, - { - Code: ` - declare const arrayOfArrays: any[]; - const isAnyNonEmptyArray1 = arrayOfArrays.some(array => array); - `, - Options: StrictBooleanExpressionsOptions{ - AllowAny: utils.Ref(true), - }, + { + Code: `(0 / 0 && 1 + 2) || x;`, }, - { + { Code: ` - 1 && true && 'x' && {}; +declare const x: number; +if (x) { +} `, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(true), AllowString: utils.Ref(true), - }, }, - { - Code: ` - let x = 0 || false || '' || null; - `, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(true), AllowString: utils.Ref(true), - }, - }, - { - Code: ` - if (1 && true && 'x') void 0; - `, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(true), AllowString: utils.Ref(true), - }, + { + Code: `(x: bigint) => !x;`, }, - { - Code: ` - if (0 || false || '') void 0; - `, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(true), AllowString: utils.Ref(true), - }, + { + Code: `(x: T) => (x ? 1 : 0);`, }, - { + { Code: ` - 1 && true && 'x' ? {} : null; +declare const x: null | object; +if (x) { +} `, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(true), AllowString: utils.Ref(true), - }, }, - { - Code: ` - 0 || false || '' ? null : {}; - `, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(true), AllowString: utils.Ref(true), - }, - }, - { + { + Code: `(x?: { a: any }) => !x;`, + }, + { + Code: `(x: T) => (x ? 1 : 0);`, + }, + { + Code: ` + declare const x: boolean | null; + if (x) { + } + `, Options: StrictBooleanExpressionsOptions{AllowNullableBoolean: utils.Ref(true)}}, + { + Code: ` + (x?: boolean) => !x; + `, Options: StrictBooleanExpressionsOptions{AllowNullableBoolean: utils.Ref(true)}}, + { + Code: ` + (x: T) => (x ? 1 : 0); + `, Options: StrictBooleanExpressionsOptions{AllowNullableBoolean: utils.Ref(true)}}, + { + Code: ` + const a: (undefined | boolean | null)[] = [true, undefined, null]; + a.some(x => x); + `, Options: StrictBooleanExpressionsOptions{AllowNullableBoolean: utils.Ref(true)}}, + { + Code: ` + declare const x: string | null; + if (x) { + } + `, Options: StrictBooleanExpressionsOptions{AllowNullableString: utils.Ref(true)}}, + { + Code: ` + (x?: string) => !x; + `, Options: StrictBooleanExpressionsOptions{AllowNullableString: utils.Ref(true)}, + }, + { + Code: ` + (x: T) => (x ? 1 : 0); + `, Options: StrictBooleanExpressionsOptions{AllowNullableString: utils.Ref(true)}}, + { + Code: ` + declare const x: number | null; + if (x) { + } + `, Options: StrictBooleanExpressionsOptions{AllowNullableNumber: utils.Ref(true)}}, + { + Code: ` + (x?: number) => !x; + `, Options: StrictBooleanExpressionsOptions{AllowNullableNumber: utils.Ref(true)}}, + { + Code: ` + (x: T) => (x ? 1 : 0); + `, Options: StrictBooleanExpressionsOptions{AllowNullableNumber: utils.Ref(true)}}, + { + Code: ` + declare const arrayOfArrays: (null | unknown[])[]; + const isAnyNonEmptyArray1 = arrayOfArrays.some(array => array?.length); + `, Options: StrictBooleanExpressionsOptions{AllowNullableNumber: utils.Ref(true)}}, + { + Code: ` + declare const x: any; + if (x) { + } + `, Options: StrictBooleanExpressionsOptions{AllowAny: utils.Ref(true)}}, + { + Code: ` + x => !x; + `, Options: StrictBooleanExpressionsOptions{AllowAny: utils.Ref(true)}}, + { + Code: ` + (x: T) => (x ? 1 : 0); + `, Options: StrictBooleanExpressionsOptions{AllowAny: utils.Ref(true)}}, + { + Code: ` + declare const arrayOfArrays: any[]; + const isAnyNonEmptyArray1 = arrayOfArrays.some(array => array); + `, Options: StrictBooleanExpressionsOptions{AllowAny: utils.Ref(true)}}, + { + Code: ` + 1 && true && 'x' && {}; + `, Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(true), AllowString: utils.Ref(true)}}, + { + Code: ` + let x = 0 || false || '' || null; + `, Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(true), AllowString: utils.Ref(true)}}, + { + Code: ` + if (1 && true && 'x') void 0; + `, Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(true), AllowString: utils.Ref(true)}}, + { + Code: ` + if (0 || false || '') void 0; + `, Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(true), AllowString: utils.Ref(true)}}, + { + Code: ` + 1 && true && 'x' ? {} : null; + `, Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(true), AllowString: utils.Ref(true)}}, + { + Code: ` + 0 || false || '' ? null : {}; + `, Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(true), AllowString: utils.Ref(true)}}, + { + Code: ` + declare const arrayOfArrays: string[]; + const isAnyNonEmptyArray1 = arrayOfArrays.some(array => array); + `, Options: StrictBooleanExpressionsOptions{AllowString: utils.Ref(true)}}, + { + Code: ` + declare const arrayOfArrays: number[]; + const isAnyNonEmptyArray1 = arrayOfArrays.some(array => array); + `, Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(true)}}, + { + Code: ` + declare const arrayOfArrays: (null | object)[]; + const isAnyNonEmptyArray1 = arrayOfArrays.some(array => array); + `, Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(true)}}, + { + Code: ` + enum ExampleEnum { + This = 0, + That = 1, + } + const rand = Math.random(); + let theEnum: ExampleEnum | null = null; + if (rand < 0.3) { + theEnum = ExampleEnum.This; + } + if (theEnum) { + } + `, Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(true)}}, + { + Code: ` + enum ExampleEnum { + This = 0, + That = 1, + } + const rand = Math.random(); + let theEnum: ExampleEnum | null = null; + if (rand < 0.3) { + theEnum = ExampleEnum.This; + } + if (!theEnum) { + } + `, Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(true)}}, + { + Code: ` + enum ExampleEnum { + This = 1, + That = 2, + } + const rand = Math.random(); + let theEnum: ExampleEnum | null = null; + if (rand < 0.3) { + theEnum = ExampleEnum.This; + } + if (!theEnum) { + } + `, Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(true)}}, + { + Code: ` + enum ExampleEnum { + This = 'one', + That = 'two', + } + const rand = Math.random(); + let theEnum: ExampleEnum | null = null; + if (rand < 0.3) { + theEnum = ExampleEnum.This; + } + if (!theEnum) { + } + `, Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(true)}}, + { + Code: ` + enum ExampleEnum { + This = 0, + That = 'one', + } + (value?: ExampleEnum) => (value ? 1 : 0); + `, Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(true)}}, + { + Code: ` + enum ExampleEnum { + This = '', + That = 1, + } + (value?: ExampleEnum) => (!value ? 1 : 0); + `, Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(true)}}, + { + Code: ` + enum ExampleEnum { + This = 'this', + That = 1, + } + (value?: ExampleEnum) => (!value ? 1 : 0); + `, Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(true)}}, + { + Code: ` + enum ExampleEnum { + This = '', + That = 0, + } + (value?: ExampleEnum) => (!value ? 1 : 0); + `, Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(true)}}, + { + Code: ` + enum ExampleEnum { + This = '', + That = 0, + } + declare const arrayOfArrays: (ExampleEnum | null)[]; + const isAnyNonEmptyArray1 = arrayOfArrays.some(array => array); + `, Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(true)}}, + { + Code: ` +declare const x: string[] | null; +// eslint-disable-next-line +if (x) { +} + `, Options: StrictBooleanExpressionsOptions{AllowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: utils.Ref(true)}}, + { Code: ` - declare const arrayOfArrays: string[]; - const isAnyNonEmptyArray1 = arrayOfArrays.some(array => array); +function f(arg: 'a' | null) { + if (arg) console.log(arg); +} `, - Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(true), - }, }, - { + { Code: ` - declare const arrayOfArrays: number[]; - const isAnyNonEmptyArray1 = arrayOfArrays.some(array => array); +function f(arg: 'a' | 'b' | null) { + if (arg) console.log(arg); +} `, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(true), - }, }, - { + { Code: ` - declare const arrayOfArrays: (null | object)[]; - const isAnyNonEmptyArray1 = arrayOfArrays.some(array => array); - `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(true), - }, - }, - { +declare const x: 1 | null; +declare const y: 1; +if (x) { +} +if (y) { +} + `, Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(true)}}, + { Code: ` - enum ExampleEnum { - This = 0, - That = 1, - } - const rand = Math.random(); - let theEnum: ExampleEnum | null = null; - if (rand < 0.3) { - theEnum = ExampleEnum.This; - } - if (theEnum) { - } +function f(arg: 1 | null) { + if (arg) console.log(arg); +} `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(true), - }, }, - { + { Code: ` - enum ExampleEnum { - This = 0, - That = 1, - } - const rand = Math.random(); - let theEnum: ExampleEnum | null = null; - if (rand < 0.3) { - theEnum = ExampleEnum.This; - } - if (!theEnum) { - } +function f(arg: 1 | 2 | null) { + if (arg) console.log(arg); +} `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(true), - }, }, - { + { Code: ` - enum ExampleEnum { - This = 1, - That = 2, - } - const rand = Math.random(); - let theEnum: ExampleEnum | null = null; - if (rand < 0.3) { - theEnum = ExampleEnum.This; - } - if (!theEnum) { - } +interface Options { + readonly enableSomething?: true; +} + +function f(opts: Options): void { + if (opts.enableSomething) console.log('Do something'); +} `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(true), - }, }, - { + { Code: ` - enum ExampleEnum { - This = 'one', - That = 'two', - } - const rand = Math.random(); - let theEnum: ExampleEnum | null = null; - if (rand < 0.3) { - theEnum = ExampleEnum.This; - } - if (!theEnum) { - } +declare const x: true | null; +if (x) { +} `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(true), - }, }, - { + { Code: ` - enum ExampleEnum { - This = 0, - That = 'one', - } - (value?: ExampleEnum) => (value ? 1 : 0); - `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(true), - }, - }, - { +declare const x: 'a' | null; +declare const y: 'a'; +if (x) { +} +if (y) { +} + `, Options: StrictBooleanExpressionsOptions{AllowString: utils.Ref(true)}}, + { Code: ` - enum ExampleEnum { - This = '', - That = 1, - } - (value?: ExampleEnum) => (!value ? 1 : 0); +declare const foo: boolean & { __BRAND: 'Foo' }; +if (foo) { +} `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(true), - }, }, - { + { Code: ` - enum ExampleEnum { - This = 'this', - That = 1, - } - (value?: ExampleEnum) => (!value ? 1 : 0); +declare const foo: true & { __BRAND: 'Foo' }; +if (foo) { +} `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(true), - }, }, - { + { Code: ` - enum ExampleEnum { - This = '', - That = 0, - } - (value?: ExampleEnum) => (!value ? 1 : 0); +declare const foo: false & { __BRAND: 'Foo' }; +if (foo) { +} `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(true), - }, }, - { + { Code: ` - enum ExampleEnum { - This = '', - That = 0, - } - declare const arrayOfArrays: (ExampleEnum | null)[]; - const isAnyNonEmptyArray1 = arrayOfArrays.some(array => array); +declare function assert(a: number, b: unknown): asserts a; +declare const nullableString: string | null; +declare const boo: boolean; +assert(boo, nullableString); `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(true), - }, - }, - //TODO: oxlint-disable-next-line should not check - { - Skip: true, - Code: ` - declare const x: string[] | null; - // oxlint-disable-next-line - if (x) { - } - `, - TSConfig: "tsconfig.unstrict.json", - Options: StrictBooleanExpressionsOptions{ - AllowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: utils.Ref(true), - }, }, - { + { Code: ` - function f(arg: 'a' | null) { - if (arg) console.log(arg); - } +declare function assert(a: boolean, b: unknown): asserts b is string; +declare const nullableString: string | null; +declare const boo: boolean; +assert(boo, nullableString); `, }, - { + { Code: ` - function f(arg: 'a' | 'b' | null) { - if (arg) console.log(arg); - } +declare function assert(a: number, b: unknown): asserts b; +declare const nullableString: string | null; +declare const boo: boolean; +assert(nullableString, boo); `, }, - { + { Code: ` - declare const x: 1 | null; - declare const y: 1; - if (x) { - } - if (y) { - } +declare function assert(a: number, b: unknown): asserts b; +declare const nullableString: string | null; +declare const boo: boolean; +assert(...nullableString, nullableString); `, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(true), - }, }, - { + { Code: ` - function f(arg: 1 | null) { - if (arg) console.log(arg); - } +declare function assert( + this: object, + a: number, + b?: unknown, + c?: unknown, +): asserts c; +declare const nullableString: string | null; +declare const foo: number; +const o: { assert: typeof assert } = { + assert, +}; +o.assert(foo, nullableString); `, }, - { + { Code: ` - function f(arg: 1 | 2 | null) { - if (arg) console.log(arg); - } - `, +declare function assert(x: unknown): x is string; +declare const nullableString: string | null; +assert(nullableString); + `, }, - { + { Code: ` - interface Options { - readonly enableSomething?: true; - } +class ThisAsserter { + assertThis(this: unknown, arg2: unknown): asserts this {} +} - function f(opts: Options): void { - if (opts.enableSomething) console.log('Do something'); - } - `, +declare const lol: string | number | unknown | null; + +const thisAsserter: ThisAsserter = new ThisAsserter(); +thisAsserter.assertThis(lol); + `, }, - { - Code: ` - declare const x: true | null; - if (x) { - } - `, + { + Code: ` +function assert(this: object, a: number, b: unknown): asserts b; +function assert(a: bigint, b: unknown): asserts b; +function assert(this: object, a: string, two: string): asserts two; +function assert( + this: object, + a: string, + assertee: string, + c: bigint, + d: object, +): asserts assertee; +function assert(...args: any[]): void; + +function assert(...args: any[]) { + throw new Error('lol'); +} + +declare const nullableString: string | null; +assert(3 as any, nullableString); + `, }, - { + { Code: ` - declare const x: 'a' | null; - declare const y: 'a'; - if (x) { - } - if (y) { - } +declare const assert: any; +declare const nullableString: string | null; +assert(nullableString); `, - Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(true), - }, }, - { + { Code: ` - declare const foo: boolean & { __BRAND: 'Foo' }; - if (foo) { - } + for (let x = 0; ; x++) { + break; + } `, }, - { + { Code: ` - declare const foo: true & { __BRAND: 'Foo' }; - if (foo) { - } +[true, false].some(function (x) { + return x; +}); `, }, - { + { Code: ` - declare const foo: false & { __BRAND: 'Foo' }; - if (foo) { - } +[true, false].some(function check(x) { + return x; +}); `, }, - { + { Code: ` - declare function assert(a: number, b: unknown): asserts a; - declare const nullableString: string | null; - declare const boo: boolean; - assert(boo, nullableString); +[true, false].some(x => { + return x; +}); `, }, - { + { Code: ` - declare function assert(a: boolean, b: unknown): asserts b is string; - declare const nullableString: string | null; - declare const boo: boolean; - assert(boo, nullableString); +[1, null].filter(function (x) { + return x != null; +}); `, }, - { + { Code: ` - declare function assert(a: number, b: unknown): asserts b; - declare const nullableString: string | null; - declare const boo: boolean; - assert(nullableString, boo); +['one', 'two', ''].filter(function (x) { + return !!x; +}); `, }, - { + { Code: ` - declare function assert(a: number, b: unknown): asserts b; - declare const nullableString: string | null; - declare const boo: boolean; - assert(...nullableString, nullableString); +['one', 'two', ''].filter(function (x): boolean { + return !!x; +}); `, }, - { + { Code: ` - declare function assert( - this: object, - a: number, - b?: unknown, - c?: unknown, - ): asserts c; - declare const nullableString: string | null; - declare const foo: number; - const o: { assert: typeof assert } = { - assert, - }; - o.assert(foo, nullableString); +['one', 'two', ''].filter(function (x): boolean { + if (x) { + return true; + } +}); `, }, - { - Code: ` - declare function assert(x: unknown): x is string; - declare const nullableString: string | null; - assert(nullableString); - `, - }, - { + { Code: ` - class ThisAsserter { - assertThis(this: unknown, arg2: unknown): asserts this {} - } +['one', 'two', ''].filter(function (x): boolean { + if (x) { + return true; + } - declare const lol: string | number | unknown | null; - - const thisAsserter: ThisAsserter = new ThisAsserter(); - thisAsserter.assertThis(lol); + throw new Error('oops'); +}); `, }, - { + { Code: ` - function assert(this: object, a: number, b: unknown): asserts b; - function assert(a: bigint, b: unknown): asserts b; - function assert(this: object, a: string, two: string): asserts two; - function assert( - this: object, - a: string, - assertee: string, - c: bigint, - d: object, - ): asserts assertee; - function assert(...args: any[]): void; - - function assert(...args: any[]) { - throw new Error('lol'); - } - - declare const nullableString: string | null; - assert(3 as any, nullableString); +declare const predicate: (string) => boolean; +['one', 'two', ''].filter(predicate); `, }, - { + { Code: ` - declare const assert: any; - declare const nullableString: string | null; - assert(nullableString); +declare function notNullish(x: T): x is NonNullable; +['one', null].filter(notNullish); `, }, - { + { Code: ` - for (let x = 0; ; x++) { - break; - } +declare function predicate(x: string | null): x is string; +['one', null].filter(predicate); `, }, - { + { Code: ` - [true, false].some(function (x) { - return x; - }); +declare function predicate(x: string | null): T; +['one', null].filter(predicate); `, }, - { + { Code: ` - [true, false].some(function check(x) { - return x; - }); +declare function f(x: number): boolean; +declare function f(x: string | null): boolean; + +[35].filter(f); `, }, - { - Code: ` - [true, false].some(x => { - return x; - }); - `, + }, []rule_tester.InvalidTestCase{ + { + Code: ` +if (true && 1 + 1) { +} + `, + Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareZero", +// Output: ` +// if (true && ((1 + 1) !== 0)) { +// } +// `, +// }, +// { +// MessageId: "conditionFixCompareNaN", +// Output: ` +// if (true && (!Number.isNaN((1 + 1)))) { +// } +// `, +// }, +// { +// MessageId: "conditionFixCastBoolean", +// Output: ` +// if (true && (Boolean((1 + 1)))) { +// } +// `, +// }, +// }, + }, + }, + }, + { + Code: `while (false || 'a' + 'b') {}`, + Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `while (false || (('a' + 'b').length > 0)) {}`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `while (false || (('a' + 'b') !== "")) {}`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `while (false || (Boolean(('a' + 'b')))) {}`, + // }, + // }, + }, + }, + }, + { + Code: `(x: object) => (true || false || x ? true : false);`, + Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorObject", + }, + }, + }, + { + Code: `if (('' && {}) || (0 && void 0)) { }`, + Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `if (((''.length > 0) && {}) || (0 && void 0)) { }`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `if ((('' !== "") && {}) || (0 && void 0)) { }`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `if (((Boolean('')) && {}) || (0 && void 0)) { }`, + // }, + // }, + }, + { + MessageId: "conditionErrorObject", + }, + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `if (('' && {}) || ((0 !== 0) && void 0)) { }`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `if (('' && {}) || ((!Number.isNaN(0)) && void 0)) { }`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `if (('' && {}) || ((Boolean(0)) && void 0)) { }`, + // }, + // }, + }, + { + MessageId: "conditionErrorNullish", + }, + }, + }, + { + Code: ` + declare const array: string[]; + array.some(x => x); + `, + Options: StrictBooleanExpressionsOptions{AllowNullableBoolean: utils.Ref(true), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: ` + // declare const array: string[]; + // array.some(x => x.length > 0); + // `, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: ` + // declare const array: string[]; + // array.some(x => x !== ""); + // `, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: ` + // declare const array: string[]; + // array.some(x => Boolean(x)); + // `, + // }, + // { + // MessageId: "explicitBooleanReturnType", + // Output: ` + // declare const array: string[]; + // array.some((x): boolean => x); + // `, + // }, + // }, + }, + }, + }, + { + Code: ` +declare const foo: true & { __BRAND: 'Foo' }; +if (('' && foo) || (0 && void 0)) { } + `, + Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorString", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareStringLength", +// Output: ` +// declare const foo: true & { __BRAND: 'Foo' }; +// if (((''.length > 0) && foo) || (0 && void 0)) { } +// `, +// }, +// { +// MessageId: "conditionFixCompareEmptyString", +// Output: ` +// declare const foo: true & { __BRAND: 'Foo' }; +// if ((('' !== "") && foo) || (0 && void 0)) { } +// `, +// }, +// { +// MessageId: "conditionFixCastBoolean", +// Output: ` +// declare const foo: true & { __BRAND: 'Foo' }; +// if (((Boolean('')) && foo) || (0 && void 0)) { } +// `, +// }, +// }, + }, + { + MessageId: "conditionErrorNumber", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareZero", +// Output: ` +// declare const foo: true & { __BRAND: 'Foo' }; +// if (('' && foo) || ((0 !== 0) && void 0)) { } +// `, +// }, +// { +// MessageId: "conditionFixCompareNaN", +// Output: ` +// declare const foo: true & { __BRAND: 'Foo' }; +// if (('' && foo) || ((!Number.isNaN(0)) && void 0)) { } +// `, +// }, +// { +// MessageId: "conditionFixCastBoolean", +// Output: ` +// declare const foo: true & { __BRAND: 'Foo' }; +// if (('' && foo) || ((Boolean(0)) && void 0)) { } +// `, +// }, +// }, + }, + { + MessageId: "conditionErrorNullish", + }, + }, + }, + { + Code: ` +declare const foo: false & { __BRAND: 'Foo' }; +if (('' && {}) || (foo && void 0)) { } + `, + Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorString", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareStringLength", +// Output: ` +// declare const foo: false & { __BRAND: 'Foo' }; +// if (((''.length > 0) && {}) || (foo && void 0)) { } +// `, +// }, +// { +// MessageId: "conditionFixCompareEmptyString", +// Output: ` +// declare const foo: false & { __BRAND: 'Foo' }; +// if ((('' !== "") && {}) || (foo && void 0)) { } +// `, +// }, +// { +// MessageId: "conditionFixCastBoolean", +// Output: ` +// declare const foo: false & { __BRAND: 'Foo' }; +// if (((Boolean('')) && {}) || (foo && void 0)) { } +// `, +// }, +// }, + }, + { + MessageId: "conditionErrorObject", + }, + { + MessageId: "conditionErrorNullish", + }, + }, + }, + { + Code: `'asd' && 123 && [] && null;`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `('asd'.length > 0) && 123 && [] && null;`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `('asd' !== "") && 123 && [] && null;`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `(Boolean('asd')) && 123 && [] && null;`, + // }, + // }, + }, + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `'asd' && (123 !== 0) && [] && null;`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `'asd' && (!Number.isNaN(123)) && [] && null;`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `'asd' && (Boolean(123)) && [] && null;`, + // }, + // }, + }, + { + MessageId: "conditionErrorObject", + }, + }, + }, + { + Code: `'asd' || 123 || [] || null;`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `('asd'.length > 0) || 123 || [] || null;`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `('asd' !== "") || 123 || [] || null;`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `(Boolean('asd')) || 123 || [] || null;`, + // }, + // }, + }, + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `'asd' || (123 !== 0) || [] || null;`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `'asd' || (!Number.isNaN(123)) || [] || null;`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `'asd' || (Boolean(123)) || [] || null;`, + // }, + // }, + }, + { + MessageId: "conditionErrorObject", + }, + }, + }, + { + Code: `let x = (1 && 'a' && null) || 0 || '' || {};`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `let x = ((1 !== 0) && 'a' && null) || 0 || '' || {};`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `let x = ((!Number.isNaN(1)) && 'a' && null) || 0 || '' || {};`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `let x = ((Boolean(1)) && 'a' && null) || 0 || '' || {};`, + // }, + // }, + }, + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `let x = (1 && ('a'.length > 0) && null) || 0 || '' || {};`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `let x = (1 && ('a' !== "") && null) || 0 || '' || {};`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `let x = (1 && (Boolean('a')) && null) || 0 || '' || {};`, + // }, + // }, + }, + { + MessageId: "conditionErrorNullish", + }, + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `let x = (1 && 'a' && null) || (0 !== 0) || '' || {};`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `let x = (1 && 'a' && null) || (!Number.isNaN(0)) || '' || {};`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `let x = (1 && 'a' && null) || (Boolean(0)) || '' || {};`, + // }, + // }, + }, + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `let x = (1 && 'a' && null) || 0 || (''.length > 0) || {};`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `let x = (1 && 'a' && null) || 0 || ('' !== "") || {};`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `let x = (1 && 'a' && null) || 0 || (Boolean('')) || {};`, + // }, + // }, + }, + }, + }, + { + Code: `return (1 || 'a' || null) && 0 && '' && {};`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `return ((1 !== 0) || 'a' || null) && 0 && '' && {};`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `return ((!Number.isNaN(1)) || 'a' || null) && 0 && '' && {};`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `return ((Boolean(1)) || 'a' || null) && 0 && '' && {};`, + // }, + // }, + }, + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `return (1 || ('a'.length > 0) || null) && 0 && '' && {};`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `return (1 || ('a' !== "") || null) && 0 && '' && {};`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `return (1 || (Boolean('a')) || null) && 0 && '' && {};`, + // }, + // }, + }, + { + MessageId: "conditionErrorNullish", + }, + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `return (1 || 'a' || null) && (0 !== 0) && '' && {};`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `return (1 || 'a' || null) && (!Number.isNaN(0)) && '' && {};`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `return (1 || 'a' || null) && (Boolean(0)) && '' && {};`, + // }, + // }, + }, + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `return (1 || 'a' || null) && 0 && (''.length > 0) && {};`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `return (1 || 'a' || null) && 0 && ('' !== "") && {};`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `return (1 || 'a' || null) && 0 && (Boolean('')) && {};`, + // }, + // }, + }, + }, + }, + { + Code: `console.log((1 && []) || ('a' && {}));`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `console.log(((1 !== 0) && []) || ('a' && {}));`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `console.log(((!Number.isNaN(1)) && []) || ('a' && {}));`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `console.log(((Boolean(1)) && []) || ('a' && {}));`, + // }, + // }, + }, + { + MessageId: "conditionErrorObject", + }, + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `console.log((1 && []) || (('a'.length > 0) && {}));`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `console.log((1 && []) || (('a' !== "") && {}));`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `console.log((1 && []) || ((Boolean('a')) && {}));`, + // }, + // }, + }, + }, + }, + { + Code: `if ((1 && []) || ('a' && {})) void 0;`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + // MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `if (((1 !== 0) && []) || ('a' && {})) void 0;`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `if (((!Number.isNaN(1)) && []) || ('a' && {})) void 0;`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `if (((Boolean(1)) && []) || ('a' && {})) void 0;`, + // }, + // }, + }, + { + MessageId: "conditionErrorObject", + }, + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `if ((1 && []) || (('a'.length > 0) && {})) void 0;`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `if ((1 && []) || (('a' !== "") && {})) void 0;`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `if ((1 && []) || ((Boolean('a')) && {})) void 0;`, + // }, + // }, + }, + { + MessageId: "conditionErrorObject", + }, + }, + }, + { + Code: `let x = null || 0 || 'a' || [] ? {} : undefined;`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", + }, + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `let x = null || (0 !== 0) || 'a' || [] ? {} : undefined;`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `let x = null || (!Number.isNaN(0)) || 'a' || [] ? {} : undefined;`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `let x = null || (Boolean(0)) || 'a' || [] ? {} : undefined;`, + // }, + // }, + }, + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `let x = null || 0 || ('a'.length > 0) || [] ? {} : undefined;`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `let x = null || 0 || ('a' !== "") || [] ? {} : undefined;`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `let x = null || 0 || (Boolean('a')) || [] ? {} : undefined;`, + // }, + // }, + }, + { + MessageId: "conditionErrorObject", + }, + }, + }, + { + Code: `return !(null || 0 || 'a' || []);`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", + }, + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `return !(null || (0 !== 0) || 'a' || []);`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `return !(null || (!Number.isNaN(0)) || 'a' || []);`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `return !(null || (Boolean(0)) || 'a' || []);`, + // }, + // }, + }, + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `return !(null || 0 || ('a'.length > 0) || []);`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `return !(null || 0 || ('a' !== "") || []);`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `return !(null || 0 || (Boolean('a')) || []);`, + // }, + // }, + }, + { + MessageId: "conditionErrorObject", + }, + }, + }, + { + Code: `null || {};`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", + }, + }, + }, + { + Code: `undefined && [];`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", + }, + }, + }, + { + Code: ` +declare const x: null; +if (x) { +} + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", + }, }, - { - Code: ` - [1, null].filter(function (x) { - return x != null; - }); - `, + }, + { + Code: `(x: undefined) => !x;`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", + }, }, - { - Code: ` - ['one', 'two', ''].filter(function (x) { - return !!x; - }); - `, + }, + { + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", + }, }, - { - Code: ` - ['one', 'two', ''].filter(function (x): boolean { - return !!x; - }); - `, + }, + { + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", + }, }, - { - Code: ` - ['one', 'two', ''].filter(function (x): boolean { - if (x) { - return true; - } - }); - `, + }, + { + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", + }, }, - { - Code: ` - ['one', 'two', ''].filter(function (x): boolean { - if (x) { - return true; - } - - throw new Error('oops'); - }); - `, + }, + { + Code: `[] || 1;`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorObject", + }, }, - { - Code: ` - declare const predicate: (string) => boolean; - ['one', 'two', ''].filter(predicate); - `, + }, + { + Code: `({}) && 'a';`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorObject", + }, }, - { - Code: ` - declare function notNullish(x: T): x is NonNullable; - ['one', null].filter(notNullish); - `, - }, - { - Code: ` - declare function predicate(x: string | null): x is string; - ['one', null].filter(predicate); - `, - }, - { - Code: ` - declare function predicate(x: string | null): T; - ['one', null].filter(predicate); - `, - }, - { - Code: ` - declare function f(x: number): boolean; - declare function f(x: string | null): boolean; - - [35].filter(f); - `, - }, - }, []rule_tester.InvalidTestCase{ - { - Code: ` - if (true && 1 + 1) { - } - `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 2}, - }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */ - }, - { - Code: "while (false || 'a' + 'b') {}", - Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, - }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ - }, - { - Code: "(x: object) => (true || false || x ? true : false);", - Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObject", Line: 1}, - }, - }, - { - Code: "if (('' && {}) || (0 && void 0)) { }", - Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, - {MessageId: "unexpectedObject", Line: 1}, - {MessageId: "unexpectedNumber", Line: 1}, - {MessageId: "unexpectedNullish", Line: 1}, - }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */ - }, - { - Code: ` - declare const array: string[]; - array.some(x => x); - `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableBoolean: utils.Ref(true), AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString"}, - }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, explicitBooleanReturnType */ - }, - { - Code: ` - declare const foo: true & { __BRAND: 'Foo' }; - if (('' && foo) || (0 && void 0)) { } - `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 3}, - {MessageId: "unexpectedNumber", Line: 3}, - {MessageId: "unexpectedNullish", Line: 3}, - }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */ - }, - { - Code: ` - declare const foo: false & { __BRAND: 'Foo' }; - if (('' && {}) || (foo && void 0)) { } - `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 3}, - {MessageId: "unexpectedObject", Line: 3}, - {MessageId: "unexpectedNullish", Line: 3}, - }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ - }, - { - Code: "'asd' && 123 && [] && null;", - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, - {MessageId: "unexpectedNumber", Line: 1}, - {MessageId: "unexpectedObject", Line: 1}, - }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */ - }, - { - Code: "'asd' || 123 || [] || null;", - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, - {MessageId: "unexpectedNumber", Line: 1}, - {MessageId: "unexpectedObject", Line: 1}, - }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */ - }, - { - Code: "let x = (1 && 'a' && null) || 0 || '' || {};", - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, - {MessageId: "unexpectedString", Line: 1}, - {MessageId: "unexpectedNullish", Line: 1}, - {MessageId: "unexpectedNumber", Line: 1}, - {MessageId: "unexpectedString", Line: 1}, - }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ - }, - { - Code: "return (1 || 'a' || null) && 0 && '' && {};", - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, - {MessageId: "unexpectedString", Line: 1}, - {MessageId: "unexpectedNullish", Line: 1}, - {MessageId: "unexpectedNumber", Line: 1}, - {MessageId: "unexpectedString", Line: 1}, - }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ - }, - { - Code: "console.log((1 && []) || ('a' && {}));", - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, - {MessageId: "unexpectedObject", Line: 1}, - {MessageId: "unexpectedString", Line: 1}, - }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ - }, - { - Code: "if ((1 && []) || ('a' && {})) void 0;", - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, - {MessageId: "unexpectedObject", Line: 1}, - {MessageId: "unexpectedString", Line: 1}, - {MessageId: "unexpectedObject", Line: 1}, - }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ - }, - { - Code: "let x = null || 0 || 'a' || [] ? {} : undefined;", - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1}, - {MessageId: "unexpectedNumber", Line: 1}, - {MessageId: "unexpectedString", Line: 1}, - {MessageId: "unexpectedObject", Line: 1}, - }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ - }, - { - Code: "return !(null || 0 || 'a' || []);", - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1}, - {MessageId: "unexpectedNumber", Line: 1}, - {MessageId: "unexpectedString", Line: 1}, - {MessageId: "unexpectedObject", Line: 1}, - }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ - }, - { - Code: "null || {};", - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1}, - }, - }, - { - Code: "undefined && [];", - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1}, - }, - }, - { - Code: ` - declare const x: null; - if (x) { - } - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 3}, - }, - }, - { - Code: "(x: undefined) => !x;", - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1}, - }, - }, - { - Code: "(x: T) => (x ? 1 : 0);", - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1}, - }, - }, - { - Code: "(x: T) => (x ? 1 : 0);", - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1}, - }, - }, - { - Code: "(x: T) => (x ? 1 : 0);", - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 1}, - }, - }, - { - Code: "[] || 1;", - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObject", Line: 1}, - }, - }, - { - Code: "({}) && 'a';", - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObject", Line: 1}, - }, - }, - { - Code: ` - declare const x: symbol; - if (x) { - } - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObject", Line: 3}, - }, - }, - { - Code: "(x: () => void) => !x;", - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObject", Line: 1}, - }, - }, - { - Code: "(x: T) => (x ? 1 : 0);", - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObject", Line: 1}, - }, - }, - { - Code: "(x: T) => (x ? 1 : 0);", - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObject", Line: 1}, - }, - }, - { - Code: "(x: T) => (x ? 1 : 0);", - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObject", Line: 1}, - }, - }, - { - Code: " void>(x: T) => (x ? 1 : 0);", - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObject", Line: 1}, - }, - }, - { - Code: "while ('') {}", - Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, - }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ - }, - { - Code: "for (; 'foo'; ) {}", - Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, - }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ - }, - { - Code: ` - declare const x: string; - if (x) { - } - `, - Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 3}, - }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ - }, - { - Code: "(x: string) => !x;", - Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, - }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ - }, - { - Code: "(x: T) => (x ? 1 : 0);", - Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 1}, - }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean */ - }, - { - Code: "while (0n) {}", - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, - }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */ - }, - { - Code: "for (; 123; ) {}", - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, - }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */ - }, - { - Code: ` - declare const x: number; - if (x) { - } - `, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 3}, - }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */ - }, - { - Code: "(x: bigint) => !x;", - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, - }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */ - }, - { - Code: "(x: T) => (x ? 1 : 0);", - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, - }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */ - }, - { - Code: "![]['length']; // doesn't count as array.length when computed", - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 1}, - }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */ - }, - { - Code: ` - declare const a: any[] & { notLength: number }; - if (a.notLength) { - } - `, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 3}, - }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean */ - }, - { - Code: ` - if (![].length) { - } - `, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 2}, - }, /* Suggestions: conditionFixCompareArrayLengthZero */ - }, - { - Code: ` - (a: number[]) => a.length && '...'; - `, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 2}, - }, /* Suggestions: conditionFixCompareArrayLengthNonzero */ - }, - { - Code: ` - (...a: T) => a.length || 'empty'; - `, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 2}, - }, /* Suggestions: conditionFixCompareArrayLengthNonzero */ - }, - { - Code: ` - declare const x: string | number; - if (x) { - } - `, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(true), AllowString: utils.Ref(true), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedMixedCondition", Line: 3}, - }, - }, - { - Code: "(x: bigint | string) => !x;", - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(true), AllowString: utils.Ref(true), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedMixedCondition", Line: 1}, - }, - }, - { - Code: "(x: T) => (x ? 1 : 0);", - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(true), AllowString: utils.Ref(true), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedMixedCondition", Line: 1}, - }, - }, - { - Code: ` - declare const x: boolean | null; - if (x) { - } - `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableBoolean: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableBoolean", Line: 3}, - }, /* Suggestions: conditionFixDefaultFalse, conditionFixCompareTrue */ - }, - { - Code: "(x?: boolean) => !x;", - Options: StrictBooleanExpressionsOptions{ - AllowNullableBoolean: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableBoolean", Line: 1}, - }, /* Suggestions: conditionFixDefaultFalse, conditionFixCompareFalse */ - }, - { - Code: "(x: T) => (x ? 1 : 0);", - Options: StrictBooleanExpressionsOptions{ - AllowNullableBoolean: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableBoolean", Line: 1}, - }, /* Suggestions: conditionFixDefaultFalse, conditionFixCompareTrue */ - }, - { - Code: ` - declare const x: object | null; - if (x) { - } - `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableObject", Line: 3}, - }, /* Suggestions: conditionFixCompareNullish */ - }, - { - Code: "(x?: { a: number }) => !x;", - Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableObject", Line: 1}, - }, /* Suggestions: conditionFixCompareNullish */ - }, - { - Code: "(x: T) => (x ? 1 : 0);", - Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableObject", Line: 1}, - }, /* Suggestions: conditionFixCompareNullish */ - }, - { - Code: ` - declare const x: string | null; - if (x) { - } - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 3}, - }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ - }, - { - Code: "(x?: string) => !x;", - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 1}, - }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ - }, - { - Code: "(x: T) => (x ? 1 : 0);", - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 1}, - }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ - }, - { - Code: ` - function foo(x: '' | 'bar' | null) { - if (!x) { - } - } - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 3}, - }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ - }, - { - Code: ` - declare const x: number | null; - if (x) { - } - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableNumber", Line: 3}, - }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultZero, conditionFixCastBoolean */ - }, - { - Code: "(x?: number) => !x;", - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableNumber", Line: 1}, - }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultZero, conditionFixCastBoolean */ - }, - { - Code: "(x: T) => (x ? 1 : 0);", - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableNumber", Line: 1}, - }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultZero, conditionFixCastBoolean */ - }, - { - Code: ` - function foo(x: 0 | 1 | null) { - if (!x) { - } - } - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableNumber", Line: 3}, - }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultZero, conditionFixCastBoolean */ - }, - { - Code: ` - enum ExampleEnum { - This = 0, - That = 1, - } - const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; - if (theEnum) { - } - `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableEnum", Line: 7}, - }, /* Suggestions: conditionFixCompareNullish */ - }, - { - Code: ` - enum ExampleEnum { - This = 0, - That = 1, - } - const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; - if (!theEnum) { - } - `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableEnum", Line: 7}, - }, /* Suggestions: conditionFixCompareNullish */ - }, - { - Code: ` - enum ExampleEnum { - This, - That, - } - const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; - if (!theEnum) { - } - `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableEnum", Line: 7}, - }, /* Suggestions: conditionFixCompareNullish */ - }, - { - Code: ` - enum ExampleEnum { - This = '', - That = 'a', - } - const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; - if (!theEnum) { - } - `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableEnum", Line: 7}, - }, /* Suggestions: conditionFixCompareNullish */ - }, - { - Code: ` - enum ExampleEnum { - This = '', - That = 0, - } - const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; - if (!theEnum) { - } - `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableEnum", Line: 7}, - }, /* Suggestions: conditionFixCompareNullish */ - }, - { - Code: ` - enum ExampleEnum { - This = 'one', - That = 'two', - } - const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; - if (!theEnum) { - } - `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableEnum", Line: 7}, - }, /* Suggestions: conditionFixCompareNullish */ - }, - { - Code: ` - enum ExampleEnum { - This = 1, - That = 2, - } - const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; - if (!theEnum) { - } - `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableEnum", Line: 7}, - }, /* Suggestions: conditionFixCompareNullish */ - }, - { - Code: ` - enum ExampleEnum { - This = 0, - That = 'one', - } - (value?: ExampleEnum) => (value ? 1 : 0); - `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableEnum", Line: 6}, - }, /* Suggestions: conditionFixCompareNullish */ - }, - { - Code: ` - enum ExampleEnum { - This = '', - That = 1, - } - (value?: ExampleEnum) => (!value ? 1 : 0); - `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableEnum", Line: 6}, - }, /* Suggestions: conditionFixCompareNullish */ - }, - { - Code: ` - enum ExampleEnum { - This = 'this', - That = 1, - } - (value?: ExampleEnum) => (!value ? 1 : 0); - `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableEnum", Line: 6}, - }, /* Suggestions: conditionFixCompareNullish */ - }, - { - Code: ` - enum ExampleEnum { - This = '', - That = 0, - } - (value?: ExampleEnum) => (!value ? 1 : 0); - `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableEnum: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableEnum", Line: 6}, - }, /* Suggestions: conditionFixCompareNullish */ - }, - { - Code: ` - if (x) { - } - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedAny", Line: 2}, - }, /* Suggestions: conditionFixCastBoolean */ - }, - { - Code: "x => !x;", - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedAny", Line: 1}, - }, /* Suggestions: conditionFixCastBoolean */ - }, - { - Code: "(x: T) => (x ? 1 : 0);", - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedAny", Line: 1}, - }, /* Suggestions: conditionFixCastBoolean */ - }, - { - Code: "(x: T) => (x ? 1 : 0);", - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedAny", Line: 1}, - }, /* Suggestions: conditionFixCastBoolean */ - }, - { - Code: ` - declare const x: string[] | null; - if (x) { - } - `, - TSConfig: "tsconfig.unstrict.json", - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "noStrictNullCheck", Line: 0}, - {MessageId: "unexpectedObject", Line: 3}, - }, - }, - { - Code: ` - declare const obj: { x: number } | null; - !obj ? 1 : 0 - !obj - obj || 0 - obj && 1 || 0 + }, + { + Code: ` +declare const x: symbol; +if (x) { +} `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableObject", Line: 3}, - {MessageId: "unexpectedNullableObject", Line: 4}, - {MessageId: "unexpectedNullableObject", Line: 5}, - {MessageId: "unexpectedNullableObject", Line: 6}, - }, /* Suggestions: conditionFixCompareNullish, conditionFixCompareNullish, conditionFixCompareNullish, conditionFixCompareNullish */ - }, - { - Code: ` - declare function assert(x: unknown): asserts x; - declare const nullableString: string | null; - assert(nullableString); + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorObject", + }, + }, + }, + { + Code: `(x: () => void) => !x;`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorObject", + }, + }, + }, + { + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorObject", + }, + }, + }, + { + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorObject", + }, + }, + }, + { + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorObject", + }, + }, + }, + { + Code: ` void>(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorObject", + }, + }, + }, + { + Code: `while ('') {}`, + Options: StrictBooleanExpressionsOptions{AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `while (''.length > 0) {}`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `while ('' !== "") {}`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `while (Boolean('')) {}`, + // }, + // }, + }, + }, + }, + { + Code: `for (; 'foo'; ) {}`, + Options: StrictBooleanExpressionsOptions{AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `for (; 'foo'.length > 0; ) {}`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `for (; 'foo' !== ""; ) {}`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `for (; Boolean('foo'); ) {}`, + // }, + // }, + }, + }, + }, + { + Code: ` +declare const x: string; +if (x) { +} `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 4}, - }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ - }, - { - Code: ` - declare function assert(a: number, b: unknown): asserts b; - declare const nullableString: string | null; - assert(foo, nullableString); + Options: StrictBooleanExpressionsOptions{AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorString", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareStringLength", +// Output: ` +// declare const x: string; +// if (x.length > 0) { +// } +// `, +// }, +// { +// MessageId: "conditionFixCompareEmptyString", +// Output: ` +// declare const x: string; +// if (x !== "") { +// } +// `, +// }, +// { +// MessageId: "conditionFixCastBoolean", +// Output: ` +// declare const x: string; +// if (Boolean(x)) { +// } +// `, +// }, +// }, + }, + }, + }, + { + Code: `(x: string) => !x;`, + Options: StrictBooleanExpressionsOptions{AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `(x: string) => x.length === 0;`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `(x: string) => x === "";`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `(x: string) => !Boolean(x);`, + // }, + // }, + }, + }, + }, + { + Code: `(x: T) => (x ? 1 : 0);`, + Options: StrictBooleanExpressionsOptions{AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `(x: T) => ((x.length > 0) ? 1 : 0);`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `(x: T) => ((x !== "") ? 1 : 0);`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `(x: T) => ((Boolean(x)) ? 1 : 0);`, + // }, + // }, + }, + }, + }, + { + Code: `while (0n) {}`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `while (0n !== 0) {}`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `while (!Number.isNaN(0n)) {}`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `while (Boolean(0n)) {}`, + // }, + // }, + }, + }, + }, + { + Code: `for (; 123; ) {}`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `for (; 123 !== 0; ) {}`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `for (; !Number.isNaN(123); ) {}`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `for (; Boolean(123); ) {}`, + // }, + // }, + }, + }, + }, + { + Code: ` +declare const x: number; +if (x) { +} `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 4}, - }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ - }, - { - Code: ` - declare function assert(a: number, b: unknown): asserts b; - declare function assert(one: number, two: unknown): asserts two; - declare const nullableString: string | null; - assert(foo, nullableString); + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareZero", +// Output: ` +// declare const x: number; +// if (x !== 0) { +// } +// `, +// }, +// { +// MessageId: "conditionFixCompareNaN", +// Output: ` +// declare const x: number; +// if (!Number.isNaN(x)) { +// } +// `, +// }, +// { +// MessageId: "conditionFixCastBoolean", +// Output: ` +// declare const x: number; +// if (Boolean(x)) { +// } +// `, +// }, +// }, + }, + }, + }, + { + Code: `(x: bigint) => !x;`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `(x: bigint) => x === 0;`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `(x: bigint) => Number.isNaN(x);`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `(x: bigint) => !Boolean(x);`, + // }, + // }, + }, + }, + }, + { + Code: `(x: T) => (x ? 1 : 0);`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `(x: T) => ((x !== 0) ? 1 : 0);`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `(x: T) => ((!Number.isNaN(x)) ? 1 : 0);`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `(x: T) => ((Boolean(x)) ? 1 : 0);`, + // }, + // }, + }, + }, + }, + { + Code: `![]['length']; // doesn't count as array.length when computed`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `[]['length'] === 0; // doesn't count as array.length when computed`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `Number.isNaN([]['length']); // doesn't count as array.length when computed`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `!Boolean([]['length']); // doesn't count as array.length when computed`, + // }, + // }, + }, + }, + }, + { + Code: ` +declare const a: any[] & { notLength: number }; +if (a.notLength) { +} `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 5}, - }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ - }, - { - Code: ` - declare function assert(this: object, a: number, b: unknown): asserts b; - declare const nullableString: string | null; - assert(foo, nullableString); + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareZero", +// Output: ` +// declare const a: any[] & { notLength: number }; +// if (a.notLength !== 0) { +// } +// `, +// }, +// { +// MessageId: "conditionFixCompareNaN", +// Output: ` +// declare const a: any[] & { notLength: number }; +// if (!Number.isNaN(a.notLength)) { +// } +// `, +// }, +// { +// MessageId: "conditionFixCastBoolean", +// Output: ` +// declare const a: any[] & { notLength: number }; +// if (Boolean(a.notLength)) { +// } +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +if (![].length) { +} `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 4}, - }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ - }, - { - // This should be checkable, but the TS API doesn't currently report - // `someAssert(maybeString)` as a type predicate call, which appears to be - // a bug. - // - // See https://github.com/microsoft/TypeScript/issues/59707 - Skip: true, - Code: ` - function asserts1(x: string | number | undefined): asserts x {} - function asserts2(x: string | number | undefined): asserts x {} + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareArrayLengthZero", +// Output: ` +// if ([].length === 0) { +// } +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +(a: number[]) => a.length && '...'; + `, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareArrayLengthNonzero", +// Output: ` +// (a: number[]) => (a.length > 0) && '...'; +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +(...a: T) => a.length || 'empty'; + `, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareArrayLengthNonzero", +// Output: ` +// (...a: T) => (a.length > 0) || 'empty'; +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +declare const x: string | number; +if (x) { +} + `, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(true), AllowString: utils.Ref(true)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorOther", + }, + }, + }, + { + Code: `(x: bigint | string) => !x;`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(true), AllowString: utils.Ref(true)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorOther", + }, + }, + }, + { + Code: `(x: T) => (x ? 1 : 0);`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(true), AllowString: utils.Ref(true)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorOther", + }, + }, + }, + { + Code: ` +declare const x: boolean | null; +if (x) { +} + `, + Options: StrictBooleanExpressionsOptions{AllowNullableBoolean: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableBoolean", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixDefaultFalse", +// Output: ` +// declare const x: boolean | null; +// if (x ?? false) { +// } +// `, +// }, +// { +// MessageId: "conditionFixCompareTrue", +// Output: ` +// declare const x: boolean | null; +// if (x === true) { +// } +// `, +// }, +// }, + }, + }, + }, + { + Code: `(x?: boolean) => !x;`, + Options: StrictBooleanExpressionsOptions{AllowNullableBoolean: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableBoolean", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixDefaultFalse", + // Output: `(x?: boolean) => !(x ?? false);`, + // }, + // { + // MessageId: "conditionFixCompareFalse", + // Output: `(x?: boolean) => x === false;`, + // }, + // }, + }, + }, + }, + { + Code: `(x: T) => (x ? 1 : 0);`, + Options: StrictBooleanExpressionsOptions{AllowNullableBoolean: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableBoolean", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixDefaultFalse", + // Output: `(x: T) => ((x ?? false) ? 1 : 0);`, + // }, + // { + // MessageId: "conditionFixCompareTrue", + // Output: `(x: T) => ((x === true) ? 1 : 0);`, + // }, + // }, + }, + }, + }, + { + Code: ` +declare const x: object | null; +if (x) { +} + `, + Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableObject", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareNullish", +// Output: ` +// declare const x: object | null; +// if (x != null) { +// } +// `, +// }, +// }, + }, + }, + }, + { + Code: `(x?: { a: number }) => !x;`, + Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableObject", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: `(x?: { a: number }) => x == null;`, + // }, + // }, + }, + }, + }, + { + Code: `(x: T) => (x ? 1 : 0);`, + Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableObject", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: `(x: T) => ((x != null) ? 1 : 0);`, + // }, + // }, + }, + }, + }, + { + Code: ` +declare const x: string | null; +if (x) { +} + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableString", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareNullish", +// Output: ` +// declare const x: string | null; +// if (x != null) { +// } +// `, +// }, +// { +// MessageId: "conditionFixDefaultEmptyString", +// Output: ` +// declare const x: string | null; +// if (x ?? "") { +// } +// `, +// }, +// { +// MessageId: "conditionFixCastBoolean", +// Output: ` +// declare const x: string | null; +// if (Boolean(x)) { +// } +// `, +// }, +// }, + }, + }, + }, + { + Code: `(x?: string) => !x;`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: `(x?: string) => x == null;`, + // }, + // { + // MessageId: "conditionFixDefaultEmptyString", + // Output: `(x?: string) => !(x ?? "");`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `(x?: string) => !Boolean(x);`, + // }, + // }, + }, + }, + }, + { + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: `(x: T) => ((x != null) ? 1 : 0);`, + // }, + // { + // MessageId: "conditionFixDefaultEmptyString", + // Output: `(x: T) => ((x ?? "") ? 1 : 0);`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `(x: T) => ((Boolean(x)) ? 1 : 0);`, + // }, + // }, + }, + }, + }, + { + Code: ` +function foo(x: '' | 'bar' | null) { + if (!x) { + } +} + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableString", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareNullish", +// Output: ` +// function foo(x: '' | 'bar' | null) { +// if (x == null) { +// } +// } +// `, +// }, +// { +// MessageId: "conditionFixDefaultEmptyString", +// Output: ` +// function foo(x: '' | 'bar' | null) { +// if (!(x ?? "")) { +// } +// } +// `, +// }, +// { +// MessageId: "conditionFixCastBoolean", +// Output: ` +// function foo(x: '' | 'bar' | null) { +// if (!Boolean(x)) { +// } +// } +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +declare const x: number | null; +if (x) { +} + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableNumber", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareNullish", +// Output: ` +// declare const x: number | null; +// if (x != null) { +// } +// `, +// }, +// { +// MessageId: "conditionFixDefaultZero", +// Output: ` +// declare const x: number | null; +// if (x ?? 0) { +// } +// `, +// }, +// { +// MessageId: "conditionFixCastBoolean", +// Output: ` +// declare const x: number | null; +// if (Boolean(x)) { +// } +// `, +// }, +// }, + }, + }, + }, + { + Code: `(x?: number) => !x;`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: `(x?: number) => x == null;`, + // }, + // { + // MessageId: "conditionFixDefaultZero", + // Output: `(x?: number) => !(x ?? 0);`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `(x?: number) => !Boolean(x);`, + // }, + // }, + }, + }, + }, + { + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: `(x: T) => ((x != null) ? 1 : 0);`, + // }, + // { + // MessageId: "conditionFixDefaultZero", + // Output: `(x: T) => ((x ?? 0) ? 1 : 0);`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `(x: T) => ((Boolean(x)) ? 1 : 0);`, + // }, + // }, + }, + }, + }, + { + Code: ` +function foo(x: 0 | 1 | null) { + if (!x) { + } +} + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableNumber", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareNullish", +// Output: ` +// function foo(x: 0 | 1 | null) { +// if (x == null) { +// } +// } +// `, +// }, +// { +// MessageId: "conditionFixDefaultZero", +// Output: ` +// function foo(x: 0 | 1 | null) { +// if (!(x ?? 0)) { +// } +// } +// `, +// }, +// { +// MessageId: "conditionFixCastBoolean", +// Output: ` +// function foo(x: 0 | 1 | null) { +// if (!Boolean(x)) { +// } +// } +// `, +// }, +// }, + }, + }, + }, + { + Code: ` + enum ExampleEnum { + This = 0, + That = 1, + } + const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + if (theEnum) { + } + `, + Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableEnum", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // enum ExampleEnum { + // This = 0, + // That = 1, + // } + // const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + // if (theEnum != null) { + // } + // `, + // }, + // }, + }, + }, + }, + { + Code: ` + enum ExampleEnum { + This = 0, + That = 1, + } + const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + if (!theEnum) { + } + `, + Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableEnum", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // enum ExampleEnum { + // This = 0, + // That = 1, + // } + // const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + // if (theEnum == null) { + // } + // `, + // }, + // }, + }, + }, + }, + { + Code: ` + enum ExampleEnum { + This, + That, + } + const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + if (!theEnum) { + } + `, + Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableEnum", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // enum ExampleEnum { + // This, + // That, + // } + // const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + // if (theEnum == null) { + // } + // `, + // }, + // }, + }, + }, + }, + { + Code: ` + enum ExampleEnum { + This = '', + That = 'a', + } + const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + if (!theEnum) { + } + `, + Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableEnum", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // enum ExampleEnum { + // This = '', + // That = 'a', + // } + // const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + // if (theEnum == null) { + // } + // `, + // }, + // }, + }, + }, + }, + { + Code: ` + enum ExampleEnum { + This = '', + That = 0, + } + const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + if (!theEnum) { + } + `, + Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableEnum", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // enum ExampleEnum { + // This = '', + // That = 0, + // } + // const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + // if (theEnum == null) { + // } + // `, + // }, + // }, + }, + }, + }, + { + Code: ` + enum ExampleEnum { + This = 'one', + That = 'two', + } + const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + if (!theEnum) { + } + `, + Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableEnum", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // enum ExampleEnum { + // This = 'one', + // That = 'two', + // } + // const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + // if (theEnum == null) { + // } + // `, + // }, + // }, + }, + }, + }, + { + Code: ` + enum ExampleEnum { + This = 1, + That = 2, + } + const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + if (!theEnum) { + } + `, + Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableEnum", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // enum ExampleEnum { + // This = 1, + // That = 2, + // } + // const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + // if (theEnum == null) { + // } + // `, + // }, + // }, + }, + }, + }, + { + Code: ` + enum ExampleEnum { + This = 0, + That = 'one', + } + (value?: ExampleEnum) => (value ? 1 : 0); + `, + Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableEnum", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // enum ExampleEnum { + // This = 0, + // That = 'one', + // } + // (value?: ExampleEnum) => ((value != null) ? 1 : 0); + // `, + // }, + // }, + }, + }, + }, + { + Code: ` + enum ExampleEnum { + This = '', + That = 1, + } + (value?: ExampleEnum) => (!value ? 1 : 0); + `, + Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableEnum", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // enum ExampleEnum { + // This = '', + // That = 1, + // } + // (value?: ExampleEnum) => ((value == null) ? 1 : 0); + // `, + // }, + // }, + }, + }, + }, + { + Code: ` + enum ExampleEnum { + This = 'this', + That = 1, + } + (value?: ExampleEnum) => (!value ? 1 : 0); + `, + Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableEnum", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // enum ExampleEnum { + // This = 'this', + // That = 1, + // } + // (value?: ExampleEnum) => ((value == null) ? 1 : 0); + // `, + // }, + // }, + }, + }, + }, + { + Code: ` + enum ExampleEnum { + This = '', + That = 0, + } + (value?: ExampleEnum) => (!value ? 1 : 0); + `, + Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableEnum", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // enum ExampleEnum { + // This = '', + // That = 0, + // } + // (value?: ExampleEnum) => ((value == null) ? 1 : 0); + // `, + // }, + // }, + }, + }, + }, + { + Code: ` +if (x) { +} + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorAny", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCastBoolean", +// Output: ` +// if (Boolean(x)) { +// } +// `, +// }, +// }, + }, + }, + }, + { + Code: `x => !x;`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorAny", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCastBoolean", + // Output: `x => !(Boolean(x));`, + // }, + // }, + }, + }, + }, + { + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorAny", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCastBoolean", + // Output: `(x: T) => ((Boolean(x)) ? 1 : 0);`, + // }, + // }, + }, + }, + }, + { + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorAny", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCastBoolean", + // Output: `(x: T) => ((Boolean(x)) ? 1 : 0);`, + // }, + // }, + }, + }, + }, + { + Code: ` +declare const x: string[] | null; +if (x) { +} + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "noStrictNullCheck", + }, + { + MessageId: "conditionErrorObject", + }, + }, + }, + { + Code: ` + declare const obj: { x: number } | null; + !obj ? 1 : 0 + !obj + obj || 0 + obj && 1 || 0 + `, + Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableObject", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // declare const obj: { x: number } | null; + // (obj == null) ? 1 : 0 + // !obj + // obj || 0 + // obj && 1 || 0 + // `, + // }, + // }, + }, + { + MessageId: "conditionErrorNullableObject", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // declare const obj: { x: number } | null; + // !obj ? 1 : 0 + // obj == null + // obj || 0 + // obj && 1 || 0 + // `, + // }, + // }, + }, + { + MessageId: "conditionErrorNullableObject", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // declare const obj: { x: number } | null; + // !obj ? 1 : 0 + // !obj + // ;(obj != null) || 0 + // obj && 1 || 0 + // `, + // }, + // }, + }, + { + MessageId: "conditionErrorNullableObject", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // declare const obj: { x: number } | null; + // !obj ? 1 : 0 + // !obj + // obj || 0 + // ;(obj != null) && 1 || 0 + // `, + // }, + // }, + }, + }, + }, + { + Code: ` +declare function assert(x: unknown): asserts x; +declare const nullableString: string | null; +assert(nullableString); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableString", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareNullish", +// Output: ` +// declare function assert(x: unknown): asserts x; +// declare const nullableString: string | null; +// assert(nullableString != null); +// `, +// }, +// { +// MessageId: "conditionFixDefaultEmptyString", +// Output: ` +// declare function assert(x: unknown): asserts x; +// declare const nullableString: string | null; +// assert(nullableString ?? ""); +// `, +// }, +// { +// MessageId: "conditionFixCastBoolean", +// Output: ` +// declare function assert(x: unknown): asserts x; +// declare const nullableString: string | null; +// assert(Boolean(nullableString)); +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +declare function assert(a: number, b: unknown): asserts b; +declare const nullableString: string | null; +assert(foo, nullableString); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableString", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareNullish", +// Output: ` +// declare function assert(a: number, b: unknown): asserts b; +// declare const nullableString: string | null; +// assert(foo, nullableString != null); +// `, +// }, +// { +// MessageId: "conditionFixDefaultEmptyString", +// Output: ` +// declare function assert(a: number, b: unknown): asserts b; +// declare const nullableString: string | null; +// assert(foo, nullableString ?? ""); +// `, +// }, +// { +// MessageId: "conditionFixCastBoolean", +// Output: ` +// declare function assert(a: number, b: unknown): asserts b; +// declare const nullableString: string | null; +// assert(foo, Boolean(nullableString)); +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +declare function assert(a: number, b: unknown): asserts b; +declare function assert(one: number, two: unknown): asserts two; +declare const nullableString: string | null; +assert(foo, nullableString); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableString", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareNullish", +// Output: ` +// declare function assert(a: number, b: unknown): asserts b; +// declare function assert(one: number, two: unknown): asserts two; +// declare const nullableString: string | null; +// assert(foo, nullableString != null); +// `, +// }, +// { +// MessageId: "conditionFixDefaultEmptyString", +// Output: ` +// declare function assert(a: number, b: unknown): asserts b; +// declare function assert(one: number, two: unknown): asserts two; +// declare const nullableString: string | null; +// assert(foo, nullableString ?? ""); +// `, +// }, +// { +// MessageId: "conditionFixCastBoolean", +// Output: ` +// declare function assert(a: number, b: unknown): asserts b; +// declare function assert(one: number, two: unknown): asserts two; +// declare const nullableString: string | null; +// assert(foo, Boolean(nullableString)); +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +declare function assert(this: object, a: number, b: unknown): asserts b; +declare const nullableString: string | null; +assert(foo, nullableString); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableString", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareNullish", +// Output: ` +// declare function assert(this: object, a: number, b: unknown): asserts b; +// declare const nullableString: string | null; +// assert(foo, nullableString != null); +// `, +// }, +// { +// MessageId: "conditionFixDefaultEmptyString", +// Output: ` +// declare function assert(this: object, a: number, b: unknown): asserts b; +// declare const nullableString: string | null; +// assert(foo, nullableString ?? ""); +// `, +// }, +// { +// MessageId: "conditionFixCastBoolean", +// Output: ` +// declare function assert(this: object, a: number, b: unknown): asserts b; +// declare const nullableString: string | null; +// assert(foo, Boolean(nullableString)); +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +function asserts1(x: string | number | undefined): asserts x {} +function asserts2(x: string | number | undefined): asserts x {} - const maybeString = Math.random() ? 'string'.slice() : undefined; +const maybeString = Math.random() ? 'string'.slice() : undefined; - const someAssert: typeof asserts1 | typeof asserts2 = - Math.random() > 0.5 ? asserts1 : asserts2; +const someAssert: typeof asserts1 | typeof asserts2 = + Math.random() > 0.5 ? asserts1 : asserts2; - someAssert(maybeString); - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString"}, - }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ - }, - { - Code: ` - function assert(this: object, a: number, b: unknown): asserts b; - function assert(a: bigint, b: unknown): asserts b; - function assert(this: object, a: string, two: string): asserts two; - function assert( - this: object, - a: string, - assertee: string, - c: bigint, - d: object, - ): asserts assertee; +someAssert(maybeString); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableString", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareNullish", +// Output: ` +// function asserts1(x: string | number | undefined): asserts x {} +// function asserts2(x: string | number | undefined): asserts x {} - function assert(...args: any[]) { - throw new Error('lol'); - } +// const maybeString = Math.random() ? 'string'.slice() : undefined; - declare const nullableString: string | null; - assert(3 as any, nullableString); - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 18}, - }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ - }, - { - Code: ` - function assert(this: object, a: number, b: unknown): asserts b; - function assert(a: bigint, b: unknown): asserts b; - function assert(this: object, a: string, two: string): asserts two; - function assert( - this: object, - a: string, - assertee: string, - c: bigint, - d: object, - ): asserts assertee; - function assert(a: any, two: unknown, ...rest: any[]): asserts two; +// const someAssert: typeof asserts1 | typeof asserts2 = +// Math.random() > 0.5 ? asserts1 : asserts2; - function assert(...args: any[]) { - throw new Error('lol'); - } +// someAssert(maybeString != null); +// `, +// }, +// { +// MessageId: "conditionFixDefaultEmptyString", +// Output: ` +// function asserts1(x: string | number | undefined): asserts x {} +// function asserts2(x: string | number | undefined): asserts x {} - declare const nullableString: string | null; - assert(3 as any, nullableString, 'more', 'args', 'afterwards'); - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 19}, - }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ - }, - { - Code: ` - declare function assert(a: boolean, b: unknown): asserts b; - declare function assert({ a }: { a: boolean }, b: unknown): asserts b; - declare const nullableString: string | null; - declare const boo: boolean; - assert(boo, nullableString); - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 6}, - }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ - }, - { - Code: ` - function assert(one: unknown): asserts one; - function assert(one: unknown, two: unknown): asserts two; - function assert(...args: unknown[]) { - throw new Error('not implemented'); - } - declare const nullableString: string | null; - assert(nullableString); - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 8}, - }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ - }, - { - Code: ` - ['one', 'two', ''].find(x => { - return x; - }); - `, - Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 2}, - }, /* Suggestions: explicitBooleanReturnType */ - }, - { - Code: ` - ['one', 'two', ''].find(x => { - return; - }); - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 2}, - }, /* Suggestions: explicitBooleanReturnType */ - }, - { - Code: ` - ['one', 'two', ''].findLast(x => { - return undefined; - }); - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 2}, - }, /* Suggestions: explicitBooleanReturnType */ - }, - { - Code: ` - ['one', 'two', ''].find(x => { - if (x) { - return Math.random() > 0.5; - } - }); - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableBoolean", Line: 2}, - }, /* Suggestions: explicitBooleanReturnType */ - }, - { - Code: ` - const predicate = (x: string) => { - if (x) { - return Math.random() > 0.5; - } - }; +// const maybeString = Math.random() ? 'string'.slice() : undefined; - ['one', 'two', ''].find(predicate); - `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableBoolean: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableBoolean", Line: 8}, - }, - }, - { - Code: ` - [1, null].every(async x => { - return x != null; - }); - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "predicateCannotBeAsync", Line: 2}, - }, - }, - { - Code: ` - const predicate = async x => { - return x != null; - }; +// const someAssert: typeof asserts1 | typeof asserts2 = +// Math.random() > 0.5 ? asserts1 : asserts2; - [1, null].every(predicate); - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObject", Line: 6}, - }, - }, - { - Code: ` - [1, null].every((x): boolean | number => { - return x != null; - }); - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedMixedCondition", Line: 2}, - }, - }, - { - Code: ` - [1, null].every((x): boolean | undefined => { - return x != null; - }); - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableBoolean", Line: 2}, - }, - }, - { - Code: ` - [1, null].every((x, i) => {}); - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 2}, - }, /* Suggestions: explicitBooleanReturnType */ - }, - { - Code: ` - [() => {}, null].every((x: () => void) => {}); - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 2}, - }, /* Suggestions: explicitBooleanReturnType */ - }, - { - Code: ` - [() => {}, null].every(function (x: () => void) {}); - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 2}, - }, /* Suggestions: explicitBooleanReturnType */ - }, - { - Code: ` - [() => {}, null].every(() => {}); - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullish", Line: 2}, - }, /* Suggestions: explicitBooleanReturnType */ - }, - { - Code: ` - declare function f(x: number): string; - declare function f(x: string | null): boolean; +// someAssert(maybeString ?? ""); +// `, +// }, +// { +// MessageId: "conditionFixCastBoolean", +// Output: ` +// function asserts1(x: string | number | undefined): asserts x {} +// function asserts2(x: string | number | undefined): asserts x {} - [35].filter(f); - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedMixedCondition", Line: 5}, +// const maybeString = Math.random() ? 'string'.slice() : undefined; + +// const someAssert: typeof asserts1 | typeof asserts2 = +// Math.random() > 0.5 ? asserts1 : asserts2; + +// someAssert(Boolean(maybeString)); +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +function assert(this: object, a: number, b: unknown): asserts b; +function assert(a: bigint, b: unknown): asserts b; +function assert(this: object, a: string, two: string): asserts two; +function assert( + this: object, + a: string, + assertee: string, + c: bigint, + d: object, +): asserts assertee; + +function assert(...args: any[]) { + throw new Error('lol'); +} + +declare const nullableString: string | null; +assert(3 as any, nullableString); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableString", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareNullish", +// Output: ` +// function assert(this: object, a: number, b: unknown): asserts b; +// function assert(a: bigint, b: unknown): asserts b; +// function assert(this: object, a: string, two: string): asserts two; +// function assert( +// this: object, +// a: string, +// assertee: string, +// c: bigint, +// d: object, +// ): asserts assertee; + +// function assert(...args: any[]) { +// throw new Error('lol'); +// } + +// declare const nullableString: string | null; +// assert(3 as any, nullableString != null); +// `, +// }, +// { +// MessageId: "conditionFixDefaultEmptyString", +// Output: ` +// function assert(this: object, a: number, b: unknown): asserts b; +// function assert(a: bigint, b: unknown): asserts b; +// function assert(this: object, a: string, two: string): asserts two; +// function assert( +// this: object, +// a: string, +// assertee: string, +// c: bigint, +// d: object, +// ): asserts assertee; + +// function assert(...args: any[]) { +// throw new Error('lol'); +// } + +// declare const nullableString: string | null; +// assert(3 as any, nullableString ?? ""); +// `, +// }, +// { +// MessageId: "conditionFixCastBoolean", +// Output: ` +// function assert(this: object, a: number, b: unknown): asserts b; +// function assert(a: bigint, b: unknown): asserts b; +// function assert(this: object, a: string, two: string): asserts two; +// function assert( +// this: object, +// a: string, +// assertee: string, +// c: bigint, +// d: object, +// ): asserts assertee; + +// function assert(...args: any[]) { +// throw new Error('lol'); +// } + +// declare const nullableString: string | null; +// assert(3 as any, Boolean(nullableString)); +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +function assert(this: object, a: number, b: unknown): asserts b; +function assert(a: bigint, b: unknown): asserts b; +function assert(this: object, a: string, two: string): asserts two; +function assert( + this: object, + a: string, + assertee: string, + c: bigint, + d: object, +): asserts assertee; +function assert(a: any, two: unknown, ...rest: any[]): asserts two; + +function assert(...args: any[]) { + throw new Error('lol'); +} + +declare const nullableString: string | null; +assert(3 as any, nullableString, 'more', 'args', 'afterwards'); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableString", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareNullish", +// Output: ` +// function assert(this: object, a: number, b: unknown): asserts b; +// function assert(a: bigint, b: unknown): asserts b; +// function assert(this: object, a: string, two: string): asserts two; +// function assert( +// this: object, +// a: string, +// assertee: string, +// c: bigint, +// d: object, +// ): asserts assertee; +// function assert(a: any, two: unknown, ...rest: any[]): asserts two; + +// function assert(...args: any[]) { +// throw new Error('lol'); +// } + +// declare const nullableString: string | null; +// assert(3 as any, nullableString != null, 'more', 'args', 'afterwards'); +// `, +// }, +// { +// MessageId: "conditionFixDefaultEmptyString", +// Output: ` +// function assert(this: object, a: number, b: unknown): asserts b; +// function assert(a: bigint, b: unknown): asserts b; +// function assert(this: object, a: string, two: string): asserts two; +// function assert( +// this: object, +// a: string, +// assertee: string, +// c: bigint, +// d: object, +// ): asserts assertee; +// function assert(a: any, two: unknown, ...rest: any[]): asserts two; + +// function assert(...args: any[]) { +// throw new Error('lol'); +// } + +// declare const nullableString: string | null; +// assert(3 as any, nullableString ?? "", 'more', 'args', 'afterwards'); +// `, +// }, +// { +// MessageId: "conditionFixCastBoolean", +// Output: ` +// function assert(this: object, a: number, b: unknown): asserts b; +// function assert(a: bigint, b: unknown): asserts b; +// function assert(this: object, a: string, two: string): asserts two; +// function assert( +// this: object, +// a: string, +// assertee: string, +// c: bigint, +// d: object, +// ): asserts assertee; +// function assert(a: any, two: unknown, ...rest: any[]): asserts two; + +// function assert(...args: any[]) { +// throw new Error('lol'); +// } + +// declare const nullableString: string | null; +// assert(3 as any, Boolean(nullableString), 'more', 'args', 'afterwards'); +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +declare function assert(a: boolean, b: unknown): asserts b; +declare function assert({ a }: { a: boolean }, b: unknown): asserts b; +declare const nullableString: string | null; +declare const boo: boolean; +assert(boo, nullableString); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableString", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareNullish", +// Output: ` +// declare function assert(a: boolean, b: unknown): asserts b; +// declare function assert({ a }: { a: boolean }, b: unknown): asserts b; +// declare const nullableString: string | null; +// declare const boo: boolean; +// assert(boo, nullableString != null); +// `, +// }, +// { +// MessageId: "conditionFixDefaultEmptyString", +// Output: ` +// declare function assert(a: boolean, b: unknown): asserts b; +// declare function assert({ a }: { a: boolean }, b: unknown): asserts b; +// declare const nullableString: string | null; +// declare const boo: boolean; +// assert(boo, nullableString ?? ""); +// `, +// }, +// { +// MessageId: "conditionFixCastBoolean", +// Output: ` +// declare function assert(a: boolean, b: unknown): asserts b; +// declare function assert({ a }: { a: boolean }, b: unknown): asserts b; +// declare const nullableString: string | null; +// declare const boo: boolean; +// assert(boo, Boolean(nullableString)); +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +function assert(one: unknown): asserts one; +function assert(one: unknown, two: unknown): asserts two; +function assert(...args: unknown[]) { + throw new Error('not implemented'); +} +declare const nullableString: string | null; +assert(nullableString); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableString", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareNullish", +// Output: ` +// function assert(one: unknown): asserts one; +// function assert(one: unknown, two: unknown): asserts two; +// function assert(...args: unknown[]) { +// throw new Error('not implemented'); +// } +// declare const nullableString: string | null; +// assert(nullableString != null); +// `, +// }, +// { +// MessageId: "conditionFixDefaultEmptyString", +// Output: ` +// function assert(one: unknown): asserts one; +// function assert(one: unknown, two: unknown): asserts two; +// function assert(...args: unknown[]) { +// throw new Error('not implemented'); +// } +// declare const nullableString: string | null; +// assert(nullableString ?? ""); +// `, +// }, +// { +// MessageId: "conditionFixCastBoolean", +// Output: ` +// function assert(one: unknown): asserts one; +// function assert(one: unknown, two: unknown): asserts two; +// function assert(...args: unknown[]) { +// throw new Error('not implemented'); +// } +// declare const nullableString: string | null; +// assert(Boolean(nullableString)); +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +['one', 'two', ''].find(x => { + return x; +}); + `, + Options: StrictBooleanExpressionsOptions{AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorString", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "explicitBooleanReturnType", +// Output: ` +// ['one', 'two', ''].find((x): boolean => { +// return x; +// }); +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +['one', 'two', ''].find(x => { + return; +}); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "explicitBooleanReturnType", +// Output: ` +// ['one', 'two', ''].find((x): boolean => { +// return; +// }); +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +['one', 'two', ''].findLast(x => { + return undefined; +}); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "explicitBooleanReturnType", +// Output: ` +// ['one', 'two', ''].findLast((x): boolean => { +// return undefined; +// }); +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +['one', 'two', ''].find(x => { + if (x) { + return Math.random() > 0.5; + } +}); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableBoolean", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "explicitBooleanReturnType", +// Output: ` +// ['one', 'two', ''].find((x): boolean => { +// if (x) { +// return Math.random() > 0.5; +// } +// }); +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +const predicate = (x: string) => { + if (x) { + return Math.random() > 0.5; + } +}; + +['one', 'two', ''].find(predicate); + `, + Options: StrictBooleanExpressionsOptions{AllowNullableBoolean: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableBoolean", + }, + }, + }, + { + Code: ` +[1, null].every(async x => { + return x != null; +}); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "predicateCannotBeAsync", }, }, - { - Code: ` - declare function f(x: number): string; - declare function f(x: number | boolean): boolean; - declare function f(x: string | null): boolean; + }, + { + Code: ` +const predicate = async x => { + return x != null; +}; - [35].filter(f); - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedMixedCondition", Line: 6}, +[1, null].every(predicate); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorObject", }, }, - { - Code: ` - declare function foo(x: number): T; - [1, null].every(foo); - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedAny", Line: 3}, + }, + { + Code: ` +[1, null].every((x): boolean | number => { + return x != null; +}); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorOther", }, }, - { - Code: ` - function foo(x: number): T {} - [1, null].every(foo); - `, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), - }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 3}, + }, + { + Code: ` +[1, null].every((x): boolean | undefined => { + return x != null; +}); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableBoolean", }, }, - { - Code: ` - declare const nullOrString: string | null; - ['one', null].filter(x => nullOrString); - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 3}, - }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean, explicitBooleanReturnType */ - }, - { - Code: ` - declare const nullOrString: string | null; - ['one', null].filter(x => !nullOrString); - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableString", Line: 3}, - }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultEmptyString, conditionFixCastBoolean */ - }, - { - Code: ` - declare const anyValue: any; - ['one', null].filter(x => anyValue); - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedAny", Line: 3}, - }, /* Suggestions: conditionFixCastBoolean, explicitBooleanReturnType */ - }, - { - Code: ` - declare const nullOrBoolean: boolean | null; - [true, null].filter(x => nullOrBoolean); - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableBoolean", Line: 3}, - }, /* Suggestions: conditionFixDefaultFalse, conditionFixCompareTrue, explicitBooleanReturnType */ - }, - { - Code: ` - enum ExampleEnum { - This = 0, - That = 1, - } - const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; - [0, 1].filter(x => theEnum); - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableEnum", Line: 7}, - }, /* Suggestions: conditionFixCompareNullish, explicitBooleanReturnType */ - }, - { - Code: ` - declare const nullOrNumber: number | null; - [0, null].filter(x => nullOrNumber); - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableNumber", Line: 3}, - }, /* Suggestions: conditionFixCompareNullish, conditionFixDefaultZero, conditionFixCastBoolean, explicitBooleanReturnType */ - }, - { - Code: ` - const objectValue: object = {}; - [{ a: 0 }, {}].filter(x => objectValue); - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObject", Line: 3}, - }, /* Suggestions: explicitBooleanReturnType */ - }, - { - Code: ` - const objectValue: object = {}; - [{ a: 0 }, {}].filter(x => { - return objectValue; - }); - `, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedObject", Line: 3}, - }, /* Suggestions: explicitBooleanReturnType */ - }, - { - Code: ` - declare const nullOrObject: object | null; - [{ a: 0 }, null].filter(x => nullOrObject); - `, - Options: StrictBooleanExpressionsOptions{ - AllowNullableObject: utils.Ref(false), + }, + { + Code: ` +[1, null].every((x, i) => {}); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "explicitBooleanReturnType", +// Output: ` +// [1, null].every((x, i): boolean => {}); +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +[() => {}, null].every((x: () => void) => {}); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "explicitBooleanReturnType", +// Output: ` +// [() => {}, null].every((x: () => void): boolean => {}); +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +[() => {}, null].every(function (x: () => void) {}); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "explicitBooleanReturnType", +// Output: ` +// [() => {}, null].every(function (x: () => void): boolean {}); +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +[() => {}, null].every(() => {}); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "explicitBooleanReturnType", +// Output: ` +// [() => {}, null].every((): boolean => {}); +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +declare function f(x: number): string; +declare function f(x: string | null): boolean; + +[35].filter(f); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorOther", }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNullableObject", Line: 3}, - }, /* Suggestions: conditionFixCompareNullish, explicitBooleanReturnType */ }, - { - Code: ` - const numbers: number[] = [1]; - [1, 2].filter(x => numbers.length); - `, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), + }, + { + Code: ` +declare function f(x: number): string; +declare function f(x: number | boolean): boolean; +declare function f(x: string | null): boolean; + +[35].filter(f); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorOther", }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 3}, - }, /* Suggestions: conditionFixCompareArrayLengthNonzero, explicitBooleanReturnType */ }, - { - Code: ` - const numberValue: number = 1; - [1, 2].filter(x => numberValue); - `, - Options: StrictBooleanExpressionsOptions{ - AllowNumber: utils.Ref(false), + }, + { + Code: ` +declare function foo(x: number): T; +[1, null].every(foo); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorAny", }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedNumber", Line: 3}, - }, /* Suggestions: conditionFixCompareZero, conditionFixCompareNaN, conditionFixCastBoolean, explicitBooleanReturnType */ }, - { - Code: ` - const stringValue: string = 'hoge'; - ['hoge', 'foo'].filter(x => stringValue); - `, - Options: StrictBooleanExpressionsOptions{ - AllowString: utils.Ref(false), + }, + { + Code: ` +function foo(x: number): T {} +[1, null].every(foo); + `, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", }, - Errors: []rule_tester.InvalidTestCaseError{ - {MessageId: "unexpectedString", Line: 3}, - }, /* Suggestions: conditionFixCompareStringLength, conditionFixCompareEmptyString, conditionFixCastBoolean, explicitBooleanReturnType */ }, - }) + }, + { + Code: ` +declare const nullOrString: string | null; +['one', null].filter(x => nullOrString); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableString", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareNullish", +// Output: ` +// declare const nullOrString: string | null; +// ['one', null].filter(x => nullOrString != null); +// `, +// }, +// { +// MessageId: "conditionFixDefaultEmptyString", +// Output: ` +// declare const nullOrString: string | null; +// ['one', null].filter(x => nullOrString ?? ""); +// `, +// }, +// { +// MessageId: "conditionFixCastBoolean", +// Output: ` +// declare const nullOrString: string | null; +// ['one', null].filter(x => Boolean(nullOrString)); +// `, +// }, +// { +// MessageId: "explicitBooleanReturnType", +// Output: ` +// declare const nullOrString: string | null; +// ['one', null].filter((x): boolean => nullOrString); +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +declare const nullOrString: string | null; +['one', null].filter(x => !nullOrString); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableString", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareNullish", +// Output: ` +// declare const nullOrString: string | null; +// ['one', null].filter(x => nullOrString == null); +// `, +// }, +// { +// MessageId: "conditionFixDefaultEmptyString", +// Output: ` +// declare const nullOrString: string | null; +// ['one', null].filter(x => !(nullOrString ?? "")); +// `, +// }, +// { +// MessageId: "conditionFixCastBoolean", +// Output: ` +// declare const nullOrString: string | null; +// ['one', null].filter(x => !Boolean(nullOrString)); +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +declare const anyValue: any; +['one', null].filter(x => anyValue); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorAny", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCastBoolean", +// Output: ` +// declare const anyValue: any; +// ['one', null].filter(x => Boolean(anyValue)); +// `, +// }, +// { +// MessageId: "explicitBooleanReturnType", +// Output: ` +// declare const anyValue: any; +// ['one', null].filter((x): boolean => anyValue); +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +declare const nullOrBoolean: boolean | null; +[true, null].filter(x => nullOrBoolean); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableBoolean", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixDefaultFalse", +// Output: ` +// declare const nullOrBoolean: boolean | null; +// [true, null].filter(x => nullOrBoolean ?? false); +// `, +// }, +// { +// MessageId: "conditionFixCompareTrue", +// Output: ` +// declare const nullOrBoolean: boolean | null; +// [true, null].filter(x => nullOrBoolean === true); +// `, +// }, +// { +// MessageId: "explicitBooleanReturnType", +// Output: ` +// declare const nullOrBoolean: boolean | null; +// [true, null].filter((x): boolean => nullOrBoolean); +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +enum ExampleEnum { + This = 0, + That = 1, +} +const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; +[0, 1].filter(x => theEnum); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableEnum", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareNullish", +// Output: ` +// enum ExampleEnum { +// This = 0, +// That = 1, +// } +// const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; +// [0, 1].filter(x => theEnum != null); +// `, +// }, +// { +// MessageId: "explicitBooleanReturnType", +// Output: ` +// enum ExampleEnum { +// This = 0, +// That = 1, +// } +// const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; +// [0, 1].filter((x): boolean => theEnum); +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +declare const nullOrNumber: number | null; +[0, null].filter(x => nullOrNumber); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableNumber", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareNullish", +// Output: ` +// declare const nullOrNumber: number | null; +// [0, null].filter(x => nullOrNumber != null); +// `, +// }, +// { +// MessageId: "conditionFixDefaultZero", +// Output: ` +// declare const nullOrNumber: number | null; +// [0, null].filter(x => nullOrNumber ?? 0); +// `, +// }, +// { +// MessageId: "conditionFixCastBoolean", +// Output: ` +// declare const nullOrNumber: number | null; +// [0, null].filter(x => Boolean(nullOrNumber)); +// `, +// }, +// { +// MessageId: "explicitBooleanReturnType", +// Output: ` +// declare const nullOrNumber: number | null; +// [0, null].filter((x): boolean => nullOrNumber); +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +const objectValue: object = {}; +[{ a: 0 }, {}].filter(x => objectValue); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorObject", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "explicitBooleanReturnType", +// Output: ` +// const objectValue: object = {}; +// [{ a: 0 }, {}].filter((x): boolean => objectValue); +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +const objectValue: object = {}; +[{ a: 0 }, {}].filter(x => { + return objectValue; +}); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorObject", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "explicitBooleanReturnType", +// Output: ` +// const objectValue: object = {}; +// [{ a: 0 }, {}].filter((x): boolean => { +// return objectValue; +// }); +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +declare const nullOrObject: object | null; +[{ a: 0 }, null].filter(x => nullOrObject); + `, + Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableObject", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareNullish", +// Output: ` +// declare const nullOrObject: object | null; +// [{ a: 0 }, null].filter(x => nullOrObject != null); +// `, +// }, +// { +// MessageId: "explicitBooleanReturnType", +// Output: ` +// declare const nullOrObject: object | null; +// [{ a: 0 }, null].filter((x): boolean => nullOrObject); +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +const numbers: number[] = [1]; +[1, 2].filter(x => numbers.length); + `, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareArrayLengthNonzero", +// Output: ` +// const numbers: number[] = [1]; +// [1, 2].filter(x => numbers.length > 0); +// `, +// }, +// { +// MessageId: "explicitBooleanReturnType", +// Output: ` +// const numbers: number[] = [1]; +// [1, 2].filter((x): boolean => numbers.length); +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +const numberValue: number = 1; +[1, 2].filter(x => numberValue); + `, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareZero", +// Output: ` +// const numberValue: number = 1; +// [1, 2].filter(x => numberValue !== 0); +// `, +// }, +// { +// MessageId: "conditionFixCompareNaN", +// Output: ` +// const numberValue: number = 1; +// [1, 2].filter(x => !Number.isNaN(numberValue)); +// `, +// }, +// { +// MessageId: "conditionFixCastBoolean", +// Output: ` +// const numberValue: number = 1; +// [1, 2].filter(x => Boolean(numberValue)); +// `, +// }, +// { +// MessageId: "explicitBooleanReturnType", +// Output: ` +// const numberValue: number = 1; +// [1, 2].filter((x): boolean => numberValue); +// `, +// }, +// }, + }, + }, + }, + { + Code: ` +const stringValue: string = 'hoge'; +['hoge', 'foo'].filter(x => stringValue); + `, + Options: StrictBooleanExpressionsOptions{AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorString", +// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ +// { +// MessageId: "conditionFixCompareStringLength", +// Output: ` +// const stringValue: string = 'hoge'; +// ['hoge', 'foo'].filter(x => stringValue.length > 0); +// `, +// }, +// { +// MessageId: "conditionFixCompareEmptyString", +// Output: ` +// const stringValue: string = 'hoge'; +// ['hoge', 'foo'].filter(x => stringValue !== ""); +// `, +// }, +// { +// MessageId: "conditionFixCastBoolean", +// Output: ` +// const stringValue: string = 'hoge'; +// ['hoge', 'foo'].filter(x => Boolean(stringValue)); +// `, +// }, +// { +// MessageId: "explicitBooleanReturnType", +// Output: ` +// const stringValue: string = 'hoge'; +// ['hoge', 'foo'].filter((x): boolean => stringValue); +// `, +// }, +// }, + }, + }, + }, + }) } From b7dfa1dc905abae341e0ae3a925d71ffc3d7fab6 Mon Sep 17 00:00:00 2001 From: Cameron Clark Date: Thu, 23 Oct 2025 13:44:36 +0100 Subject: [PATCH 25/27] fix edge cases --- .../strict_boolean_expressions.go | 154 +- .../strict_boolean_expressions_test.go | 5490 ++++++++--------- 2 files changed, 2832 insertions(+), 2812 deletions(-) diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go index 44638eb1..928c59a1 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go @@ -20,91 +20,91 @@ type StrictBooleanExpressionsOptions struct { AllowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing *bool } -func buildUnexpectedAny() rule.RuleMessage { +func buildConditionErrorNumberMessage() rule.RuleMessage { return rule.RuleMessage{ - Id: "unexpectedAny", - Description: "Unexpected any value in conditional. An explicit comparison or type cast is required.", + Id: "conditionErrorNumber", + Description: "Unexpected number value in conditional. A number can be falsy (0, NaN) or truthy.", } } -func buildUnexpectedNullableBoolean() rule.RuleMessage { +func buildConditionErrorStringMessage() rule.RuleMessage { return rule.RuleMessage{ - Id: "unexpectedNullableBoolean", - Description: "Unexpected nullable boolean value in conditional. Please handle the nullish case explicitly.", + Id: "conditionErrorString", + Description: "Unexpected string value in conditional. A string can be falsy (empty string) or truthy.", } } -func buildUnexpectedNullableString() rule.RuleMessage { +func buildConditionErrorObjectMessage() rule.RuleMessage { return rule.RuleMessage{ - Id: "unexpectedNullableString", - Description: "Unexpected nullable string value in conditional. Please handle the nullish case explicitly.", + Id: "conditionErrorObject", + Description: "Unexpected object value in conditional. An object is always truthy.", } } -func buildUnexpectedNullableNumber() rule.RuleMessage { +func buildConditionErrorNullishMessage() rule.RuleMessage { return rule.RuleMessage{ - Id: "unexpectedNullableNumber", - Description: "Unexpected nullable number value in conditional. Please handle the nullish case explicitly.", + Id: "conditionErrorNullish", + Description: "Unexpected nullish value in conditional. The expression is always falsy.", } } -func buildUnexpectedNullableEnum() rule.RuleMessage { +func buildConditionErrorOtherMessage() rule.RuleMessage { return rule.RuleMessage{ - Id: "unexpectedNullableEnum", - Description: "Unexpected nullable enum value in conditional. Please handle the nullish case explicitly.", + Id: "conditionErrorOther", + Description: "Unexpected value in conditional. A union of different types has inconsistent truthiness.", } } -func buildUnexpectedNullableObject() rule.RuleMessage { +func buildConditionErrorNullableBooleanMessage() rule.RuleMessage { return rule.RuleMessage{ - Id: "unexpectedNullableObject", - Description: "Unexpected nullable object value in conditional. An explicit null check is required.", + Id: "conditionErrorNullableBoolean", + Description: "Unexpected nullable boolean value in conditional. Please handle the nullish case explicitly.", } } -func buildUnexpectedNullish() rule.RuleMessage { +func buildConditionErrorNullableObjectMessage() rule.RuleMessage { return rule.RuleMessage{ - Id: "unexpectedNullish", - Description: "Unexpected nullish value in conditional. An explicit null check is required.", + Id: "conditionErrorNullableObject", + Description: "Unexpected nullable object value in conditional. Please handle the nullish case explicitly.", } } -func buildUnexpectedString() rule.RuleMessage { +func buildConditionErrorNullableStringMessage() rule.RuleMessage { return rule.RuleMessage{ - Id: "unexpectedString", - Description: "Unexpected string value in conditional. An explicit empty string check is required.", + Id: "conditionErrorNullableString", + Description: "Unexpected nullable string value in conditional. Please handle the nullish and empty string cases explicitly.", } } -func buildUnexpectedNumber() rule.RuleMessage { +func buildConditionErrorNullableNumberMessage() rule.RuleMessage { return rule.RuleMessage{ - Id: "unexpectedNumber", - Description: "Unexpected number value in conditional. An explicit zero/NaN check is required.", + Id: "conditionErrorNullableNumber", + Description: "Unexpected nullable number value in conditional. Please handle the nullish and zero cases explicitly.", } } -func buildUnexpectedObject() rule.RuleMessage { +func buildConditionErrorNullableEnumMessage() rule.RuleMessage { return rule.RuleMessage{ - Id: "unexpectedObject", - Description: "Unexpected object value in conditional. The condition is always true.", + Id: "conditionErrorNullableEnum", + Description: "Unexpected nullable enum value in conditional. Please handle the nullish and falsy enum cases explicitly.", } } -func buildUnexpectedMixedCondition() rule.RuleMessage { +func buildConditionErrorAnyMessage() rule.RuleMessage { return rule.RuleMessage{ - Id: "unexpectedMixedCondition", - Description: "Unexpected mixed type in conditional. The constituent types do not have a best common type.", + Id: "conditionErrorAny", + Description: "Unexpected any value in conditional. Use a more specific type to ensure type safety.", } } -func buildNoStrictNullCheck() rule.RuleMessage { +func buildNoStrictNullCheckMessage() rule.RuleMessage { return rule.RuleMessage{ Id: "noStrictNullCheck", Description: "This rule requires the `strictNullChecks` compiler option to be turned on to function correctly.", } } -func buildPredicateCannotBeAsync() rule.RuleMessage { +func buildPredicateCannotBeAsyncMessage() rule.RuleMessage { return rule.RuleMessage{ Id: "predicateCannotBeAsync", Description: "Predicate function should not be 'async'; expected a boolean return type.", @@ -154,7 +154,7 @@ var StrictBooleanExpressionsRule = rule.Rule{ !utils.IsStrictCompilerOptionEnabled(compilerOptions, compilerOptions.StrictNullChecks) { ctx.ReportRange( core.NewTextRange(0, 0), - buildNoStrictNullCheck(), + buildNoStrictNullCheckMessage(), ) } @@ -203,7 +203,7 @@ var StrictBooleanExpressionsRule = rule.Rule{ } isFunction := arg.Kind&(ast.KindArrowFunction|ast.KindFunctionExpression|ast.KindFunctionDeclaration) != 0 if isFunction && checker.GetFunctionFlags(arg)&checker.FunctionFlagsAsync != 0 { - ctx.ReportNode(arg, buildPredicateCannotBeAsync()) + ctx.ReportNode(arg, buildPredicateCannotBeAsyncMessage()) return } funcType := ctx.TypeChecker.GetTypeAtLocation(arg) @@ -246,15 +246,63 @@ func findTruthinessAssertedArgument(typeChecker *checker.Checker, callExpr *ast. if len(checkableArguments) == 0 { return nil } + + calleeType := typeChecker.GetTypeAtLocation(callExpr.Expression) + if calleeType == nil { + return nil + } + + unionTypes := utils.UnionTypeParts(calleeType) + isUnionType := len(unionTypes) > 1 + node := callExpr.AsNode() signature := typeChecker.GetResolvedSignature(node) + if signature == nil { + if !isUnionType { + return nil + } + + for _, t := range unionTypes { + signatures := typeChecker.GetCallSignatures(t) + for _, sig := range signatures { + typePredicate := typeChecker.GetTypePredicateOfSignature(sig) + if typePredicate != nil && + checker.TypePredicate_kind(typePredicate) == checker.TypePredicateKindAssertsIdentifier && + checker.TypePredicate_t(typePredicate) == nil { + parameterIndex := checker.TypePredicate_parameterIndex(typePredicate) + if int(parameterIndex) < len(checkableArguments) { + return checkableArguments[parameterIndex] + } + } + } + } return nil } + firstTypePredicateResult := typeChecker.GetTypePredicateOfSignature(signature) if firstTypePredicateResult == nil { + if !isUnionType { + return nil + } + + for _, t := range unionTypes { + signatures := typeChecker.GetCallSignatures(t) + for _, sig := range signatures { + typePredicate := typeChecker.GetTypePredicateOfSignature(sig) + if typePredicate != nil && + checker.TypePredicate_kind(typePredicate) == checker.TypePredicateKindAssertsIdentifier && + checker.TypePredicate_t(typePredicate) == nil { + parameterIndex := checker.TypePredicate_parameterIndex(typePredicate) + if int(parameterIndex) < len(checkableArguments) { + return checkableArguments[parameterIndex] + } + } + } + } return nil } + if !(checker.TypePredicate_kind(firstTypePredicateResult) == checker.TypePredicateKindAssertsIdentifier && checker.TypePredicate_t(firstTypePredicateResult) == nil) { return nil @@ -347,7 +395,7 @@ func analyzeTypeParts(typeChecker *checker.Checker, types []*checker.Type) typeI if partInfo.isEnum { info.isEnum = true } - if partInfo.variant == typeVariantBoolean || partInfo.variant == typeVariantNumber || partInfo.variant == typeVariantString { + if partInfo.variant == typeVariantBoolean || partInfo.variant == typeVariantNumber || partInfo.variant == typeVariantString || partInfo.variant == typeVariantBigInt { if metNotTruthy { continue } @@ -492,13 +540,13 @@ func checkCondition(ctx rule.RuleContext, node *ast.Node, types []*checker.Type, switch info.variant { case typeVariantAny, typeVariantUnknown, typeVariantGeneric: if !*opts.AllowAny { - ctx.ReportNode(node, buildUnexpectedAny()) + ctx.ReportNode(node, buildConditionErrorAnyMessage()) } return case typeVariantNever: return case typeVariantNullish: - ctx.ReportNode(node, buildUnexpectedNullish()) + ctx.ReportNode(node, buildConditionErrorNullishMessage()) case typeVariantString: // Known edge case: truthy primitives and nullish values are always valid boolean expressions if *opts.AllowString && info.isTruthy { @@ -508,15 +556,15 @@ func checkCondition(ctx rule.RuleContext, node *ast.Node, types []*checker.Type, if info.isNullable { if info.isEnum { if !*opts.AllowNullableEnum { - ctx.ReportNode(node, buildUnexpectedNullableEnum()) + ctx.ReportNode(node, buildConditionErrorNullableEnumMessage()) } } else { if !*opts.AllowNullableString { - ctx.ReportNode(node, buildUnexpectedNullableString()) + ctx.ReportNode(node, buildConditionErrorNullableStringMessage()) } } } else if !*opts.AllowString { - ctx.ReportNode(node, buildUnexpectedString()) + ctx.ReportNode(node, buildConditionErrorStringMessage()) } case typeVariantNumber: // Known edge case: truthy primitives and nullish values are always valid boolean expressions @@ -527,15 +575,15 @@ func checkCondition(ctx rule.RuleContext, node *ast.Node, types []*checker.Type, if info.isNullable { if info.isEnum { if !*opts.AllowNullableEnum { - ctx.ReportNode(node, buildUnexpectedNullableEnum()) + ctx.ReportNode(node, buildConditionErrorNullableEnumMessage()) } } else { if !*opts.AllowNullableNumber { - ctx.ReportNode(node, buildUnexpectedNullableNumber()) + ctx.ReportNode(node, buildConditionErrorNullableNumberMessage()) } } } else if !*opts.AllowNumber { - ctx.ReportNode(node, buildUnexpectedNumber()) + ctx.ReportNode(node, buildConditionErrorNumberMessage()) } case typeVariantBoolean: // Known edge case: truthy primitives and nullish values are always valid boolean expressions @@ -544,27 +592,27 @@ func checkCondition(ctx rule.RuleContext, node *ast.Node, types []*checker.Type, } if info.isNullable && !*opts.AllowNullableBoolean { - ctx.ReportNode(node, buildUnexpectedNullableBoolean()) + ctx.ReportNode(node, buildConditionErrorNullableBooleanMessage()) } case typeVariantObject: if info.isNullable && !*opts.AllowNullableObject { - ctx.ReportNode(node, buildUnexpectedNullableObject()) + ctx.ReportNode(node, buildConditionErrorNullableObjectMessage()) } else if !info.isNullable { - ctx.ReportNode(node, buildUnexpectedObject()) + ctx.ReportNode(node, buildConditionErrorObjectMessage()) } case typeVariantMixed: if info.isEnum { if info.isNullable && !*opts.AllowNullableEnum { - ctx.ReportNode(node, buildUnexpectedNullableEnum()) + ctx.ReportNode(node, buildConditionErrorNullableEnumMessage()) } return } - ctx.ReportNode(node, buildUnexpectedMixedCondition()) + ctx.ReportNode(node, buildConditionErrorOtherMessage()) case typeVariantBigInt: if info.isNullable && !*opts.AllowNullableNumber { - ctx.ReportNode(node, buildUnexpectedNullableNumber()) + ctx.ReportNode(node, buildConditionErrorNullableNumberMessage()) } else if !info.isNullable && !*opts.AllowNumber { - ctx.ReportNode(node, buildUnexpectedNumber()) + ctx.ReportNode(node, buildConditionErrorNumberMessage()) } } } diff --git a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go index f6503295..da7a7a94 100644 --- a/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go @@ -55,207 +55,206 @@ func TestStrictBooleanExpressionsRule(t *testing.T) { Code: `(false && 1) || (true && 2);`, }, { - Code: - ` + Code: ` declare const x: boolean; if (x) { } `, }, - { + { Code: `(x: boolean) => !x;`, }, - { + { Code: `(x: T) => (x ? 1 : 0);`, }, - { + { Code: ` declare const x: never; if (x) { } `, }, - { + { Code: ` if ('') { } `, }, - { + { Code: `while ('x') {}`, }, - { + { Code: `for (; ''; ) {}`, }, - { + { Code: `('' && '1') || x;`, }, - { + { Code: ` declare const x: string; if (x) { } `, }, - { + { Code: `(x: string) => !x;`, }, - { + { Code: `(x: T) => (x ? 1 : 0);`, }, - { + { Code: ` if (0) { } `, }, - { + { Code: `while (1n) {}`, }, - { + { Code: `for (; Infinity; ) {}`, }, - { + { Code: `(0 / 0 && 1 + 2) || x;`, }, - { + { Code: ` declare const x: number; if (x) { } `, }, - { + { Code: `(x: bigint) => !x;`, }, - { + { Code: `(x: T) => (x ? 1 : 0);`, }, - { + { Code: ` declare const x: null | object; if (x) { } `, }, - { + { Code: `(x?: { a: any }) => !x;`, }, - { + { Code: `(x: T) => (x ? 1 : 0);`, }, - { + { Code: ` declare const x: boolean | null; if (x) { } `, Options: StrictBooleanExpressionsOptions{AllowNullableBoolean: utils.Ref(true)}}, - { + { Code: ` (x?: boolean) => !x; `, Options: StrictBooleanExpressionsOptions{AllowNullableBoolean: utils.Ref(true)}}, - { + { Code: ` (x: T) => (x ? 1 : 0); `, Options: StrictBooleanExpressionsOptions{AllowNullableBoolean: utils.Ref(true)}}, - { + { Code: ` const a: (undefined | boolean | null)[] = [true, undefined, null]; a.some(x => x); `, Options: StrictBooleanExpressionsOptions{AllowNullableBoolean: utils.Ref(true)}}, - { + { Code: ` declare const x: string | null; if (x) { } `, Options: StrictBooleanExpressionsOptions{AllowNullableString: utils.Ref(true)}}, - { + { Code: ` (x?: string) => !x; `, Options: StrictBooleanExpressionsOptions{AllowNullableString: utils.Ref(true)}, - }, - { + }, + { Code: ` (x: T) => (x ? 1 : 0); `, Options: StrictBooleanExpressionsOptions{AllowNullableString: utils.Ref(true)}}, - { + { Code: ` declare const x: number | null; if (x) { } `, Options: StrictBooleanExpressionsOptions{AllowNullableNumber: utils.Ref(true)}}, - { + { Code: ` (x?: number) => !x; `, Options: StrictBooleanExpressionsOptions{AllowNullableNumber: utils.Ref(true)}}, - { + { Code: ` (x: T) => (x ? 1 : 0); `, Options: StrictBooleanExpressionsOptions{AllowNullableNumber: utils.Ref(true)}}, - { + { Code: ` declare const arrayOfArrays: (null | unknown[])[]; const isAnyNonEmptyArray1 = arrayOfArrays.some(array => array?.length); `, Options: StrictBooleanExpressionsOptions{AllowNullableNumber: utils.Ref(true)}}, - { + { Code: ` declare const x: any; if (x) { } `, Options: StrictBooleanExpressionsOptions{AllowAny: utils.Ref(true)}}, - { + { Code: ` x => !x; `, Options: StrictBooleanExpressionsOptions{AllowAny: utils.Ref(true)}}, - { + { Code: ` (x: T) => (x ? 1 : 0); `, Options: StrictBooleanExpressionsOptions{AllowAny: utils.Ref(true)}}, - { + { Code: ` declare const arrayOfArrays: any[]; const isAnyNonEmptyArray1 = arrayOfArrays.some(array => array); `, Options: StrictBooleanExpressionsOptions{AllowAny: utils.Ref(true)}}, - { + { Code: ` 1 && true && 'x' && {}; `, Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(true), AllowString: utils.Ref(true)}}, - { + { Code: ` let x = 0 || false || '' || null; `, Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(true), AllowString: utils.Ref(true)}}, - { + { Code: ` if (1 && true && 'x') void 0; `, Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(true), AllowString: utils.Ref(true)}}, - { + { Code: ` if (0 || false || '') void 0; `, Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(true), AllowString: utils.Ref(true)}}, - { + { Code: ` 1 && true && 'x' ? {} : null; `, Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(true), AllowString: utils.Ref(true)}}, - { + { Code: ` 0 || false || '' ? null : {}; `, Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(true), AllowString: utils.Ref(true)}}, - { + { Code: ` declare const arrayOfArrays: string[]; const isAnyNonEmptyArray1 = arrayOfArrays.some(array => array); `, Options: StrictBooleanExpressionsOptions{AllowString: utils.Ref(true)}}, - { + { Code: ` declare const arrayOfArrays: number[]; const isAnyNonEmptyArray1 = arrayOfArrays.some(array => array); `, Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(true)}}, - { + { Code: ` declare const arrayOfArrays: (null | object)[]; const isAnyNonEmptyArray1 = arrayOfArrays.some(array => array); `, Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(true)}}, - { + { Code: ` enum ExampleEnum { This = 0, @@ -269,7 +268,7 @@ if (x) { if (theEnum) { } `, Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(true)}}, - { + { Code: ` enum ExampleEnum { This = 0, @@ -283,7 +282,7 @@ if (x) { if (!theEnum) { } `, Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(true)}}, - { + { Code: ` enum ExampleEnum { This = 1, @@ -297,7 +296,7 @@ if (x) { if (!theEnum) { } `, Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(true)}}, - { + { Code: ` enum ExampleEnum { This = 'one', @@ -311,7 +310,7 @@ if (x) { if (!theEnum) { } `, Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(true)}}, - { + { Code: ` enum ExampleEnum { This = 0, @@ -319,7 +318,7 @@ if (x) { } (value?: ExampleEnum) => (value ? 1 : 0); `, Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(true)}}, - { + { Code: ` enum ExampleEnum { This = '', @@ -327,7 +326,7 @@ if (x) { } (value?: ExampleEnum) => (!value ? 1 : 0); `, Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(true)}}, - { + { Code: ` enum ExampleEnum { This = 'this', @@ -335,7 +334,7 @@ if (x) { } (value?: ExampleEnum) => (!value ? 1 : 0); `, Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(true)}}, - { + { Code: ` enum ExampleEnum { This = '', @@ -343,7 +342,7 @@ if (x) { } (value?: ExampleEnum) => (!value ? 1 : 0); `, Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(true)}}, - { + { Code: ` enum ExampleEnum { This = '', @@ -352,28 +351,28 @@ if (x) { declare const arrayOfArrays: (ExampleEnum | null)[]; const isAnyNonEmptyArray1 = arrayOfArrays.some(array => array); `, Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(true)}}, - { + { Code: ` declare const x: string[] | null; // eslint-disable-next-line if (x) { } `, Options: StrictBooleanExpressionsOptions{AllowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: utils.Ref(true)}}, - { + { Code: ` function f(arg: 'a' | null) { if (arg) console.log(arg); } `, }, - { + { Code: ` function f(arg: 'a' | 'b' | null) { if (arg) console.log(arg); } `, }, - { + { Code: ` declare const x: 1 | null; declare const y: 1; @@ -382,21 +381,21 @@ if (x) { if (y) { } `, Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(true)}}, - { + { Code: ` function f(arg: 1 | null) { if (arg) console.log(arg); } `, }, - { + { Code: ` function f(arg: 1 | 2 | null) { if (arg) console.log(arg); } `, }, - { + { Code: ` interface Options { readonly enableSomething?: true; @@ -407,14 +406,14 @@ function f(opts: Options): void { } `, }, - { + { Code: ` declare const x: true | null; if (x) { } `, }, - { + { Code: ` declare const x: 'a' | null; declare const y: 'a'; @@ -423,28 +422,28 @@ if (x) { if (y) { } `, Options: StrictBooleanExpressionsOptions{AllowString: utils.Ref(true)}}, - { + { Code: ` declare const foo: boolean & { __BRAND: 'Foo' }; if (foo) { } `, }, - { + { Code: ` declare const foo: true & { __BRAND: 'Foo' }; if (foo) { } `, }, - { + { Code: ` declare const foo: false & { __BRAND: 'Foo' }; if (foo) { } `, }, - { + { Code: ` declare function assert(a: number, b: unknown): asserts a; declare const nullableString: string | null; @@ -452,7 +451,7 @@ declare const boo: boolean; assert(boo, nullableString); `, }, - { + { Code: ` declare function assert(a: boolean, b: unknown): asserts b is string; declare const nullableString: string | null; @@ -460,7 +459,7 @@ declare const boo: boolean; assert(boo, nullableString); `, }, - { + { Code: ` declare function assert(a: number, b: unknown): asserts b; declare const nullableString: string | null; @@ -468,7 +467,7 @@ declare const boo: boolean; assert(nullableString, boo); `, }, - { + { Code: ` declare function assert(a: number, b: unknown): asserts b; declare const nullableString: string | null; @@ -476,7 +475,7 @@ declare const boo: boolean; assert(...nullableString, nullableString); `, }, - { + { Code: ` declare function assert( this: object, @@ -492,14 +491,14 @@ const o: { assert: typeof assert } = { o.assert(foo, nullableString); `, }, - { + { Code: ` declare function assert(x: unknown): x is string; declare const nullableString: string | null; assert(nullableString); `, }, - { + { Code: ` class ThisAsserter { assertThis(this: unknown, arg2: unknown): asserts this {} @@ -511,7 +510,7 @@ const thisAsserter: ThisAsserter = new ThisAsserter(); thisAsserter.assertThis(lol); `, }, - { + { Code: ` function assert(this: object, a: number, b: unknown): asserts b; function assert(a: bigint, b: unknown): asserts b; @@ -533,63 +532,63 @@ declare const nullableString: string | null; assert(3 as any, nullableString); `, }, - { + { Code: ` declare const assert: any; declare const nullableString: string | null; assert(nullableString); `, }, - { + { Code: ` for (let x = 0; ; x++) { break; } `, }, - { + { Code: ` [true, false].some(function (x) { return x; }); `, }, - { + { Code: ` [true, false].some(function check(x) { return x; }); `, }, - { + { Code: ` [true, false].some(x => { return x; }); `, }, - { + { Code: ` [1, null].filter(function (x) { return x != null; }); `, }, - { + { Code: ` ['one', 'two', ''].filter(function (x) { return !!x; }); `, }, - { + { Code: ` ['one', 'two', ''].filter(function (x): boolean { return !!x; }); `, }, - { + { Code: ` ['one', 'two', ''].filter(function (x): boolean { if (x) { @@ -598,7 +597,7 @@ assert(nullableString); }); `, }, - { + { Code: ` ['one', 'two', ''].filter(function (x): boolean { if (x) { @@ -609,31 +608,31 @@ assert(nullableString); }); `, }, - { + { Code: ` declare const predicate: (string) => boolean; ['one', 'two', ''].filter(predicate); `, }, - { + { Code: ` declare function notNullish(x: T): x is NonNullable; ['one', null].filter(notNullish); `, }, - { + { Code: ` declare function predicate(x: string | null): x is string; ['one', null].filter(predicate); `, }, - { + { Code: ` declare function predicate(x: string | null): T; ['one', null].filter(predicate); `, }, - { + { Code: ` declare function f(x: number): boolean; declare function f(x: string | null): boolean; @@ -641,1602 +640,1574 @@ declare function f(x: string | null): boolean; [35].filter(f); `, }, - }, []rule_tester.InvalidTestCase{ - { - Code: ` + }, []rule_tester.InvalidTestCase{ + { + Code: ` if (true && 1 + 1) { } `, - Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNumber", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareZero", -// Output: ` -// if (true && ((1 + 1) !== 0)) { -// } -// `, -// }, -// { -// MessageId: "conditionFixCompareNaN", -// Output: ` -// if (true && (!Number.isNaN((1 + 1)))) { -// } -// `, -// }, -// { -// MessageId: "conditionFixCastBoolean", -// Output: ` -// if (true && (Boolean((1 + 1)))) { -// } -// `, -// }, -// }, + Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: ` + // if (true && ((1 + 1) !== 0)) { + // } + // `, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: ` + // if (true && (!Number.isNaN((1 + 1)))) { + // } + // `, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: ` + // if (true && (Boolean((1 + 1)))) { + // } + // `, + // }, + // }, + }, }, }, - }, - { - Code: `while (false || 'a' + 'b') {}`, - Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorString", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareStringLength", - // Output: `while (false || (('a' + 'b').length > 0)) {}`, - // }, - // { - // MessageId: "conditionFixCompareEmptyString", - // Output: `while (false || (('a' + 'b') !== "")) {}`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `while (false || (Boolean(('a' + 'b')))) {}`, - // }, - // }, + { + Code: `while (false || 'a' + 'b') {}`, + Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `while (false || (('a' + 'b').length > 0)) {}`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `while (false || (('a' + 'b') !== "")) {}`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `while (false || (Boolean(('a' + 'b')))) {}`, + // }, + // }, + }, }, }, - }, - { - Code: `(x: object) => (true || false || x ? true : false);`, - Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorObject", + { + Code: `(x: object) => (true || false || x ? true : false);`, + Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorObject", + }, }, }, - }, - { - Code: `if (('' && {}) || (0 && void 0)) { }`, - Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorString", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareStringLength", - // Output: `if (((''.length > 0) && {}) || (0 && void 0)) { }`, - // }, - // { - // MessageId: "conditionFixCompareEmptyString", - // Output: `if ((('' !== "") && {}) || (0 && void 0)) { }`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `if (((Boolean('')) && {}) || (0 && void 0)) { }`, - // }, - // }, - }, - { - MessageId: "conditionErrorObject", - }, - { - MessageId: "conditionErrorNumber", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareZero", - // Output: `if (('' && {}) || ((0 !== 0) && void 0)) { }`, - // }, - // { - // MessageId: "conditionFixCompareNaN", - // Output: `if (('' && {}) || ((!Number.isNaN(0)) && void 0)) { }`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `if (('' && {}) || ((Boolean(0)) && void 0)) { }`, - // }, - // }, - }, - { - MessageId: "conditionErrorNullish", + { + Code: `if (('' && {}) || (0 && void 0)) { }`, + Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `if (((''.length > 0) && {}) || (0 && void 0)) { }`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `if ((('' !== "") && {}) || (0 && void 0)) { }`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `if (((Boolean('')) && {}) || (0 && void 0)) { }`, + // }, + // }, + }, + { + MessageId: "conditionErrorObject", + }, + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `if (('' && {}) || ((0 !== 0) && void 0)) { }`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `if (('' && {}) || ((!Number.isNaN(0)) && void 0)) { }`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `if (('' && {}) || ((Boolean(0)) && void 0)) { }`, + // }, + // }, + }, + { + MessageId: "conditionErrorNullish", + }, }, }, - }, - { - Code: ` + { + Code: ` declare const array: string[]; array.some(x => x); `, - Options: StrictBooleanExpressionsOptions{AllowNullableBoolean: utils.Ref(true), AllowString: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorString", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareStringLength", - // Output: ` - // declare const array: string[]; - // array.some(x => x.length > 0); - // `, - // }, - // { - // MessageId: "conditionFixCompareEmptyString", - // Output: ` - // declare const array: string[]; - // array.some(x => x !== ""); - // `, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: ` - // declare const array: string[]; - // array.some(x => Boolean(x)); - // `, - // }, - // { - // MessageId: "explicitBooleanReturnType", - // Output: ` - // declare const array: string[]; - // array.some((x): boolean => x); - // `, - // }, - // }, + Options: StrictBooleanExpressionsOptions{AllowNullableBoolean: utils.Ref(true), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: ` + // declare const array: string[]; + // array.some(x => x.length > 0); + // `, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: ` + // declare const array: string[]; + // array.some(x => x !== ""); + // `, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: ` + // declare const array: string[]; + // array.some(x => Boolean(x)); + // `, + // }, + // { + // MessageId: "explicitBooleanReturnType", + // Output: ` + // declare const array: string[]; + // array.some((x): boolean => x); + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` declare const foo: true & { __BRAND: 'Foo' }; if (('' && foo) || (0 && void 0)) { } `, - Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorString", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareStringLength", -// Output: ` -// declare const foo: true & { __BRAND: 'Foo' }; -// if (((''.length > 0) && foo) || (0 && void 0)) { } -// `, -// }, -// { -// MessageId: "conditionFixCompareEmptyString", -// Output: ` -// declare const foo: true & { __BRAND: 'Foo' }; -// if ((('' !== "") && foo) || (0 && void 0)) { } -// `, -// }, -// { -// MessageId: "conditionFixCastBoolean", -// Output: ` -// declare const foo: true & { __BRAND: 'Foo' }; -// if (((Boolean('')) && foo) || (0 && void 0)) { } -// `, -// }, -// }, - }, - { - MessageId: "conditionErrorNumber", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareZero", -// Output: ` -// declare const foo: true & { __BRAND: 'Foo' }; -// if (('' && foo) || ((0 !== 0) && void 0)) { } -// `, -// }, -// { -// MessageId: "conditionFixCompareNaN", -// Output: ` -// declare const foo: true & { __BRAND: 'Foo' }; -// if (('' && foo) || ((!Number.isNaN(0)) && void 0)) { } -// `, -// }, -// { -// MessageId: "conditionFixCastBoolean", -// Output: ` -// declare const foo: true & { __BRAND: 'Foo' }; -// if (('' && foo) || ((Boolean(0)) && void 0)) { } -// `, -// }, -// }, - }, - { - MessageId: "conditionErrorNullish", + Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: ` + // declare const foo: true & { __BRAND: 'Foo' }; + // if (((''.length > 0) && foo) || (0 && void 0)) { } + // `, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: ` + // declare const foo: true & { __BRAND: 'Foo' }; + // if ((('' !== "") && foo) || (0 && void 0)) { } + // `, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: ` + // declare const foo: true & { __BRAND: 'Foo' }; + // if (((Boolean('')) && foo) || (0 && void 0)) { } + // `, + // }, + // }, + }, + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: ` + // declare const foo: true & { __BRAND: 'Foo' }; + // if (('' && foo) || ((0 !== 0) && void 0)) { } + // `, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: ` + // declare const foo: true & { __BRAND: 'Foo' }; + // if (('' && foo) || ((!Number.isNaN(0)) && void 0)) { } + // `, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: ` + // declare const foo: true & { __BRAND: 'Foo' }; + // if (('' && foo) || ((Boolean(0)) && void 0)) { } + // `, + // }, + // }, + }, + { + MessageId: "conditionErrorNullish", + }, }, }, - }, - { - Code: ` + { + Code: ` declare const foo: false & { __BRAND: 'Foo' }; if (('' && {}) || (foo && void 0)) { } `, - Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorString", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareStringLength", -// Output: ` -// declare const foo: false & { __BRAND: 'Foo' }; -// if (((''.length > 0) && {}) || (foo && void 0)) { } -// `, -// }, -// { -// MessageId: "conditionFixCompareEmptyString", -// Output: ` -// declare const foo: false & { __BRAND: 'Foo' }; -// if ((('' !== "") && {}) || (foo && void 0)) { } -// `, -// }, -// { -// MessageId: "conditionFixCastBoolean", -// Output: ` -// declare const foo: false & { __BRAND: 'Foo' }; -// if (((Boolean('')) && {}) || (foo && void 0)) { } -// `, -// }, -// }, - }, - { - MessageId: "conditionErrorObject", - }, - { - MessageId: "conditionErrorNullish", + Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false), AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: ` + // declare const foo: false & { __BRAND: 'Foo' }; + // if (((''.length > 0) && {}) || (foo && void 0)) { } + // `, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: ` + // declare const foo: false & { __BRAND: 'Foo' }; + // if ((('' !== "") && {}) || (foo && void 0)) { } + // `, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: ` + // declare const foo: false & { __BRAND: 'Foo' }; + // if (((Boolean('')) && {}) || (foo && void 0)) { } + // `, + // }, + // }, + }, + { + MessageId: "conditionErrorObject", + }, + { + MessageId: "conditionErrorNullish", + }, }, }, - }, - { - Code: `'asd' && 123 && [] && null;`, - Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorString", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareStringLength", - // Output: `('asd'.length > 0) && 123 && [] && null;`, - // }, - // { - // MessageId: "conditionFixCompareEmptyString", - // Output: `('asd' !== "") && 123 && [] && null;`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `(Boolean('asd')) && 123 && [] && null;`, - // }, - // }, - }, - { - MessageId: "conditionErrorNumber", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareZero", - // Output: `'asd' && (123 !== 0) && [] && null;`, - // }, - // { - // MessageId: "conditionFixCompareNaN", - // Output: `'asd' && (!Number.isNaN(123)) && [] && null;`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `'asd' && (Boolean(123)) && [] && null;`, - // }, - // }, - }, - { - MessageId: "conditionErrorObject", + { + Code: `'asd' && 123 && [] && null;`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `('asd'.length > 0) && 123 && [] && null;`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `('asd' !== "") && 123 && [] && null;`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `(Boolean('asd')) && 123 && [] && null;`, + // }, + // }, + }, + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `'asd' && (123 !== 0) && [] && null;`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `'asd' && (!Number.isNaN(123)) && [] && null;`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `'asd' && (Boolean(123)) && [] && null;`, + // }, + // }, + }, + { + MessageId: "conditionErrorObject", + }, }, }, - }, - { - Code: `'asd' || 123 || [] || null;`, - Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorString", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareStringLength", - // Output: `('asd'.length > 0) || 123 || [] || null;`, - // }, - // { - // MessageId: "conditionFixCompareEmptyString", - // Output: `('asd' !== "") || 123 || [] || null;`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `(Boolean('asd')) || 123 || [] || null;`, - // }, - // }, - }, - { - MessageId: "conditionErrorNumber", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareZero", - // Output: `'asd' || (123 !== 0) || [] || null;`, - // }, - // { - // MessageId: "conditionFixCompareNaN", - // Output: `'asd' || (!Number.isNaN(123)) || [] || null;`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `'asd' || (Boolean(123)) || [] || null;`, - // }, - // }, - }, - { - MessageId: "conditionErrorObject", + { + Code: `'asd' || 123 || [] || null;`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `('asd'.length > 0) || 123 || [] || null;`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `('asd' !== "") || 123 || [] || null;`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `(Boolean('asd')) || 123 || [] || null;`, + // }, + // }, + }, + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `'asd' || (123 !== 0) || [] || null;`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `'asd' || (!Number.isNaN(123)) || [] || null;`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `'asd' || (Boolean(123)) || [] || null;`, + // }, + // }, + }, + { + MessageId: "conditionErrorObject", + }, }, }, - }, - { - Code: `let x = (1 && 'a' && null) || 0 || '' || {};`, - Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNumber", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareZero", - // Output: `let x = ((1 !== 0) && 'a' && null) || 0 || '' || {};`, - // }, - // { - // MessageId: "conditionFixCompareNaN", - // Output: `let x = ((!Number.isNaN(1)) && 'a' && null) || 0 || '' || {};`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `let x = ((Boolean(1)) && 'a' && null) || 0 || '' || {};`, - // }, - // }, - }, - { - MessageId: "conditionErrorString", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareStringLength", - // Output: `let x = (1 && ('a'.length > 0) && null) || 0 || '' || {};`, - // }, - // { - // MessageId: "conditionFixCompareEmptyString", - // Output: `let x = (1 && ('a' !== "") && null) || 0 || '' || {};`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `let x = (1 && (Boolean('a')) && null) || 0 || '' || {};`, - // }, - // }, - }, - { - MessageId: "conditionErrorNullish", - }, - { - MessageId: "conditionErrorNumber", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareZero", - // Output: `let x = (1 && 'a' && null) || (0 !== 0) || '' || {};`, - // }, - // { - // MessageId: "conditionFixCompareNaN", - // Output: `let x = (1 && 'a' && null) || (!Number.isNaN(0)) || '' || {};`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `let x = (1 && 'a' && null) || (Boolean(0)) || '' || {};`, - // }, - // }, - }, - { - MessageId: "conditionErrorString", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareStringLength", - // Output: `let x = (1 && 'a' && null) || 0 || (''.length > 0) || {};`, - // }, - // { - // MessageId: "conditionFixCompareEmptyString", - // Output: `let x = (1 && 'a' && null) || 0 || ('' !== "") || {};`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `let x = (1 && 'a' && null) || 0 || (Boolean('')) || {};`, - // }, - // }, + { + Code: `let x = (1 && 'a' && null) || 0 || '' || {};`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `let x = ((1 !== 0) && 'a' && null) || 0 || '' || {};`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `let x = ((!Number.isNaN(1)) && 'a' && null) || 0 || '' || {};`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `let x = ((Boolean(1)) && 'a' && null) || 0 || '' || {};`, + // }, + // }, + }, + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `let x = (1 && ('a'.length > 0) && null) || 0 || '' || {};`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `let x = (1 && ('a' !== "") && null) || 0 || '' || {};`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `let x = (1 && (Boolean('a')) && null) || 0 || '' || {};`, + // }, + // }, + }, + { + MessageId: "conditionErrorNullish", + }, + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `let x = (1 && 'a' && null) || (0 !== 0) || '' || {};`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `let x = (1 && 'a' && null) || (!Number.isNaN(0)) || '' || {};`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `let x = (1 && 'a' && null) || (Boolean(0)) || '' || {};`, + // }, + // }, + }, + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `let x = (1 && 'a' && null) || 0 || (''.length > 0) || {};`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `let x = (1 && 'a' && null) || 0 || ('' !== "") || {};`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `let x = (1 && 'a' && null) || 0 || (Boolean('')) || {};`, + // }, + // }, + }, }, }, - }, - { - Code: `return (1 || 'a' || null) && 0 && '' && {};`, - Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNumber", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareZero", - // Output: `return ((1 !== 0) || 'a' || null) && 0 && '' && {};`, - // }, - // { - // MessageId: "conditionFixCompareNaN", - // Output: `return ((!Number.isNaN(1)) || 'a' || null) && 0 && '' && {};`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `return ((Boolean(1)) || 'a' || null) && 0 && '' && {};`, - // }, - // }, - }, - { - MessageId: "conditionErrorString", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareStringLength", - // Output: `return (1 || ('a'.length > 0) || null) && 0 && '' && {};`, - // }, - // { - // MessageId: "conditionFixCompareEmptyString", - // Output: `return (1 || ('a' !== "") || null) && 0 && '' && {};`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `return (1 || (Boolean('a')) || null) && 0 && '' && {};`, - // }, - // }, - }, - { - MessageId: "conditionErrorNullish", - }, - { - MessageId: "conditionErrorNumber", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareZero", - // Output: `return (1 || 'a' || null) && (0 !== 0) && '' && {};`, - // }, - // { - // MessageId: "conditionFixCompareNaN", - // Output: `return (1 || 'a' || null) && (!Number.isNaN(0)) && '' && {};`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `return (1 || 'a' || null) && (Boolean(0)) && '' && {};`, - // }, - // }, - }, - { - MessageId: "conditionErrorString", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareStringLength", - // Output: `return (1 || 'a' || null) && 0 && (''.length > 0) && {};`, - // }, - // { - // MessageId: "conditionFixCompareEmptyString", - // Output: `return (1 || 'a' || null) && 0 && ('' !== "") && {};`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `return (1 || 'a' || null) && 0 && (Boolean('')) && {};`, - // }, - // }, + { + Code: `return (1 || 'a' || null) && 0 && '' && {};`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `return ((1 !== 0) || 'a' || null) && 0 && '' && {};`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `return ((!Number.isNaN(1)) || 'a' || null) && 0 && '' && {};`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `return ((Boolean(1)) || 'a' || null) && 0 && '' && {};`, + // }, + // }, + }, + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `return (1 || ('a'.length > 0) || null) && 0 && '' && {};`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `return (1 || ('a' !== "") || null) && 0 && '' && {};`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `return (1 || (Boolean('a')) || null) && 0 && '' && {};`, + // }, + // }, + }, + { + MessageId: "conditionErrorNullish", + }, + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `return (1 || 'a' || null) && (0 !== 0) && '' && {};`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `return (1 || 'a' || null) && (!Number.isNaN(0)) && '' && {};`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `return (1 || 'a' || null) && (Boolean(0)) && '' && {};`, + // }, + // }, + }, + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `return (1 || 'a' || null) && 0 && (''.length > 0) && {};`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `return (1 || 'a' || null) && 0 && ('' !== "") && {};`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `return (1 || 'a' || null) && 0 && (Boolean('')) && {};`, + // }, + // }, + }, }, }, - }, - { - Code: `console.log((1 && []) || ('a' && {}));`, - Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNumber", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareZero", - // Output: `console.log(((1 !== 0) && []) || ('a' && {}));`, - // }, - // { - // MessageId: "conditionFixCompareNaN", - // Output: `console.log(((!Number.isNaN(1)) && []) || ('a' && {}));`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `console.log(((Boolean(1)) && []) || ('a' && {}));`, - // }, - // }, - }, - { - MessageId: "conditionErrorObject", - }, - { - MessageId: "conditionErrorString", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareStringLength", - // Output: `console.log((1 && []) || (('a'.length > 0) && {}));`, - // }, - // { - // MessageId: "conditionFixCompareEmptyString", - // Output: `console.log((1 && []) || (('a' !== "") && {}));`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `console.log((1 && []) || ((Boolean('a')) && {}));`, - // }, - // }, + { + Code: `console.log((1 && []) || ('a' && {}));`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `console.log(((1 !== 0) && []) || ('a' && {}));`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `console.log(((!Number.isNaN(1)) && []) || ('a' && {}));`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `console.log(((Boolean(1)) && []) || ('a' && {}));`, + // }, + // }, + }, + { + MessageId: "conditionErrorObject", + }, + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `console.log((1 && []) || (('a'.length > 0) && {}));`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `console.log((1 && []) || (('a' !== "") && {}));`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `console.log((1 && []) || ((Boolean('a')) && {}));`, + // }, + // }, + }, }, }, - }, - { - Code: `if ((1 && []) || ('a' && {})) void 0;`, - Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - // MessageId: "conditionErrorNumber", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareZero", - // Output: `if (((1 !== 0) && []) || ('a' && {})) void 0;`, - // }, - // { - // MessageId: "conditionFixCompareNaN", - // Output: `if (((!Number.isNaN(1)) && []) || ('a' && {})) void 0;`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `if (((Boolean(1)) && []) || ('a' && {})) void 0;`, - // }, - // }, - }, - { - MessageId: "conditionErrorObject", - }, - { - MessageId: "conditionErrorString", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareStringLength", - // Output: `if ((1 && []) || (('a'.length > 0) && {})) void 0;`, - // }, - // { - // MessageId: "conditionFixCompareEmptyString", - // Output: `if ((1 && []) || (('a' !== "") && {})) void 0;`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `if ((1 && []) || ((Boolean('a')) && {})) void 0;`, - // }, - // }, - }, - { - MessageId: "conditionErrorObject", + { + Code: `if ((1 && []) || ('a' && {})) void 0;`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", + }, + { + MessageId: "conditionErrorObject", + }, + { + MessageId: "conditionErrorString", + }, + { + MessageId: "conditionErrorObject", + }, }, }, - }, - { - Code: `let x = null || 0 || 'a' || [] ? {} : undefined;`, - Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullish", - }, - { - MessageId: "conditionErrorNumber", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareZero", - // Output: `let x = null || (0 !== 0) || 'a' || [] ? {} : undefined;`, - // }, - // { - // MessageId: "conditionFixCompareNaN", - // Output: `let x = null || (!Number.isNaN(0)) || 'a' || [] ? {} : undefined;`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `let x = null || (Boolean(0)) || 'a' || [] ? {} : undefined;`, - // }, - // }, - }, - { - MessageId: "conditionErrorString", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareStringLength", - // Output: `let x = null || 0 || ('a'.length > 0) || [] ? {} : undefined;`, - // }, - // { - // MessageId: "conditionFixCompareEmptyString", - // Output: `let x = null || 0 || ('a' !== "") || [] ? {} : undefined;`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `let x = null || 0 || (Boolean('a')) || [] ? {} : undefined;`, - // }, - // }, - }, - { - MessageId: "conditionErrorObject", + { + Code: `let x = null || 0 || 'a' || [] ? {} : undefined;`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", + }, + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `let x = null || (0 !== 0) || 'a' || [] ? {} : undefined;`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `let x = null || (!Number.isNaN(0)) || 'a' || [] ? {} : undefined;`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `let x = null || (Boolean(0)) || 'a' || [] ? {} : undefined;`, + // }, + // }, + }, + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `let x = null || 0 || ('a'.length > 0) || [] ? {} : undefined;`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `let x = null || 0 || ('a' !== "") || [] ? {} : undefined;`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `let x = null || 0 || (Boolean('a')) || [] ? {} : undefined;`, + // }, + // }, + }, + { + MessageId: "conditionErrorObject", + }, }, }, - }, - { - Code: `return !(null || 0 || 'a' || []);`, - Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullish", - }, - { - MessageId: "conditionErrorNumber", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareZero", - // Output: `return !(null || (0 !== 0) || 'a' || []);`, - // }, - // { - // MessageId: "conditionFixCompareNaN", - // Output: `return !(null || (!Number.isNaN(0)) || 'a' || []);`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `return !(null || (Boolean(0)) || 'a' || []);`, - // }, - // }, - }, - { - MessageId: "conditionErrorString", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareStringLength", - // Output: `return !(null || 0 || ('a'.length > 0) || []);`, - // }, - // { - // MessageId: "conditionFixCompareEmptyString", - // Output: `return !(null || 0 || ('a' !== "") || []);`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `return !(null || 0 || (Boolean('a')) || []);`, - // }, - // }, - }, - { - MessageId: "conditionErrorObject", + { + Code: `return !(null || 0 || 'a' || []);`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false), AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", + }, + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `return !(null || (0 !== 0) || 'a' || []);`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `return !(null || (!Number.isNaN(0)) || 'a' || []);`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `return !(null || (Boolean(0)) || 'a' || []);`, + // }, + // }, + }, + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `return !(null || 0 || ('a'.length > 0) || []);`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `return !(null || 0 || ('a' !== "") || []);`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `return !(null || 0 || (Boolean('a')) || []);`, + // }, + // }, + }, + { + MessageId: "conditionErrorObject", + }, }, }, - }, - { - Code: `null || {};`, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullish", + { + Code: `null || {};`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", + }, }, }, - }, - { - Code: `undefined && [];`, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullish", + { + Code: `undefined && [];`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", + }, }, }, - }, - { - Code: ` + { + Code: ` declare const x: null; if (x) { } `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullish", + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", + }, }, }, - }, - { - Code: `(x: undefined) => !x;`, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullish", + { + Code: `(x: undefined) => !x;`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", + }, }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullish", + { + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", + }, }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullish", + { + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", + }, }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullish", + { + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", + }, }, }, - }, - { - Code: `[] || 1;`, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorObject", + { + Code: `[] || 1;`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorObject", + }, }, }, - }, - { - Code: `({}) && 'a';`, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorObject", + { + Code: `({}) && 'a';`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorObject", + }, }, }, - }, - { - Code: ` + { + Code: ` declare const x: symbol; if (x) { } `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorObject", + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorObject", + }, }, }, - }, - { - Code: `(x: () => void) => !x;`, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorObject", + { + Code: `(x: () => void) => !x;`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorObject", + }, }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorObject", + { + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorObject", + }, }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorObject", + { + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorObject", + }, }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorObject", + { + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorObject", + }, }, }, - }, - { - Code: ` void>(x: T) => (x ? 1 : 0);`, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorObject", + { + Code: ` void>(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorObject", + }, }, }, - }, - { - Code: `while ('') {}`, - Options: StrictBooleanExpressionsOptions{AllowString: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorString", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareStringLength", - // Output: `while (''.length > 0) {}`, - // }, - // { - // MessageId: "conditionFixCompareEmptyString", - // Output: `while ('' !== "") {}`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `while (Boolean('')) {}`, - // }, - // }, + { + Code: `while ('') {}`, + Options: StrictBooleanExpressionsOptions{AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `while (''.length > 0) {}`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `while ('' !== "") {}`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `while (Boolean('')) {}`, + // }, + // }, + }, }, }, - }, - { - Code: `for (; 'foo'; ) {}`, - Options: StrictBooleanExpressionsOptions{AllowString: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorString", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareStringLength", - // Output: `for (; 'foo'.length > 0; ) {}`, - // }, - // { - // MessageId: "conditionFixCompareEmptyString", - // Output: `for (; 'foo' !== ""; ) {}`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `for (; Boolean('foo'); ) {}`, - // }, - // }, + { + Code: `for (; 'foo'; ) {}`, + Options: StrictBooleanExpressionsOptions{AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `for (; 'foo'.length > 0; ) {}`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `for (; 'foo' !== ""; ) {}`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `for (; Boolean('foo'); ) {}`, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` declare const x: string; if (x) { } `, - Options: StrictBooleanExpressionsOptions{AllowString: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorString", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareStringLength", -// Output: ` -// declare const x: string; -// if (x.length > 0) { -// } -// `, -// }, -// { -// MessageId: "conditionFixCompareEmptyString", -// Output: ` -// declare const x: string; -// if (x !== "") { -// } -// `, -// }, -// { -// MessageId: "conditionFixCastBoolean", -// Output: ` -// declare const x: string; -// if (Boolean(x)) { -// } -// `, -// }, -// }, + Options: StrictBooleanExpressionsOptions{AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: ` + // declare const x: string; + // if (x.length > 0) { + // } + // `, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: ` + // declare const x: string; + // if (x !== "") { + // } + // `, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: ` + // declare const x: string; + // if (Boolean(x)) { + // } + // `, + // }, + // }, + }, }, }, - }, - { - Code: `(x: string) => !x;`, - Options: StrictBooleanExpressionsOptions{AllowString: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorString", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareStringLength", - // Output: `(x: string) => x.length === 0;`, - // }, - // { - // MessageId: "conditionFixCompareEmptyString", - // Output: `(x: string) => x === "";`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `(x: string) => !Boolean(x);`, - // }, - // }, + { + Code: `(x: string) => !x;`, + Options: StrictBooleanExpressionsOptions{AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `(x: string) => x.length === 0;`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `(x: string) => x === "";`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `(x: string) => !Boolean(x);`, + // }, + // }, + }, }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Options: StrictBooleanExpressionsOptions{AllowString: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorString", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareStringLength", - // Output: `(x: T) => ((x.length > 0) ? 1 : 0);`, - // }, - // { - // MessageId: "conditionFixCompareEmptyString", - // Output: `(x: T) => ((x !== "") ? 1 : 0);`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `(x: T) => ((Boolean(x)) ? 1 : 0);`, - // }, - // }, + { + Code: `(x: T) => (x ? 1 : 0);`, + Options: StrictBooleanExpressionsOptions{AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: `(x: T) => ((x.length > 0) ? 1 : 0);`, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: `(x: T) => ((x !== "") ? 1 : 0);`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `(x: T) => ((Boolean(x)) ? 1 : 0);`, + // }, + // }, + }, }, }, - }, - { - Code: `while (0n) {}`, - Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNumber", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareZero", - // Output: `while (0n !== 0) {}`, - // }, - // { - // MessageId: "conditionFixCompareNaN", - // Output: `while (!Number.isNaN(0n)) {}`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `while (Boolean(0n)) {}`, - // }, - // }, + { + Code: `while (0n) {}`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `while (0n !== 0) {}`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `while (!Number.isNaN(0n)) {}`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `while (Boolean(0n)) {}`, + // }, + // }, + }, }, }, - }, - { - Code: `for (; 123; ) {}`, - Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNumber", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareZero", - // Output: `for (; 123 !== 0; ) {}`, - // }, - // { - // MessageId: "conditionFixCompareNaN", - // Output: `for (; !Number.isNaN(123); ) {}`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `for (; Boolean(123); ) {}`, - // }, - // }, + { + Code: `for (; 123; ) {}`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `for (; 123 !== 0; ) {}`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `for (; !Number.isNaN(123); ) {}`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `for (; Boolean(123); ) {}`, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` declare const x: number; if (x) { } `, - Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNumber", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareZero", -// Output: ` -// declare const x: number; -// if (x !== 0) { -// } -// `, -// }, -// { -// MessageId: "conditionFixCompareNaN", -// Output: ` -// declare const x: number; -// if (!Number.isNaN(x)) { -// } -// `, -// }, -// { -// MessageId: "conditionFixCastBoolean", -// Output: ` -// declare const x: number; -// if (Boolean(x)) { -// } -// `, -// }, -// }, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: ` + // declare const x: number; + // if (x !== 0) { + // } + // `, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: ` + // declare const x: number; + // if (!Number.isNaN(x)) { + // } + // `, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: ` + // declare const x: number; + // if (Boolean(x)) { + // } + // `, + // }, + // }, + }, }, }, - }, - { - Code: `(x: bigint) => !x;`, - Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNumber", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareZero", - // Output: `(x: bigint) => x === 0;`, - // }, - // { - // MessageId: "conditionFixCompareNaN", - // Output: `(x: bigint) => Number.isNaN(x);`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `(x: bigint) => !Boolean(x);`, - // }, - // }, + { + Code: `(x: bigint) => !x;`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `(x: bigint) => x === 0;`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `(x: bigint) => Number.isNaN(x);`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `(x: bigint) => !Boolean(x);`, + // }, + // }, + }, }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNumber", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareZero", - // Output: `(x: T) => ((x !== 0) ? 1 : 0);`, - // }, - // { - // MessageId: "conditionFixCompareNaN", - // Output: `(x: T) => ((!Number.isNaN(x)) ? 1 : 0);`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `(x: T) => ((Boolean(x)) ? 1 : 0);`, - // }, - // }, + { + Code: `(x: T) => (x ? 1 : 0);`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `(x: T) => ((x !== 0) ? 1 : 0);`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `(x: T) => ((!Number.isNaN(x)) ? 1 : 0);`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `(x: T) => ((Boolean(x)) ? 1 : 0);`, + // }, + // }, + }, }, }, - }, - { - Code: `![]['length']; // doesn't count as array.length when computed`, - Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNumber", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareZero", - // Output: `[]['length'] === 0; // doesn't count as array.length when computed`, - // }, - // { - // MessageId: "conditionFixCompareNaN", - // Output: `Number.isNaN([]['length']); // doesn't count as array.length when computed`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `!Boolean([]['length']); // doesn't count as array.length when computed`, - // }, - // }, + { + Code: `![]['length']; // doesn't count as array.length when computed`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: `[]['length'] === 0; // doesn't count as array.length when computed`, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: `Number.isNaN([]['length']); // doesn't count as array.length when computed`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `!Boolean([]['length']); // doesn't count as array.length when computed`, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` declare const a: any[] & { notLength: number }; if (a.notLength) { } `, - Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNumber", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareZero", -// Output: ` -// declare const a: any[] & { notLength: number }; -// if (a.notLength !== 0) { -// } -// `, -// }, -// { -// MessageId: "conditionFixCompareNaN", -// Output: ` -// declare const a: any[] & { notLength: number }; -// if (!Number.isNaN(a.notLength)) { -// } -// `, -// }, -// { -// MessageId: "conditionFixCastBoolean", -// Output: ` -// declare const a: any[] & { notLength: number }; -// if (Boolean(a.notLength)) { -// } -// `, -// }, -// }, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: ` + // declare const a: any[] & { notLength: number }; + // if (a.notLength !== 0) { + // } + // `, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: ` + // declare const a: any[] & { notLength: number }; + // if (!Number.isNaN(a.notLength)) { + // } + // `, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: ` + // declare const a: any[] & { notLength: number }; + // if (Boolean(a.notLength)) { + // } + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` if (![].length) { } `, - Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNumber", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareArrayLengthZero", -// Output: ` -// if ([].length === 0) { -// } -// `, -// }, -// }, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareArrayLengthZero", + // Output: ` + // if ([].length === 0) { + // } + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` (a: number[]) => a.length && '...'; `, - Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNumber", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareArrayLengthNonzero", -// Output: ` -// (a: number[]) => (a.length > 0) && '...'; -// `, -// }, -// }, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareArrayLengthNonzero", + // Output: ` + // (a: number[]) => (a.length > 0) && '...'; + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` (...a: T) => a.length || 'empty'; `, - Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNumber", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareArrayLengthNonzero", -// Output: ` -// (...a: T) => (a.length > 0) || 'empty'; -// `, -// }, -// }, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareArrayLengthNonzero", + // Output: ` + // (...a: T) => (a.length > 0) || 'empty'; + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` declare const x: string | number; if (x) { } `, - Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(true), AllowString: utils.Ref(true)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorOther", + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(true), AllowString: utils.Ref(true)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorOther", + }, }, }, - }, - { - Code: `(x: bigint | string) => !x;`, - Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(true), AllowString: utils.Ref(true)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorOther", + { + Code: `(x: bigint | string) => !x;`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(true), AllowString: utils.Ref(true)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorOther", + }, }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(true), AllowString: utils.Ref(true)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorOther", + { + Code: `(x: T) => (x ? 1 : 0);`, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(true), AllowString: utils.Ref(true)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorOther", + }, }, }, - }, - { - Code: ` + { + Code: ` declare const x: boolean | null; if (x) { } `, - Options: StrictBooleanExpressionsOptions{AllowNullableBoolean: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableBoolean", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixDefaultFalse", -// Output: ` -// declare const x: boolean | null; -// if (x ?? false) { -// } -// `, -// }, -// { -// MessageId: "conditionFixCompareTrue", -// Output: ` -// declare const x: boolean | null; -// if (x === true) { -// } -// `, -// }, -// }, + Options: StrictBooleanExpressionsOptions{AllowNullableBoolean: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableBoolean", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixDefaultFalse", + // Output: ` + // declare const x: boolean | null; + // if (x ?? false) { + // } + // `, + // }, + // { + // MessageId: "conditionFixCompareTrue", + // Output: ` + // declare const x: boolean | null; + // if (x === true) { + // } + // `, + // }, + // }, + }, }, }, - }, - { - Code: `(x?: boolean) => !x;`, - Options: StrictBooleanExpressionsOptions{AllowNullableBoolean: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableBoolean", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixDefaultFalse", - // Output: `(x?: boolean) => !(x ?? false);`, - // }, - // { - // MessageId: "conditionFixCompareFalse", - // Output: `(x?: boolean) => x === false;`, - // }, - // }, + { + Code: `(x?: boolean) => !x;`, + Options: StrictBooleanExpressionsOptions{AllowNullableBoolean: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableBoolean", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixDefaultFalse", + // Output: `(x?: boolean) => !(x ?? false);`, + // }, + // { + // MessageId: "conditionFixCompareFalse", + // Output: `(x?: boolean) => x === false;`, + // }, + // }, + }, }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Options: StrictBooleanExpressionsOptions{AllowNullableBoolean: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableBoolean", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixDefaultFalse", - // Output: `(x: T) => ((x ?? false) ? 1 : 0);`, - // }, - // { - // MessageId: "conditionFixCompareTrue", - // Output: `(x: T) => ((x === true) ? 1 : 0);`, - // }, - // }, + { + Code: `(x: T) => (x ? 1 : 0);`, + Options: StrictBooleanExpressionsOptions{AllowNullableBoolean: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableBoolean", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixDefaultFalse", + // Output: `(x: T) => ((x ?? false) ? 1 : 0);`, + // }, + // { + // MessageId: "conditionFixCompareTrue", + // Output: `(x: T) => ((x === true) ? 1 : 0);`, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` declare const x: object | null; if (x) { } `, - Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableObject", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareNullish", -// Output: ` -// declare const x: object | null; -// if (x != null) { -// } -// `, -// }, -// }, + Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableObject", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // declare const x: object | null; + // if (x != null) { + // } + // `, + // }, + // }, + }, }, }, - }, - { - Code: `(x?: { a: number }) => !x;`, - Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableObject", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareNullish", - // Output: `(x?: { a: number }) => x == null;`, - // }, - // }, + { + Code: `(x?: { a: number }) => !x;`, + Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableObject", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: `(x?: { a: number }) => x == null;`, + // }, + // }, + }, }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableObject", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareNullish", - // Output: `(x: T) => ((x != null) ? 1 : 0);`, - // }, - // }, + { + Code: `(x: T) => (x ? 1 : 0);`, + Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableObject", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: `(x: T) => ((x != null) ? 1 : 0);`, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` declare const x: string | null; if (x) { } `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableString", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareNullish", -// Output: ` -// declare const x: string | null; -// if (x != null) { -// } -// `, -// }, -// { -// MessageId: "conditionFixDefaultEmptyString", -// Output: ` -// declare const x: string | null; -// if (x ?? "") { -// } -// `, -// }, -// { -// MessageId: "conditionFixCastBoolean", -// Output: ` -// declare const x: string | null; -// if (Boolean(x)) { -// } -// `, -// }, -// }, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // declare const x: string | null; + // if (x != null) { + // } + // `, + // }, + // { + // MessageId: "conditionFixDefaultEmptyString", + // Output: ` + // declare const x: string | null; + // if (x ?? "") { + // } + // `, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: ` + // declare const x: string | null; + // if (Boolean(x)) { + // } + // `, + // }, + // }, + }, }, }, - }, - { - Code: `(x?: string) => !x;`, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableString", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareNullish", - // Output: `(x?: string) => x == null;`, - // }, - // { - // MessageId: "conditionFixDefaultEmptyString", - // Output: `(x?: string) => !(x ?? "");`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `(x?: string) => !Boolean(x);`, - // }, - // }, + { + Code: `(x?: string) => !x;`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: `(x?: string) => x == null;`, + // }, + // { + // MessageId: "conditionFixDefaultEmptyString", + // Output: `(x?: string) => !(x ?? "");`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `(x?: string) => !Boolean(x);`, + // }, + // }, + }, }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableString", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareNullish", - // Output: `(x: T) => ((x != null) ? 1 : 0);`, - // }, - // { - // MessageId: "conditionFixDefaultEmptyString", - // Output: `(x: T) => ((x ?? "") ? 1 : 0);`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `(x: T) => ((Boolean(x)) ? 1 : 0);`, - // }, - // }, + { + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: `(x: T) => ((x != null) ? 1 : 0);`, + // }, + // { + // MessageId: "conditionFixDefaultEmptyString", + // Output: `(x: T) => ((x ?? "") ? 1 : 0);`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `(x: T) => ((Boolean(x)) ? 1 : 0);`, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` function foo(x: '' | 'bar' | null) { if (!x) { } } `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableString", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareNullish", -// Output: ` -// function foo(x: '' | 'bar' | null) { -// if (x == null) { -// } -// } -// `, -// }, -// { -// MessageId: "conditionFixDefaultEmptyString", -// Output: ` -// function foo(x: '' | 'bar' | null) { -// if (!(x ?? "")) { -// } -// } -// `, -// }, -// { -// MessageId: "conditionFixCastBoolean", -// Output: ` -// function foo(x: '' | 'bar' | null) { -// if (!Boolean(x)) { -// } -// } -// `, -// }, -// }, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // function foo(x: '' | 'bar' | null) { + // if (x == null) { + // } + // } + // `, + // }, + // { + // MessageId: "conditionFixDefaultEmptyString", + // Output: ` + // function foo(x: '' | 'bar' | null) { + // if (!(x ?? "")) { + // } + // } + // `, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: ` + // function foo(x: '' | 'bar' | null) { + // if (!Boolean(x)) { + // } + // } + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` declare const x: number | null; if (x) { } `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableNumber", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareNullish", -// Output: ` -// declare const x: number | null; -// if (x != null) { -// } -// `, -// }, -// { -// MessageId: "conditionFixDefaultZero", -// Output: ` -// declare const x: number | null; -// if (x ?? 0) { -// } -// `, -// }, -// { -// MessageId: "conditionFixCastBoolean", -// Output: ` -// declare const x: number | null; -// if (Boolean(x)) { -// } -// `, -// }, -// }, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // declare const x: number | null; + // if (x != null) { + // } + // `, + // }, + // { + // MessageId: "conditionFixDefaultZero", + // Output: ` + // declare const x: number | null; + // if (x ?? 0) { + // } + // `, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: ` + // declare const x: number | null; + // if (Boolean(x)) { + // } + // `, + // }, + // }, + }, }, }, - }, - { - Code: `(x?: number) => !x;`, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableNumber", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareNullish", - // Output: `(x?: number) => x == null;`, - // }, - // { - // MessageId: "conditionFixDefaultZero", - // Output: `(x?: number) => !(x ?? 0);`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `(x?: number) => !Boolean(x);`, - // }, - // }, + { + Code: `(x?: number) => !x;`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: `(x?: number) => x == null;`, + // }, + // { + // MessageId: "conditionFixDefaultZero", + // Output: `(x?: number) => !(x ?? 0);`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `(x?: number) => !Boolean(x);`, + // }, + // }, + }, }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableNumber", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareNullish", - // Output: `(x: T) => ((x != null) ? 1 : 0);`, - // }, - // { - // MessageId: "conditionFixDefaultZero", - // Output: `(x: T) => ((x ?? 0) ? 1 : 0);`, - // }, - // { - // MessageId: "conditionFixCastBoolean", - // Output: `(x: T) => ((Boolean(x)) ? 1 : 0);`, - // }, - // }, + { + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: `(x: T) => ((x != null) ? 1 : 0);`, + // }, + // { + // MessageId: "conditionFixDefaultZero", + // Output: `(x: T) => ((x ?? 0) ? 1 : 0);`, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: `(x: T) => ((Boolean(x)) ? 1 : 0);`, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` function foo(x: 0 | 1 | null) { if (!x) { } } `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableNumber", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareNullish", -// Output: ` -// function foo(x: 0 | 1 | null) { -// if (x == null) { -// } -// } -// `, -// }, -// { -// MessageId: "conditionFixDefaultZero", -// Output: ` -// function foo(x: 0 | 1 | null) { -// if (!(x ?? 0)) { -// } -// } -// `, -// }, -// { -// MessageId: "conditionFixCastBoolean", -// Output: ` -// function foo(x: 0 | 1 | null) { -// if (!Boolean(x)) { -// } -// } -// `, -// }, -// }, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // function foo(x: 0 | 1 | null) { + // if (x == null) { + // } + // } + // `, + // }, + // { + // MessageId: "conditionFixDefaultZero", + // Output: ` + // function foo(x: 0 | 1 | null) { + // if (!(x ?? 0)) { + // } + // } + // `, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: ` + // function foo(x: 0 | 1 | null) { + // if (!Boolean(x)) { + // } + // } + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` enum ExampleEnum { This = 0, That = 1, @@ -2245,29 +2216,29 @@ function foo(x: 0 | 1 | null) { if (theEnum) { } `, - Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableEnum", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareNullish", - // Output: ` - // enum ExampleEnum { - // This = 0, - // That = 1, - // } - // const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; - // if (theEnum != null) { - // } - // `, - // }, - // }, + Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableEnum", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // enum ExampleEnum { + // This = 0, + // That = 1, + // } + // const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + // if (theEnum != null) { + // } + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` enum ExampleEnum { This = 0, That = 1, @@ -2276,29 +2247,29 @@ function foo(x: 0 | 1 | null) { if (!theEnum) { } `, - Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableEnum", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareNullish", - // Output: ` - // enum ExampleEnum { - // This = 0, - // That = 1, - // } - // const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; - // if (theEnum == null) { - // } - // `, - // }, - // }, + Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableEnum", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // enum ExampleEnum { + // This = 0, + // That = 1, + // } + // const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + // if (theEnum == null) { + // } + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` enum ExampleEnum { This, That, @@ -2307,29 +2278,29 @@ function foo(x: 0 | 1 | null) { if (!theEnum) { } `, - Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableEnum", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareNullish", - // Output: ` - // enum ExampleEnum { - // This, - // That, - // } - // const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; - // if (theEnum == null) { - // } - // `, - // }, - // }, + Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableEnum", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // enum ExampleEnum { + // This, + // That, + // } + // const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + // if (theEnum == null) { + // } + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` enum ExampleEnum { This = '', That = 'a', @@ -2338,29 +2309,29 @@ function foo(x: 0 | 1 | null) { if (!theEnum) { } `, - Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableEnum", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareNullish", - // Output: ` - // enum ExampleEnum { - // This = '', - // That = 'a', - // } - // const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; - // if (theEnum == null) { - // } - // `, - // }, - // }, + Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableEnum", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // enum ExampleEnum { + // This = '', + // That = 'a', + // } + // const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + // if (theEnum == null) { + // } + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` enum ExampleEnum { This = '', That = 0, @@ -2369,29 +2340,29 @@ function foo(x: 0 | 1 | null) { if (!theEnum) { } `, - Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableEnum", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareNullish", - // Output: ` - // enum ExampleEnum { - // This = '', - // That = 0, - // } - // const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; - // if (theEnum == null) { - // } - // `, - // }, - // }, + Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableEnum", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // enum ExampleEnum { + // This = '', + // That = 0, + // } + // const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + // if (theEnum == null) { + // } + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` enum ExampleEnum { This = 'one', That = 'two', @@ -2400,29 +2371,29 @@ function foo(x: 0 | 1 | null) { if (!theEnum) { } `, - Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableEnum", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareNullish", - // Output: ` - // enum ExampleEnum { - // This = 'one', - // That = 'two', - // } - // const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; - // if (theEnum == null) { - // } - // `, - // }, - // }, + Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableEnum", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // enum ExampleEnum { + // This = 'one', + // That = 'two', + // } + // const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + // if (theEnum == null) { + // } + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` enum ExampleEnum { This = 1, That = 2, @@ -2431,327 +2402,452 @@ function foo(x: 0 | 1 | null) { if (!theEnum) { } `, - Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableEnum", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareNullish", - // Output: ` - // enum ExampleEnum { - // This = 1, - // That = 2, - // } - // const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; - // if (theEnum == null) { - // } - // `, - // }, - // }, + Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableEnum", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // enum ExampleEnum { + // This = 1, + // That = 2, + // } + // const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + // if (theEnum == null) { + // } + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` enum ExampleEnum { This = 0, That = 'one', } (value?: ExampleEnum) => (value ? 1 : 0); `, - Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableEnum", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareNullish", - // Output: ` - // enum ExampleEnum { - // This = 0, - // That = 'one', - // } - // (value?: ExampleEnum) => ((value != null) ? 1 : 0); - // `, - // }, - // }, + Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableEnum", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // enum ExampleEnum { + // This = 0, + // That = 'one', + // } + // (value?: ExampleEnum) => ((value != null) ? 1 : 0); + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` enum ExampleEnum { This = '', That = 1, } (value?: ExampleEnum) => (!value ? 1 : 0); `, - Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableEnum", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareNullish", - // Output: ` - // enum ExampleEnum { - // This = '', - // That = 1, - // } - // (value?: ExampleEnum) => ((value == null) ? 1 : 0); - // `, - // }, - // }, + Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableEnum", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // enum ExampleEnum { + // This = '', + // That = 1, + // } + // (value?: ExampleEnum) => ((value == null) ? 1 : 0); + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` enum ExampleEnum { This = 'this', That = 1, } (value?: ExampleEnum) => (!value ? 1 : 0); `, - Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableEnum", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareNullish", - // Output: ` - // enum ExampleEnum { - // This = 'this', - // That = 1, - // } - // (value?: ExampleEnum) => ((value == null) ? 1 : 0); - // `, - // }, - // }, + Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableEnum", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // enum ExampleEnum { + // This = 'this', + // That = 1, + // } + // (value?: ExampleEnum) => ((value == null) ? 1 : 0); + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` enum ExampleEnum { This = '', That = 0, } (value?: ExampleEnum) => (!value ? 1 : 0); `, - Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableEnum", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareNullish", - // Output: ` - // enum ExampleEnum { - // This = '', - // That = 0, - // } - // (value?: ExampleEnum) => ((value == null) ? 1 : 0); - // `, - // }, - // }, + Options: StrictBooleanExpressionsOptions{AllowNullableEnum: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableEnum", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // enum ExampleEnum { + // This = '', + // That = 0, + // } + // (value?: ExampleEnum) => ((value == null) ? 1 : 0); + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` if (x) { } `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorAny", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCastBoolean", -// Output: ` -// if (Boolean(x)) { -// } -// `, -// }, -// }, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorAny", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCastBoolean", + // Output: ` + // if (Boolean(x)) { + // } + // `, + // }, + // }, + }, }, }, - }, - { - Code: `x => !x;`, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorAny", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCastBoolean", - // Output: `x => !(Boolean(x));`, - // }, - // }, + { + Code: `x => !x;`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorAny", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCastBoolean", + // Output: `x => !(Boolean(x));`, + // }, + // }, + }, }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorAny", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCastBoolean", - // Output: `(x: T) => ((Boolean(x)) ? 1 : 0);`, - // }, - // }, + { + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorAny", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCastBoolean", + // Output: `(x: T) => ((Boolean(x)) ? 1 : 0);`, + // }, + // }, + }, }, }, - }, - { - Code: `(x: T) => (x ? 1 : 0);`, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorAny", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCastBoolean", - // Output: `(x: T) => ((Boolean(x)) ? 1 : 0);`, - // }, - // }, + { + Code: `(x: T) => (x ? 1 : 0);`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorAny", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCastBoolean", + // Output: `(x: T) => ((Boolean(x)) ? 1 : 0);`, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` declare const x: string[] | null; if (x) { } `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "noStrictNullCheck", - }, - { - MessageId: "conditionErrorObject", + TSConfig: "tsconfig.unstrict.json", + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "noStrictNullCheck", + }, + { + MessageId: "conditionErrorObject", + }, }, }, - }, - { - Code: ` + { + Code: ` declare const obj: { x: number } | null; !obj ? 1 : 0 !obj obj || 0 obj && 1 || 0 `, - Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableObject", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareNullish", - // Output: ` - // declare const obj: { x: number } | null; - // (obj == null) ? 1 : 0 - // !obj - // obj || 0 - // obj && 1 || 0 - // `, - // }, - // }, - }, - { - MessageId: "conditionErrorNullableObject", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareNullish", - // Output: ` - // declare const obj: { x: number } | null; - // !obj ? 1 : 0 - // obj == null - // obj || 0 - // obj && 1 || 0 - // `, - // }, - // }, - }, - { - MessageId: "conditionErrorNullableObject", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareNullish", - // Output: ` - // declare const obj: { x: number } | null; - // !obj ? 1 : 0 - // !obj - // ;(obj != null) || 0 - // obj && 1 || 0 - // `, - // }, - // }, - }, - { - MessageId: "conditionErrorNullableObject", - // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ - // { - // MessageId: "conditionFixCompareNullish", - // Output: ` - // declare const obj: { x: number } | null; - // !obj ? 1 : 0 - // !obj - // obj || 0 - // ;(obj != null) && 1 || 0 - // `, - // }, - // }, + Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableObject", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // declare const obj: { x: number } | null; + // (obj == null) ? 1 : 0 + // !obj + // obj || 0 + // obj && 1 || 0 + // `, + // }, + // }, + }, + { + MessageId: "conditionErrorNullableObject", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // declare const obj: { x: number } | null; + // !obj ? 1 : 0 + // obj == null + // obj || 0 + // obj && 1 || 0 + // `, + // }, + // }, + }, + { + MessageId: "conditionErrorNullableObject", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // declare const obj: { x: number } | null; + // !obj ? 1 : 0 + // !obj + // ;(obj != null) || 0 + // obj && 1 || 0 + // `, + // }, + // }, + }, + { + MessageId: "conditionErrorNullableObject", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // declare const obj: { x: number } | null; + // !obj ? 1 : 0 + // !obj + // obj || 0 + // ;(obj != null) && 1 || 0 + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` declare function assert(x: unknown): asserts x; declare const nullableString: string | null; assert(nullableString); `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableString", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareNullish", -// Output: ` -// declare function assert(x: unknown): asserts x; -// declare const nullableString: string | null; -// assert(nullableString != null); -// `, -// }, -// { -// MessageId: "conditionFixDefaultEmptyString", -// Output: ` -// declare function assert(x: unknown): asserts x; -// declare const nullableString: string | null; -// assert(nullableString ?? ""); -// `, -// }, -// { -// MessageId: "conditionFixCastBoolean", -// Output: ` -// declare function assert(x: unknown): asserts x; -// declare const nullableString: string | null; -// assert(Boolean(nullableString)); -// `, -// }, -// }, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // declare function assert(x: unknown): asserts x; + // declare const nullableString: string | null; + // assert(nullableString != null); + // `, + // }, + // { + // MessageId: "conditionFixDefaultEmptyString", + // Output: ` + // declare function assert(x: unknown): asserts x; + // declare const nullableString: string | null; + // assert(nullableString ?? ""); + // `, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: ` + // declare function assert(x: unknown): asserts x; + // declare const nullableString: string | null; + // assert(Boolean(nullableString)); + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` +declare function assert(a: number, b: unknown): asserts b; +declare const nullableString: string | null; +assert(foo, nullableString); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // declare function assert(a: number, b: unknown): asserts b; + // declare const nullableString: string | null; + // assert(foo, nullableString != null); + // `, + // }, + // { + // MessageId: "conditionFixDefaultEmptyString", + // Output: ` + // declare function assert(a: number, b: unknown): asserts b; + // declare const nullableString: string | null; + // assert(foo, nullableString ?? ""); + // `, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: ` + // declare function assert(a: number, b: unknown): asserts b; + // declare const nullableString: string | null; + // assert(foo, Boolean(nullableString)); + // `, + // }, + // }, + }, + }, + }, + { + Code: ` declare function assert(a: number, b: unknown): asserts b; +declare function assert(one: number, two: unknown): asserts two; +declare const nullableString: string | null; +assert(foo, nullableString); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // declare function assert(a: number, b: unknown): asserts b; + // declare function assert(one: number, two: unknown): asserts two; + // declare const nullableString: string | null; + // assert(foo, nullableString != null); + // `, + // }, + // { + // MessageId: "conditionFixDefaultEmptyString", + // Output: ` + // declare function assert(a: number, b: unknown): asserts b; + // declare function assert(one: number, two: unknown): asserts two; + // declare const nullableString: string | null; + // assert(foo, nullableString ?? ""); + // `, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: ` + // declare function assert(a: number, b: unknown): asserts b; + // declare function assert(one: number, two: unknown): asserts two; + // declare const nullableString: string | null; + // assert(foo, Boolean(nullableString)); + // `, + // }, + // }, + }, + }, + }, + { + Code: ` +declare function assert(this: object, a: number, b: unknown): asserts b; declare const nullableString: string | null; assert(foo, nullableString); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // declare function assert(this: object, a: number, b: unknown): asserts b; + // declare const nullableString: string | null; + // assert(foo, nullableString != null); + // `, + // }, + // { + // MessageId: "conditionFixDefaultEmptyString", + // Output: ` + // declare function assert(this: object, a: number, b: unknown): asserts b; + // declare const nullableString: string | null; + // assert(foo, nullableString ?? ""); + // `, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: ` + // declare function assert(this: object, a: number, b: unknown): asserts b; + // declare const nullableString: string | null; + // assert(foo, Boolean(nullableString)); + // `, + // }, + // }, + }, + }, + }, + { + Code: ` +function asserts1(x: string | number | undefined): asserts x {} +function asserts2(x: string | number | undefined): asserts x {} + +const maybeString = Math.random() ? 'string'.slice() : undefined; + +const someAssert: typeof asserts1 | typeof asserts2 = + Math.random() > 0.5 ? asserts1 : asserts2; + +someAssert(maybeString); `, Errors: []rule_tester.InvalidTestCaseError{ { @@ -2760,167 +2856,43 @@ assert(foo, nullableString); // { // MessageId: "conditionFixCompareNullish", // Output: ` -// declare function assert(a: number, b: unknown): asserts b; -// declare const nullableString: string | null; -// assert(foo, nullableString != null); +// function asserts1(x: string | number | undefined): asserts x {} +// function asserts2(x: string | number | undefined): asserts x {} + +// const maybeString = Math.random() ? 'string'.slice() : undefined; + +// const someAssert: typeof asserts1 | typeof asserts2 = +// Math.random() > 0.5 ? asserts1 : asserts2; + +// someAssert(maybeString != null); // `, // }, // { // MessageId: "conditionFixDefaultEmptyString", // Output: ` -// declare function assert(a: number, b: unknown): asserts b; -// declare const nullableString: string | null; -// assert(foo, nullableString ?? ""); +// function asserts1(x: string | number | undefined): asserts x {} +// function asserts2(x: string | number | undefined): asserts x {} + +// const maybeString = Math.random() ? 'string'.slice() : undefined; + +// const someAssert: typeof asserts1 | typeof asserts2 = +// Math.random() > 0.5 ? asserts1 : asserts2; + +// someAssert(maybeString ?? ""); // `, // }, // { // MessageId: "conditionFixCastBoolean", // Output: ` -// declare function assert(a: number, b: unknown): asserts b; -// declare const nullableString: string | null; -// assert(foo, Boolean(nullableString)); -// `, -// }, -// }, - }, - }, - }, - { - Code: ` -declare function assert(a: number, b: unknown): asserts b; -declare function assert(one: number, two: unknown): asserts two; -declare const nullableString: string | null; -assert(foo, nullableString); - `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableString", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareNullish", -// Output: ` -// declare function assert(a: number, b: unknown): asserts b; -// declare function assert(one: number, two: unknown): asserts two; -// declare const nullableString: string | null; -// assert(foo, nullableString != null); -// `, -// }, -// { -// MessageId: "conditionFixDefaultEmptyString", -// Output: ` -// declare function assert(a: number, b: unknown): asserts b; -// declare function assert(one: number, two: unknown): asserts two; -// declare const nullableString: string | null; -// assert(foo, nullableString ?? ""); -// `, -// }, -// { -// MessageId: "conditionFixCastBoolean", -// Output: ` -// declare function assert(a: number, b: unknown): asserts b; -// declare function assert(one: number, two: unknown): asserts two; -// declare const nullableString: string | null; -// assert(foo, Boolean(nullableString)); -// `, -// }, -// }, - }, - }, - }, - { - Code: ` -declare function assert(this: object, a: number, b: unknown): asserts b; -declare const nullableString: string | null; -assert(foo, nullableString); - `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableString", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareNullish", -// Output: ` -// declare function assert(this: object, a: number, b: unknown): asserts b; -// declare const nullableString: string | null; -// assert(foo, nullableString != null); -// `, -// }, -// { -// MessageId: "conditionFixDefaultEmptyString", -// Output: ` -// declare function assert(this: object, a: number, b: unknown): asserts b; -// declare const nullableString: string | null; -// assert(foo, nullableString ?? ""); -// `, -// }, -// { -// MessageId: "conditionFixCastBoolean", -// Output: ` -// declare function assert(this: object, a: number, b: unknown): asserts b; -// declare const nullableString: string | null; -// assert(foo, Boolean(nullableString)); -// `, -// }, -// }, - }, - }, - }, - { - Code: ` -function asserts1(x: string | number | undefined): asserts x {} -function asserts2(x: string | number | undefined): asserts x {} - -const maybeString = Math.random() ? 'string'.slice() : undefined; - -const someAssert: typeof asserts1 | typeof asserts2 = - Math.random() > 0.5 ? asserts1 : asserts2; - -someAssert(maybeString); - `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableString", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareNullish", -// Output: ` -// function asserts1(x: string | number | undefined): asserts x {} -// function asserts2(x: string | number | undefined): asserts x {} - -// const maybeString = Math.random() ? 'string'.slice() : undefined; - -// const someAssert: typeof asserts1 | typeof asserts2 = -// Math.random() > 0.5 ? asserts1 : asserts2; - -// someAssert(maybeString != null); -// `, -// }, -// { -// MessageId: "conditionFixDefaultEmptyString", -// Output: ` -// function asserts1(x: string | number | undefined): asserts x {} -// function asserts2(x: string | number | undefined): asserts x {} - -// const maybeString = Math.random() ? 'string'.slice() : undefined; - -// const someAssert: typeof asserts1 | typeof asserts2 = -// Math.random() > 0.5 ? asserts1 : asserts2; - -// someAssert(maybeString ?? ""); -// `, -// }, -// { -// MessageId: "conditionFixCastBoolean", -// Output: ` -// function asserts1(x: string | number | undefined): asserts x {} -// function asserts2(x: string | number | undefined): asserts x {} - -// const maybeString = Math.random() ? 'string'.slice() : undefined; - -// const someAssert: typeof asserts1 | typeof asserts2 = -// Math.random() > 0.5 ? asserts1 : asserts2; - -// someAssert(Boolean(maybeString)); +// function asserts1(x: string | number | undefined): asserts x {} +// function asserts2(x: string | number | undefined): asserts x {} + +// const maybeString = Math.random() ? 'string'.slice() : undefined; + +// const someAssert: typeof asserts1 | typeof asserts2 = +// Math.random() > 0.5 ? asserts1 : asserts2; + +// someAssert(Boolean(maybeString)); // `, // }, // }, @@ -2947,82 +2919,82 @@ function assert(...args: any[]) { declare const nullableString: string | null; assert(3 as any, nullableString); `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableString", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareNullish", -// Output: ` -// function assert(this: object, a: number, b: unknown): asserts b; -// function assert(a: bigint, b: unknown): asserts b; -// function assert(this: object, a: string, two: string): asserts two; -// function assert( -// this: object, -// a: string, -// assertee: string, -// c: bigint, -// d: object, -// ): asserts assertee; + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // function assert(this: object, a: number, b: unknown): asserts b; + // function assert(a: bigint, b: unknown): asserts b; + // function assert(this: object, a: string, two: string): asserts two; + // function assert( + // this: object, + // a: string, + // assertee: string, + // c: bigint, + // d: object, + // ): asserts assertee; -// function assert(...args: any[]) { -// throw new Error('lol'); -// } + // function assert(...args: any[]) { + // throw new Error('lol'); + // } -// declare const nullableString: string | null; -// assert(3 as any, nullableString != null); -// `, -// }, -// { -// MessageId: "conditionFixDefaultEmptyString", -// Output: ` -// function assert(this: object, a: number, b: unknown): asserts b; -// function assert(a: bigint, b: unknown): asserts b; -// function assert(this: object, a: string, two: string): asserts two; -// function assert( -// this: object, -// a: string, -// assertee: string, -// c: bigint, -// d: object, -// ): asserts assertee; + // declare const nullableString: string | null; + // assert(3 as any, nullableString != null); + // `, + // }, + // { + // MessageId: "conditionFixDefaultEmptyString", + // Output: ` + // function assert(this: object, a: number, b: unknown): asserts b; + // function assert(a: bigint, b: unknown): asserts b; + // function assert(this: object, a: string, two: string): asserts two; + // function assert( + // this: object, + // a: string, + // assertee: string, + // c: bigint, + // d: object, + // ): asserts assertee; -// function assert(...args: any[]) { -// throw new Error('lol'); -// } + // function assert(...args: any[]) { + // throw new Error('lol'); + // } -// declare const nullableString: string | null; -// assert(3 as any, nullableString ?? ""); -// `, -// }, -// { -// MessageId: "conditionFixCastBoolean", -// Output: ` -// function assert(this: object, a: number, b: unknown): asserts b; -// function assert(a: bigint, b: unknown): asserts b; -// function assert(this: object, a: string, two: string): asserts two; -// function assert( -// this: object, -// a: string, -// assertee: string, -// c: bigint, -// d: object, -// ): asserts assertee; + // declare const nullableString: string | null; + // assert(3 as any, nullableString ?? ""); + // `, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: ` + // function assert(this: object, a: number, b: unknown): asserts b; + // function assert(a: bigint, b: unknown): asserts b; + // function assert(this: object, a: string, two: string): asserts two; + // function assert( + // this: object, + // a: string, + // assertee: string, + // c: bigint, + // d: object, + // ): asserts assertee; -// function assert(...args: any[]) { -// throw new Error('lol'); -// } + // function assert(...args: any[]) { + // throw new Error('lol'); + // } -// declare const nullableString: string | null; -// assert(3 as any, Boolean(nullableString)); -// `, -// }, -// }, + // declare const nullableString: string | null; + // assert(3 as any, Boolean(nullableString)); + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` function assert(this: object, a: number, b: unknown): asserts b; function assert(a: bigint, b: unknown): asserts b; function assert(this: object, a: string, two: string): asserts two; @@ -3042,131 +3014,131 @@ function assert(...args: any[]) { declare const nullableString: string | null; assert(3 as any, nullableString, 'more', 'args', 'afterwards'); `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableString", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareNullish", -// Output: ` -// function assert(this: object, a: number, b: unknown): asserts b; -// function assert(a: bigint, b: unknown): asserts b; -// function assert(this: object, a: string, two: string): asserts two; -// function assert( -// this: object, -// a: string, -// assertee: string, -// c: bigint, -// d: object, -// ): asserts assertee; -// function assert(a: any, two: unknown, ...rest: any[]): asserts two; + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // function assert(this: object, a: number, b: unknown): asserts b; + // function assert(a: bigint, b: unknown): asserts b; + // function assert(this: object, a: string, two: string): asserts two; + // function assert( + // this: object, + // a: string, + // assertee: string, + // c: bigint, + // d: object, + // ): asserts assertee; + // function assert(a: any, two: unknown, ...rest: any[]): asserts two; -// function assert(...args: any[]) { -// throw new Error('lol'); -// } + // function assert(...args: any[]) { + // throw new Error('lol'); + // } -// declare const nullableString: string | null; -// assert(3 as any, nullableString != null, 'more', 'args', 'afterwards'); -// `, -// }, -// { -// MessageId: "conditionFixDefaultEmptyString", -// Output: ` -// function assert(this: object, a: number, b: unknown): asserts b; -// function assert(a: bigint, b: unknown): asserts b; -// function assert(this: object, a: string, two: string): asserts two; -// function assert( -// this: object, -// a: string, -// assertee: string, -// c: bigint, -// d: object, -// ): asserts assertee; -// function assert(a: any, two: unknown, ...rest: any[]): asserts two; + // declare const nullableString: string | null; + // assert(3 as any, nullableString != null, 'more', 'args', 'afterwards'); + // `, + // }, + // { + // MessageId: "conditionFixDefaultEmptyString", + // Output: ` + // function assert(this: object, a: number, b: unknown): asserts b; + // function assert(a: bigint, b: unknown): asserts b; + // function assert(this: object, a: string, two: string): asserts two; + // function assert( + // this: object, + // a: string, + // assertee: string, + // c: bigint, + // d: object, + // ): asserts assertee; + // function assert(a: any, two: unknown, ...rest: any[]): asserts two; -// function assert(...args: any[]) { -// throw new Error('lol'); -// } + // function assert(...args: any[]) { + // throw new Error('lol'); + // } -// declare const nullableString: string | null; -// assert(3 as any, nullableString ?? "", 'more', 'args', 'afterwards'); -// `, -// }, -// { -// MessageId: "conditionFixCastBoolean", -// Output: ` -// function assert(this: object, a: number, b: unknown): asserts b; -// function assert(a: bigint, b: unknown): asserts b; -// function assert(this: object, a: string, two: string): asserts two; -// function assert( -// this: object, -// a: string, -// assertee: string, -// c: bigint, -// d: object, -// ): asserts assertee; -// function assert(a: any, two: unknown, ...rest: any[]): asserts two; + // declare const nullableString: string | null; + // assert(3 as any, nullableString ?? "", 'more', 'args', 'afterwards'); + // `, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: ` + // function assert(this: object, a: number, b: unknown): asserts b; + // function assert(a: bigint, b: unknown): asserts b; + // function assert(this: object, a: string, two: string): asserts two; + // function assert( + // this: object, + // a: string, + // assertee: string, + // c: bigint, + // d: object, + // ): asserts assertee; + // function assert(a: any, two: unknown, ...rest: any[]): asserts two; -// function assert(...args: any[]) { -// throw new Error('lol'); -// } + // function assert(...args: any[]) { + // throw new Error('lol'); + // } -// declare const nullableString: string | null; -// assert(3 as any, Boolean(nullableString), 'more', 'args', 'afterwards'); -// `, -// }, -// }, + // declare const nullableString: string | null; + // assert(3 as any, Boolean(nullableString), 'more', 'args', 'afterwards'); + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` declare function assert(a: boolean, b: unknown): asserts b; declare function assert({ a }: { a: boolean }, b: unknown): asserts b; declare const nullableString: string | null; declare const boo: boolean; assert(boo, nullableString); `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableString", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareNullish", -// Output: ` -// declare function assert(a: boolean, b: unknown): asserts b; -// declare function assert({ a }: { a: boolean }, b: unknown): asserts b; -// declare const nullableString: string | null; -// declare const boo: boolean; -// assert(boo, nullableString != null); -// `, -// }, -// { -// MessageId: "conditionFixDefaultEmptyString", -// Output: ` -// declare function assert(a: boolean, b: unknown): asserts b; -// declare function assert({ a }: { a: boolean }, b: unknown): asserts b; -// declare const nullableString: string | null; -// declare const boo: boolean; -// assert(boo, nullableString ?? ""); -// `, -// }, -// { -// MessageId: "conditionFixCastBoolean", -// Output: ` -// declare function assert(a: boolean, b: unknown): asserts b; -// declare function assert({ a }: { a: boolean }, b: unknown): asserts b; -// declare const nullableString: string | null; -// declare const boo: boolean; -// assert(boo, Boolean(nullableString)); -// `, -// }, -// }, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // declare function assert(a: boolean, b: unknown): asserts b; + // declare function assert({ a }: { a: boolean }, b: unknown): asserts b; + // declare const nullableString: string | null; + // declare const boo: boolean; + // assert(boo, nullableString != null); + // `, + // }, + // { + // MessageId: "conditionFixDefaultEmptyString", + // Output: ` + // declare function assert(a: boolean, b: unknown): asserts b; + // declare function assert({ a }: { a: boolean }, b: unknown): asserts b; + // declare const nullableString: string | null; + // declare const boo: boolean; + // assert(boo, nullableString ?? ""); + // `, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: ` + // declare function assert(a: boolean, b: unknown): asserts b; + // declare function assert({ a }: { a: boolean }, b: unknown): asserts b; + // declare const nullableString: string | null; + // declare const boo: boolean; + // assert(boo, Boolean(nullableString)); + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` function assert(one: unknown): asserts one; function assert(one: unknown, two: unknown): asserts two; function assert(...args: unknown[]) { @@ -3175,145 +3147,145 @@ function assert(...args: unknown[]) { declare const nullableString: string | null; assert(nullableString); `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableString", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareNullish", -// Output: ` -// function assert(one: unknown): asserts one; -// function assert(one: unknown, two: unknown): asserts two; -// function assert(...args: unknown[]) { -// throw new Error('not implemented'); -// } -// declare const nullableString: string | null; -// assert(nullableString != null); -// `, -// }, -// { -// MessageId: "conditionFixDefaultEmptyString", -// Output: ` -// function assert(one: unknown): asserts one; -// function assert(one: unknown, two: unknown): asserts two; -// function assert(...args: unknown[]) { -// throw new Error('not implemented'); -// } -// declare const nullableString: string | null; -// assert(nullableString ?? ""); -// `, -// }, -// { -// MessageId: "conditionFixCastBoolean", -// Output: ` -// function assert(one: unknown): asserts one; -// function assert(one: unknown, two: unknown): asserts two; -// function assert(...args: unknown[]) { -// throw new Error('not implemented'); -// } -// declare const nullableString: string | null; -// assert(Boolean(nullableString)); -// `, -// }, -// }, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // function assert(one: unknown): asserts one; + // function assert(one: unknown, two: unknown): asserts two; + // function assert(...args: unknown[]) { + // throw new Error('not implemented'); + // } + // declare const nullableString: string | null; + // assert(nullableString != null); + // `, + // }, + // { + // MessageId: "conditionFixDefaultEmptyString", + // Output: ` + // function assert(one: unknown): asserts one; + // function assert(one: unknown, two: unknown): asserts two; + // function assert(...args: unknown[]) { + // throw new Error('not implemented'); + // } + // declare const nullableString: string | null; + // assert(nullableString ?? ""); + // `, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: ` + // function assert(one: unknown): asserts one; + // function assert(one: unknown, two: unknown): asserts two; + // function assert(...args: unknown[]) { + // throw new Error('not implemented'); + // } + // declare const nullableString: string | null; + // assert(Boolean(nullableString)); + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` ['one', 'two', ''].find(x => { return x; }); `, - Options: StrictBooleanExpressionsOptions{AllowString: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorString", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "explicitBooleanReturnType", -// Output: ` -// ['one', 'two', ''].find((x): boolean => { -// return x; -// }); -// `, -// }, -// }, + Options: StrictBooleanExpressionsOptions{AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "explicitBooleanReturnType", + // Output: ` + // ['one', 'two', ''].find((x): boolean => { + // return x; + // }); + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` ['one', 'two', ''].find(x => { return; }); `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullish", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "explicitBooleanReturnType", -// Output: ` -// ['one', 'two', ''].find((x): boolean => { -// return; -// }); -// `, -// }, -// }, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "explicitBooleanReturnType", + // Output: ` + // ['one', 'two', ''].find((x): boolean => { + // return; + // }); + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` ['one', 'two', ''].findLast(x => { return undefined; }); `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullish", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "explicitBooleanReturnType", -// Output: ` -// ['one', 'two', ''].findLast((x): boolean => { -// return undefined; -// }); -// `, -// }, -// }, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "explicitBooleanReturnType", + // Output: ` + // ['one', 'two', ''].findLast((x): boolean => { + // return undefined; + // }); + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` ['one', 'two', ''].find(x => { if (x) { return Math.random() > 0.5; } }); `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableBoolean", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "explicitBooleanReturnType", -// Output: ` -// ['one', 'two', ''].find((x): boolean => { -// if (x) { -// return Math.random() > 0.5; -// } -// }); -// `, -// }, -// }, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableBoolean", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "explicitBooleanReturnType", + // Output: ` + // ['one', 'two', ''].find((x): boolean => { + // if (x) { + // return Math.random() > 0.5; + // } + // }); + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` const predicate = (x: string) => { if (x) { return Math.random() > 0.5; @@ -3322,323 +3294,323 @@ const predicate = (x: string) => { ['one', 'two', ''].find(predicate); `, - Options: StrictBooleanExpressionsOptions{AllowNullableBoolean: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableBoolean", + Options: StrictBooleanExpressionsOptions{AllowNullableBoolean: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableBoolean", + }, }, }, - }, - { - Code: ` + { + Code: ` [1, null].every(async x => { return x != null; }); `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "predicateCannotBeAsync", + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "predicateCannotBeAsync", + }, }, }, - }, - { - Code: ` + { + Code: ` const predicate = async x => { return x != null; }; [1, null].every(predicate); `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorObject", + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorObject", + }, }, }, - }, - { - Code: ` + { + Code: ` [1, null].every((x): boolean | number => { return x != null; }); `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorOther", + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorOther", + }, }, }, - }, - { - Code: ` + { + Code: ` [1, null].every((x): boolean | undefined => { return x != null; }); `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableBoolean", + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableBoolean", + }, }, }, - }, - { - Code: ` + { + Code: ` [1, null].every((x, i) => {}); `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullish", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "explicitBooleanReturnType", -// Output: ` -// [1, null].every((x, i): boolean => {}); -// `, -// }, -// }, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "explicitBooleanReturnType", + // Output: ` + // [1, null].every((x, i): boolean => {}); + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` [() => {}, null].every((x: () => void) => {}); `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullish", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "explicitBooleanReturnType", -// Output: ` -// [() => {}, null].every((x: () => void): boolean => {}); -// `, -// }, -// }, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "explicitBooleanReturnType", + // Output: ` + // [() => {}, null].every((x: () => void): boolean => {}); + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` [() => {}, null].every(function (x: () => void) {}); `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullish", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "explicitBooleanReturnType", -// Output: ` -// [() => {}, null].every(function (x: () => void): boolean {}); -// `, -// }, -// }, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "explicitBooleanReturnType", + // Output: ` + // [() => {}, null].every(function (x: () => void): boolean {}); + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` [() => {}, null].every(() => {}); `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullish", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "explicitBooleanReturnType", -// Output: ` -// [() => {}, null].every((): boolean => {}); -// `, -// }, -// }, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullish", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "explicitBooleanReturnType", + // Output: ` + // [() => {}, null].every((): boolean => {}); + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` declare function f(x: number): string; declare function f(x: string | null): boolean; [35].filter(f); `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorOther", + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorOther", + }, }, }, - }, - { - Code: ` + { + Code: ` declare function f(x: number): string; declare function f(x: number | boolean): boolean; declare function f(x: string | null): boolean; [35].filter(f); `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorOther", + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorOther", + }, }, }, - }, - { - Code: ` + { + Code: ` declare function foo(x: number): T; [1, null].every(foo); `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorAny", + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorAny", + }, }, }, - }, - { - Code: ` + { + Code: ` function foo(x: number): T {} [1, null].every(foo); `, - Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNumber", + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", + }, }, }, - }, - { - Code: ` + { + Code: ` declare const nullOrString: string | null; ['one', null].filter(x => nullOrString); `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableString", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareNullish", -// Output: ` -// declare const nullOrString: string | null; -// ['one', null].filter(x => nullOrString != null); -// `, -// }, -// { -// MessageId: "conditionFixDefaultEmptyString", -// Output: ` -// declare const nullOrString: string | null; -// ['one', null].filter(x => nullOrString ?? ""); -// `, -// }, -// { -// MessageId: "conditionFixCastBoolean", -// Output: ` -// declare const nullOrString: string | null; -// ['one', null].filter(x => Boolean(nullOrString)); -// `, -// }, -// { -// MessageId: "explicitBooleanReturnType", -// Output: ` -// declare const nullOrString: string | null; -// ['one', null].filter((x): boolean => nullOrString); -// `, -// }, -// }, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // declare const nullOrString: string | null; + // ['one', null].filter(x => nullOrString != null); + // `, + // }, + // { + // MessageId: "conditionFixDefaultEmptyString", + // Output: ` + // declare const nullOrString: string | null; + // ['one', null].filter(x => nullOrString ?? ""); + // `, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: ` + // declare const nullOrString: string | null; + // ['one', null].filter(x => Boolean(nullOrString)); + // `, + // }, + // { + // MessageId: "explicitBooleanReturnType", + // Output: ` + // declare const nullOrString: string | null; + // ['one', null].filter((x): boolean => nullOrString); + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` declare const nullOrString: string | null; ['one', null].filter(x => !nullOrString); `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableString", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareNullish", -// Output: ` -// declare const nullOrString: string | null; -// ['one', null].filter(x => nullOrString == null); -// `, -// }, -// { -// MessageId: "conditionFixDefaultEmptyString", -// Output: ` -// declare const nullOrString: string | null; -// ['one', null].filter(x => !(nullOrString ?? "")); -// `, -// }, -// { -// MessageId: "conditionFixCastBoolean", -// Output: ` -// declare const nullOrString: string | null; -// ['one', null].filter(x => !Boolean(nullOrString)); -// `, -// }, -// }, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // declare const nullOrString: string | null; + // ['one', null].filter(x => nullOrString == null); + // `, + // }, + // { + // MessageId: "conditionFixDefaultEmptyString", + // Output: ` + // declare const nullOrString: string | null; + // ['one', null].filter(x => !(nullOrString ?? "")); + // `, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: ` + // declare const nullOrString: string | null; + // ['one', null].filter(x => !Boolean(nullOrString)); + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` declare const anyValue: any; ['one', null].filter(x => anyValue); `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorAny", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCastBoolean", -// Output: ` -// declare const anyValue: any; -// ['one', null].filter(x => Boolean(anyValue)); -// `, -// }, -// { -// MessageId: "explicitBooleanReturnType", -// Output: ` -// declare const anyValue: any; -// ['one', null].filter((x): boolean => anyValue); -// `, -// }, -// }, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorAny", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCastBoolean", + // Output: ` + // declare const anyValue: any; + // ['one', null].filter(x => Boolean(anyValue)); + // `, + // }, + // { + // MessageId: "explicitBooleanReturnType", + // Output: ` + // declare const anyValue: any; + // ['one', null].filter((x): boolean => anyValue); + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` declare const nullOrBoolean: boolean | null; [true, null].filter(x => nullOrBoolean); `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableBoolean", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixDefaultFalse", -// Output: ` -// declare const nullOrBoolean: boolean | null; -// [true, null].filter(x => nullOrBoolean ?? false); -// `, -// }, -// { -// MessageId: "conditionFixCompareTrue", -// Output: ` -// declare const nullOrBoolean: boolean | null; -// [true, null].filter(x => nullOrBoolean === true); -// `, -// }, -// { -// MessageId: "explicitBooleanReturnType", -// Output: ` -// declare const nullOrBoolean: boolean | null; -// [true, null].filter((x): boolean => nullOrBoolean); -// `, -// }, -// }, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableBoolean", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixDefaultFalse", + // Output: ` + // declare const nullOrBoolean: boolean | null; + // [true, null].filter(x => nullOrBoolean ?? false); + // `, + // }, + // { + // MessageId: "conditionFixCompareTrue", + // Output: ` + // declare const nullOrBoolean: boolean | null; + // [true, null].filter(x => nullOrBoolean === true); + // `, + // }, + // { + // MessageId: "explicitBooleanReturnType", + // Output: ` + // declare const nullOrBoolean: boolean | null; + // [true, null].filter((x): boolean => nullOrBoolean); + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` enum ExampleEnum { This = 0, That = 1, @@ -3646,260 +3618,260 @@ enum ExampleEnum { const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; [0, 1].filter(x => theEnum); `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableEnum", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareNullish", -// Output: ` -// enum ExampleEnum { -// This = 0, -// That = 1, -// } -// const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; -// [0, 1].filter(x => theEnum != null); -// `, -// }, -// { -// MessageId: "explicitBooleanReturnType", -// Output: ` -// enum ExampleEnum { -// This = 0, -// That = 1, -// } -// const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; -// [0, 1].filter((x): boolean => theEnum); -// `, -// }, -// }, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableEnum", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // enum ExampleEnum { + // This = 0, + // That = 1, + // } + // const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + // [0, 1].filter(x => theEnum != null); + // `, + // }, + // { + // MessageId: "explicitBooleanReturnType", + // Output: ` + // enum ExampleEnum { + // This = 0, + // That = 1, + // } + // const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null; + // [0, 1].filter((x): boolean => theEnum); + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` declare const nullOrNumber: number | null; [0, null].filter(x => nullOrNumber); `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableNumber", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareNullish", -// Output: ` -// declare const nullOrNumber: number | null; -// [0, null].filter(x => nullOrNumber != null); -// `, -// }, -// { -// MessageId: "conditionFixDefaultZero", -// Output: ` -// declare const nullOrNumber: number | null; -// [0, null].filter(x => nullOrNumber ?? 0); -// `, -// }, -// { -// MessageId: "conditionFixCastBoolean", -// Output: ` -// declare const nullOrNumber: number | null; -// [0, null].filter(x => Boolean(nullOrNumber)); -// `, -// }, -// { -// MessageId: "explicitBooleanReturnType", -// Output: ` -// declare const nullOrNumber: number | null; -// [0, null].filter((x): boolean => nullOrNumber); -// `, -// }, -// }, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // declare const nullOrNumber: number | null; + // [0, null].filter(x => nullOrNumber != null); + // `, + // }, + // { + // MessageId: "conditionFixDefaultZero", + // Output: ` + // declare const nullOrNumber: number | null; + // [0, null].filter(x => nullOrNumber ?? 0); + // `, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: ` + // declare const nullOrNumber: number | null; + // [0, null].filter(x => Boolean(nullOrNumber)); + // `, + // }, + // { + // MessageId: "explicitBooleanReturnType", + // Output: ` + // declare const nullOrNumber: number | null; + // [0, null].filter((x): boolean => nullOrNumber); + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` const objectValue: object = {}; [{ a: 0 }, {}].filter(x => objectValue); `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorObject", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "explicitBooleanReturnType", -// Output: ` -// const objectValue: object = {}; -// [{ a: 0 }, {}].filter((x): boolean => objectValue); -// `, -// }, -// }, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorObject", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "explicitBooleanReturnType", + // Output: ` + // const objectValue: object = {}; + // [{ a: 0 }, {}].filter((x): boolean => objectValue); + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` const objectValue: object = {}; [{ a: 0 }, {}].filter(x => { return objectValue; }); `, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorObject", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "explicitBooleanReturnType", -// Output: ` -// const objectValue: object = {}; -// [{ a: 0 }, {}].filter((x): boolean => { -// return objectValue; -// }); -// `, -// }, -// }, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorObject", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "explicitBooleanReturnType", + // Output: ` + // const objectValue: object = {}; + // [{ a: 0 }, {}].filter((x): boolean => { + // return objectValue; + // }); + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` declare const nullOrObject: object | null; [{ a: 0 }, null].filter(x => nullOrObject); `, - Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNullableObject", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareNullish", -// Output: ` -// declare const nullOrObject: object | null; -// [{ a: 0 }, null].filter(x => nullOrObject != null); -// `, -// }, -// { -// MessageId: "explicitBooleanReturnType", -// Output: ` -// declare const nullOrObject: object | null; -// [{ a: 0 }, null].filter((x): boolean => nullOrObject); -// `, -// }, -// }, + Options: StrictBooleanExpressionsOptions{AllowNullableObject: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableObject", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareNullish", + // Output: ` + // declare const nullOrObject: object | null; + // [{ a: 0 }, null].filter(x => nullOrObject != null); + // `, + // }, + // { + // MessageId: "explicitBooleanReturnType", + // Output: ` + // declare const nullOrObject: object | null; + // [{ a: 0 }, null].filter((x): boolean => nullOrObject); + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` const numbers: number[] = [1]; [1, 2].filter(x => numbers.length); `, - Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNumber", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareArrayLengthNonzero", -// Output: ` -// const numbers: number[] = [1]; -// [1, 2].filter(x => numbers.length > 0); -// `, -// }, -// { -// MessageId: "explicitBooleanReturnType", -// Output: ` -// const numbers: number[] = [1]; -// [1, 2].filter((x): boolean => numbers.length); -// `, -// }, -// }, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareArrayLengthNonzero", + // Output: ` + // const numbers: number[] = [1]; + // [1, 2].filter(x => numbers.length > 0); + // `, + // }, + // { + // MessageId: "explicitBooleanReturnType", + // Output: ` + // const numbers: number[] = [1]; + // [1, 2].filter((x): boolean => numbers.length); + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` const numberValue: number = 1; [1, 2].filter(x => numberValue); `, - Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorNumber", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareZero", -// Output: ` -// const numberValue: number = 1; -// [1, 2].filter(x => numberValue !== 0); -// `, -// }, -// { -// MessageId: "conditionFixCompareNaN", -// Output: ` -// const numberValue: number = 1; -// [1, 2].filter(x => !Number.isNaN(numberValue)); -// `, -// }, -// { -// MessageId: "conditionFixCastBoolean", -// Output: ` -// const numberValue: number = 1; -// [1, 2].filter(x => Boolean(numberValue)); -// `, -// }, -// { -// MessageId: "explicitBooleanReturnType", -// Output: ` -// const numberValue: number = 1; -// [1, 2].filter((x): boolean => numberValue); -// `, -// }, -// }, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareZero", + // Output: ` + // const numberValue: number = 1; + // [1, 2].filter(x => numberValue !== 0); + // `, + // }, + // { + // MessageId: "conditionFixCompareNaN", + // Output: ` + // const numberValue: number = 1; + // [1, 2].filter(x => !Number.isNaN(numberValue)); + // `, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: ` + // const numberValue: number = 1; + // [1, 2].filter(x => Boolean(numberValue)); + // `, + // }, + // { + // MessageId: "explicitBooleanReturnType", + // Output: ` + // const numberValue: number = 1; + // [1, 2].filter((x): boolean => numberValue); + // `, + // }, + // }, + }, }, }, - }, - { - Code: ` + { + Code: ` const stringValue: string = 'hoge'; ['hoge', 'foo'].filter(x => stringValue); `, - Options: StrictBooleanExpressionsOptions{AllowString: utils.Ref(false)}, - Errors: []rule_tester.InvalidTestCaseError{ - { - MessageId: "conditionErrorString", -// Suggestions: []rule_tester.InvalidTestCaseSuggestion{ -// { -// MessageId: "conditionFixCompareStringLength", -// Output: ` -// const stringValue: string = 'hoge'; -// ['hoge', 'foo'].filter(x => stringValue.length > 0); -// `, -// }, -// { -// MessageId: "conditionFixCompareEmptyString", -// Output: ` -// const stringValue: string = 'hoge'; -// ['hoge', 'foo'].filter(x => stringValue !== ""); -// `, -// }, -// { -// MessageId: "conditionFixCastBoolean", -// Output: ` -// const stringValue: string = 'hoge'; -// ['hoge', 'foo'].filter(x => Boolean(stringValue)); -// `, -// }, -// { -// MessageId: "explicitBooleanReturnType", -// Output: ` -// const stringValue: string = 'hoge'; -// ['hoge', 'foo'].filter((x): boolean => stringValue); -// `, -// }, -// }, - }, - }, - }, - }) + Options: StrictBooleanExpressionsOptions{AllowString: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorString", + // Suggestions: []rule_tester.InvalidTestCaseSuggestion{ + // { + // MessageId: "conditionFixCompareStringLength", + // Output: ` + // const stringValue: string = 'hoge'; + // ['hoge', 'foo'].filter(x => stringValue.length > 0); + // `, + // }, + // { + // MessageId: "conditionFixCompareEmptyString", + // Output: ` + // const stringValue: string = 'hoge'; + // ['hoge', 'foo'].filter(x => stringValue !== ""); + // `, + // }, + // { + // MessageId: "conditionFixCastBoolean", + // Output: ` + // const stringValue: string = 'hoge'; + // ['hoge', 'foo'].filter(x => Boolean(stringValue)); + // `, + // }, + // { + // MessageId: "explicitBooleanReturnType", + // Output: ` + // const stringValue: string = 'hoge'; + // ['hoge', 'foo'].filter((x): boolean => stringValue); + // `, + // }, + // }, + }, + }, + }, + }) } From b48b8cd56901e7763ae8a8763f6ee98795b4ba78 Mon Sep 17 00:00:00 2001 From: Cameron Clark Date: Thu, 23 Oct 2025 13:45:05 +0100 Subject: [PATCH 26/27] fix --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7793d79d..a6e0b014 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,3 @@ internal/collections/ e2e/node_modules/ node_modules/ -.idea/ From 8b895423d22ab4f0ceaeee6012bf631c91c28d5a Mon Sep 17 00:00:00 2001 From: Cameron Clark Date: Thu, 23 Oct 2025 13:45:34 +0100 Subject: [PATCH 27/27] u --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index a6e0b014..3663a5a5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ /tsgolint internal/collections/ e2e/node_modules/ -node_modules/ - +node_modules/ \ No newline at end of file