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..928c59a1 --- /dev/null +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions.go @@ -0,0 +1,618 @@ +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 buildConditionErrorNumberMessage() rule.RuleMessage { + return rule.RuleMessage{ + Id: "conditionErrorNumber", + Description: "Unexpected number value in conditional. A number can be falsy (0, NaN) or truthy.", + } +} + +func buildConditionErrorStringMessage() rule.RuleMessage { + return rule.RuleMessage{ + Id: "conditionErrorString", + Description: "Unexpected string value in conditional. A string can be falsy (empty string) or truthy.", + } +} + +func buildConditionErrorObjectMessage() rule.RuleMessage { + return rule.RuleMessage{ + Id: "conditionErrorObject", + Description: "Unexpected object value in conditional. An object is always truthy.", + } +} + +func buildConditionErrorNullishMessage() rule.RuleMessage { + return rule.RuleMessage{ + Id: "conditionErrorNullish", + Description: "Unexpected nullish value in conditional. The expression is always falsy.", + } +} + +func buildConditionErrorOtherMessage() rule.RuleMessage { + return rule.RuleMessage{ + Id: "conditionErrorOther", + Description: "Unexpected value in conditional. A union of different types has inconsistent truthiness.", + } +} + +func buildConditionErrorNullableBooleanMessage() rule.RuleMessage { + return rule.RuleMessage{ + Id: "conditionErrorNullableBoolean", + Description: "Unexpected nullable boolean value in conditional. Please handle the nullish case explicitly.", + } +} + +func buildConditionErrorNullableObjectMessage() rule.RuleMessage { + return rule.RuleMessage{ + Id: "conditionErrorNullableObject", + Description: "Unexpected nullable object value in conditional. Please handle the nullish case explicitly.", + } +} + +func buildConditionErrorNullableStringMessage() rule.RuleMessage { + return rule.RuleMessage{ + Id: "conditionErrorNullableString", + Description: "Unexpected nullable string value in conditional. Please handle the nullish and empty string cases explicitly.", + } +} + +func buildConditionErrorNullableNumberMessage() rule.RuleMessage { + return rule.RuleMessage{ + Id: "conditionErrorNullableNumber", + Description: "Unexpected nullable number value in conditional. Please handle the nullish and zero cases explicitly.", + } +} + +func buildConditionErrorNullableEnumMessage() rule.RuleMessage { + return rule.RuleMessage{ + Id: "conditionErrorNullableEnum", + Description: "Unexpected nullable enum value in conditional. Please handle the nullish and falsy enum cases explicitly.", + } +} + +func buildConditionErrorAnyMessage() rule.RuleMessage { + return rule.RuleMessage{ + Id: "conditionErrorAny", + Description: "Unexpected any value in conditional. Use a more specific type to ensure type safety.", + } +} + +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 buildPredicateCannotBeAsyncMessage() 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{ + 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(true) + } + 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), + buildNoStrictNullCheckMessage(), + ) + } + + return rule.RuleListeners{ + ast.KindIfStatement: func(node *ast.Node) { + ifStmt := node.AsIfStatement() + traverseNode(ctx, ifStmt.Expression, opts, true) + }, + ast.KindWhileStatement: func(node *ast.Node) { + whileStmt := node.AsWhileStatement() + traverseNode(ctx, whileStmt.Expression, opts, true) + }, + ast.KindDoStatement: func(node *ast.Node) { + doStmt := node.AsDoStatement() + traverseNode(ctx, doStmt.Expression, opts, true) + }, + ast.KindForStatement: func(node *ast.Node) { + forStmt := node.AsForStatement() + if forStmt.Condition != nil { + traverseNode(ctx, forStmt.Condition, opts, true) + } + }, + ast.KindConditionalExpression: func(node *ast.Node) { + condExpr := node.AsConditionalExpression() + traverseNode(ctx, condExpr.Condition, opts, true) + }, + ast.KindBinaryExpression: func(node *ast.Node) { + binExpr := node.AsBinaryExpression() + if ast.IsLogicalExpression(node) && binExpr.OperatorToken.Kind != ast.KindQuestionQuestionToken { + traverseLogicalExpression(ctx, binExpr, opts, false) + } + }, + ast.KindCallExpression: func(node *ast.Node) { + callExpr := node.AsCallExpression() + + assertedArgument := findTruthinessAssertedArgument(ctx.TypeChecker, callExpr) + if assertedArgument != nil { + traverseNode(ctx, assertedArgument, opts, true) + } + + if utils.IsArrayMethodCallWithPredicate(ctx.TypeChecker, callExpr) { + if callExpr.Arguments != nil && len(callExpr.Arguments.Nodes) > 0 { + arg := callExpr.Arguments.Nodes[0] + 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, buildPredicateCannotBeAsyncMessage()) + return + } + 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) + if typeFlags&checker.TypeFlagsTypeParameter != 0 { + constraint := ctx.TypeChecker.GetConstraintOfTypeParameter(returnType) + if constraint != nil { + returnType = constraint + } + } + + types = append(types, utils.UnionTypeParts(returnType)...) + } + checkCondition(ctx, node, types, opts) + } + } + }, + ast.KindPrefixUnaryExpression: func(node *ast.Node) { + unaryExpr := node.AsPrefixUnaryExpression() + if unaryExpr.Operator == ast.KindExclamationToken { + traverseNode(ctx, unaryExpr.Operand, opts, true) + } + }, + } + }, +} + +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 { + break + } + checkableArguments = append(checkableArguments, argument) + } + 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 + } + parameterIndex := checker.TypePredicate_parameterIndex(firstTypePredicateResult) + if int(parameterIndex) >= len(checkableArguments) { + return nil + } + return checkableArguments[parameterIndex] +} + +func checkNode(ctx rule.RuleContext, node *ast.Node, opts StrictBooleanExpressionsOptions) { + nodeType := utils.GetConstrainedTypeAtLocation(ctx.TypeChecker, node) + checkCondition(ctx, node, utils.UnionTypeParts(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 traversedNodes.Has(node) { + return + } + 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 { + traverseLogicalExpression(ctx, binExpr, opts, isCondition) + return + } + } + + if !isCondition { + return + } + + checkNode(ctx, node, opts) +} + +// 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 analyzeTypeParts(typeChecker *checker.Checker, types []*checker.Type) typeInfo { + info := typeInfo{ + isUnion: len(types) > 1, + types: types, + } + variants := make(map[typeVariant]bool) + + metNotTruthy := false + + for _, part := range info.types { + 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 || partInfo.variant == typeVariantBigInt { + if metNotTruthy { + continue + } + info.isTruthy = partInfo.isTruthy + metNotTruthy = !partInfo.isTruthy + } + } + + 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 + } + } + } else { + info.variant = typeVariantMixed + } + + return info +} + +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 + } + + 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|checker.TypeFlagsBooleanLike) != 0 { + if t.AsLiteralType().String() == "true" { + info.isTruthy = true + } + info.variant = typeVariantBoolean + 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|checker.TypeFlagsStringLike) != 0 { + info.variant = typeVariantString + if t.IsStringLiteral() { + literal := t.AsLiteralType() + if literal != nil && literal.Value() != "" { + info.isTruthy = true + } + } + return info + } + + if flags&(checker.TypeFlagsNumber|checker.TypeFlagsNumberLiteral|checker.TypeFlagsNumberLike) != 0 { + info.variant = typeVariantNumber + 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 t.IsBigIntLiteral() { + literal := t.AsLiteralType() + if literal != nil && literal.String() != "0" { + info.isTruthy = true + } + } + return info + } + + if flags&(checker.TypeFlagsESSymbol|checker.TypeFlagsUniqueESSymbol) != 0 { + info.variant = typeVariantObject + return info + } + + if flags&(checker.TypeFlagsObject|checker.TypeFlagsNonPrimitive) != 0 { + info.variant = typeVariantObject + return info + } + + info.variant = typeVariantMixed + return info +} + +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: + if !*opts.AllowAny { + ctx.ReportNode(node, buildConditionErrorAnyMessage()) + } + return + case typeVariantNever: + return + case typeVariantNullish: + ctx.ReportNode(node, buildConditionErrorNullishMessage()) + case typeVariantString: + // Known edge case: truthy primitives and nullish values are always valid boolean expressions + if *opts.AllowString && info.isTruthy { + return + } + + if info.isNullable { + if info.isEnum { + if !*opts.AllowNullableEnum { + ctx.ReportNode(node, buildConditionErrorNullableEnumMessage()) + } + } else { + if !*opts.AllowNullableString { + ctx.ReportNode(node, buildConditionErrorNullableStringMessage()) + } + } + } else if !*opts.AllowString { + ctx.ReportNode(node, buildConditionErrorStringMessage()) + } + case typeVariantNumber: + // Known edge case: truthy primitives and nullish values are always valid boolean expressions + if *opts.AllowNumber && info.isTruthy { + return + } + + if info.isNullable { + if info.isEnum { + if !*opts.AllowNullableEnum { + ctx.ReportNode(node, buildConditionErrorNullableEnumMessage()) + } + } else { + if !*opts.AllowNullableNumber { + ctx.ReportNode(node, buildConditionErrorNullableNumberMessage()) + } + } + } else if !*opts.AllowNumber { + ctx.ReportNode(node, buildConditionErrorNumberMessage()) + } + 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, buildConditionErrorNullableBooleanMessage()) + } + case typeVariantObject: + if info.isNullable && !*opts.AllowNullableObject { + ctx.ReportNode(node, buildConditionErrorNullableObjectMessage()) + } else if !info.isNullable { + ctx.ReportNode(node, buildConditionErrorObjectMessage()) + } + case typeVariantMixed: + if info.isEnum { + if info.isNullable && !*opts.AllowNullableEnum { + ctx.ReportNode(node, buildConditionErrorNullableEnumMessage()) + } + return + } + ctx.ReportNode(node, buildConditionErrorOtherMessage()) + case typeVariantBigInt: + if info.isNullable && !*opts.AllowNullableNumber { + ctx.ReportNode(node, buildConditionErrorNullableNumberMessage()) + } else if !info.isNullable && !*opts.AllowNumber { + 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 new file mode 100644 index 00000000..da7a7a94 --- /dev/null +++ b/internal/rules/strict_boolean_expressions/strict_boolean_expressions_test.go @@ -0,0 +1,3877 @@ +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) { + rule_tester.RunRuleTester( + fixtures.GetRootDir(), + "tsconfig.json", + t, + &StrictBooleanExpressionsRule, + []rule_tester.ValidTestCase{ + { + 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?: { 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: ` +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{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; +} + +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; +declare const y: 'a'; +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; +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{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", + }, + { + 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: `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: `(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: `[] || 1;`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorObject", + }, + }, + }, + { + Code: `({}) && 'a';`, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorObject", + }, + }, + }, + { + Code: ` +declare const x: symbol; +if (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: ` 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) { +} + `, + 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) { +} + `, + 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) { +} + `, + 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) { +} + `, + 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) { +} + `, + TSConfig: "tsconfig.unstrict.json", + 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 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)); +// `, +// }, +// }, + }, + }, + }, + { + 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: ` +const predicate = async x => { + return x != null; +}; + +[1, null].every(predicate); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorObject", + }, + }, + }, + { + Code: ` +[1, null].every((x): boolean | number => { + return x != null; +}); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorOther", + }, + }, + }, + { + Code: ` +[1, null].every((x): boolean | undefined => { + return x != null; +}); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNullableBoolean", + }, + }, + }, + { + 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", + }, + }, + }, + { + 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", + }, + }, + }, + { + Code: ` +declare function foo(x: number): T; +[1, null].every(foo); + `, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorAny", + }, + }, + }, + { + Code: ` +function foo(x: number): T {} +[1, null].every(foo); + `, + Options: StrictBooleanExpressionsOptions{AllowNumber: utils.Ref(false)}, + Errors: []rule_tester.InvalidTestCaseError{ + { + MessageId: "conditionErrorNumber", + }, + }, + }, + { + 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); + // `, + // }, + // }, + }, + }, + }, + }) +} 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())) 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