Skip to content
Dashboard

Stress testing Biome's noFloatingPromises lint rule

Software Engineer

Link to headingWhat is a floating Promise?

Link to headingArray of Promises

[1, 2, 3].map(async (x) => x + 1)

Link to headingPromise-like objects

function normalPromise(): Promise<number> {
return new Promise((_, reject) => reject(2))
}
normalPromise() // linter warns: floating Promise

function promiseLike(): PromiseLike<number> {
return new Promise((_, reject) => reject(2))
}
promiseLike() // floating Promise

typescript-eslint handles this kind of case with a special configuration option for “Thenables” (another name for what PromiseLike represents)

Link to headingStructural typing tricks

/** a direct copy of the TypeScript `Promise` type, but with a different name */
interface Duck<T> {
then<TResult1 = T, TResult2 = never>(
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null
): Promise<TResult1 | TResult2>
catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<T | TResult>
}
function promise(): Duck<string> {
return new Promise((_, reject) => reject(2))
}
promise() // floating Promise (thenable)

Link to headingConditional type aliases

async function promiseLike() {
return new Promise((_, reject) => reject(2))
}
promiseLike() // linter warns: floating Promise

type Cheating<T extends 1> = T extends 1 ? Promise<string> : Promise<string>
async function promiseLike(): Cheating<1> {
return new Promise((_, reject) => reject(2))
}
promiseLike() // floating Promise

Link to headingProxy-based Promises

new Promise((_, reject) => reject(2)).then(() => {})

function createLazyPromise<
T extends string,
U extends (prop: PropertyKey) => Promise<T>,
>(getValue: U) {
let resolve: (value: T) => void
const promise = new Promise<T>((r) => {
resolve = r
return r
})
const proxy = new Proxy(promise as Promise<T>, {
get(target, prop, receiver) {
if (prop in target) {
return Reflect.get(target, prop, receiver)
}
// Access to any other property triggers resolution
getValue(prop).then(resolve) // floating promise
return undefined // Could also return another proxy here
},
})
return proxy as Promise<T>
}
const lazy = createLazyPromise((prop) =>
Promise.resolve(`You accessed: ${String(prop)}`),
)
lazy.then((result) => {
console.log(result) // floating Promise
})
(lazy as any).foo // floating Promise

Link to headingFrozen Promise objects

Object.freeze(new Promise((_, reject) => reject(2)))

Link to headingPromise-returning object members

const sneakyObject = {
rejectSomething() {
return new Promise((_, reject) => reject(2))
}
}
sneakyObject.rejectSomething() // floating Promise

Link to headingProperty getters

const sneakyObject2 = {
get something() {
return new Promise((_, reject) => reject(2))
},
}
sneakyObject2.something // floating Promise

Link to headingType remapping with getters

interface Things {
Thing: string
}
type CalculateGetter<T> = {
[K in keyof T as K extends string ? `get${K}` : never]: () => Promise<T[K]>
}
declare const lazyThings: CalculateGetter<Things>
lazyThings.getThing() // floating Promise

Link to headingShort-circuit operators

true && new Promise((_, reject) => reject(2)) // floating Promise

Link to headingRandomized expressions

Math.random() > 0.5
? new Promise((_, reject) => reject(2)) // floating Promise
: null

Link to headingOptional chaining fallbacks

const optionalObject: Record<string, (() => unknown) | undefined> = {}
optionalObject?.nonExistentMethod?.() || new Promise((_, reject) => reject(2))

Link to headingImmediately invoked functions

(() => new Promise((_, reject) => reject(2)))()

Link to headingThe comma operator

let _x = 5
_x++, new Promise((_, reject) => reject(2))

Link to headingWinner

Link to headingJoin us