isThenable
Checks whether a value is a thenable — an object or function with a callable then method that matches the minimal PromiseLike<T> shape. This helper is intentionally narrow and does not attempt to validate full Promise behavior, only the presence and type of then.
It never mutates input and never invokes the then method. Use it when you need to branch on "can I treat this like a Promise-like value?" before attaching handlers or normalizing to a concrete Promise.
Signature
function isThenable<T = unknown>(value: unknown): value is PromiseLike<T>
Parameters
| Name | Type | Description |
|---|---|---|
| value | unknown |
The value to normalize. |
Returns
true: If value is notnullorundefined, is an object or function, and has athenproperty whose type is function.false: For all other values, including primitives, objects withoutthen, and objects whosethenproperty is not a function.
Behavior
- Accepts both objects and functions as potential thenables.
- Rejects
nullandundefinedimmediately. - Requires a
then,own, or inherited property whose value is a function. - Does not invoke
then; it only checks for the presence and type of the method. - Does not coerce or wrap values; the original value is preserved.
- Never mutates input.
- The current implementation directly accesses the
thenproperty; if a value exposes a hostile then getter that throws, this helper will propagate that exception rather than swallowing it.
Examples
isThenable(Promise.resolve(1));
// true (narrowed to PromiseLike<number>)
isThenable({
then(onFulfilled: (value: string) => void) {
onFulfilled("ok");
},
});
// true (custom thenable)
const fnThenable = Object.assign(
() => "value",
{
then(onFulfilled: (value: number) => void) {
onFulfilled(42);
},
},
);
isThenable(fnThenable);
// true (functions with then are supported)
isThenable(123);
// false
isThenable({ then: "not-a-function" });
// false
isThenable(null);
// false
declare const maybeThenable: unknown;
if (isThenable<{ id: number }>(maybeThenable)) {
maybeThenable.then((value) => {
// value is { id: number } here
});
}
Notes
- This helper deliberately targets the minimal and widely used
PromiseLike<T>pattern: "has a callable then". It does not assert on catch, finally, orPromisebrand-specific behavior. - Because it only checks shape, plain objects and functions with a compatible
thenmethod are accepted, which matches how the Promise resolution algorithm itself treats thenables. - The generic parameter
Texists purely for type narrowing and does not affect runtime behavior; it lets you line up the runtime check with the expected fulfillment type when working withunknown. - The helper never invokes
thenand never constructs new Promises. It is suitable as a low-level building block in higher-level normalization helpers that may wrap thenables in concrete Promises or branch between sync and async paths.