This rule raises an issue when an object, class, or module defines a property or method named then.
JavaScript treats objects with a then property as "thenable" - similar to Promises. This can cause unexpected behavior when these
objects are used with await expressions or dynamic imports.
When JavaScript encounters an object with a then property in an await expression, it assumes the object is Promise-like
and tries to resolve it. This can lead to:
then method doesn’t behave like a Promise
then method throws an exception
The same issue occurs with dynamic imports. If a module exports a then function, the import statement will treat the entire module as
thenable, potentially calling the then function instead of returning the module object.
This behavior stems from JavaScript’s Promise resolution algorithm, which checks for a then property to determine if an object
should be treated as a Promise.
This issue can cause applications to hang indefinitely, throw unexpected errors, or fail silently. In production environments, this could lead to
unresponsive user interfaces, incomplete data processing, or application crashes. The debugging experience is particularly poor because the connection
between the then property and the problematic behavior is not immediately obvious.
Rename the then property to a more descriptive name that reflects its actual purpose.
const userAction = {
userId: 123,
then() { // Noncompliant
console.log('Action completed');
}
};
const userAction = {
userId: 123,
complete() {
console.log('Action completed');
}
};
The rule does not raise an issue when then is used as part of a JSON Schema conditional validation construct alongside an
if property. In JSON Schema, if/then/else are standard keywords for conditional schema application
and do not create thenable behavior.
const schema = {
if: { properties: { age: { minimum: 18 } } },
then: { required: ['driversLicense'] }, // Compliant - JSON Schema conditional
else: { required: ['parentConsent'] },
};
No issue will be raised when then is used as a type reference (assigning the Function constructor as a value) in an
interface shape descriptor. This pattern describes the expected interface shape of an object, not a method creating thenable behavior.
const connectionInterface = {
open: Function,
send: Function,
then: Function, // Compliant - interface shape descriptor
close: Function,
};
No issue will be raised when a then property is inside an object that also has an is property and the project uses a
validation library such as Yup or Joi. This {is, then} pattern is recognized as a conditional validation configuration used by methods
like .when() and .conditional() in these libraries.
import * as yup from 'yup';
const schema = yup.string().when('hasName', {
is: true,
then: (schema) => schema.required(), // Compliant: conditional validation config
});
const formSchema = yup.object().shape({
parentalConsent: yup.bool().when('age', {
is: (age) => age < 18,
then: (schema) => schema.required(), // Compliant: conditional validation config
}),
});
import Joi from 'joi';
const schema = Joi.string().when('type', {
is: 'email',
then: Joi.string().email().required(), // Compliant: conditional validation config
});