Create a deferred promise with typed resolution

TypeScript
// A promise whose resolve and reject handles can be handed to other code.
interface Deferred<T> {
  readonly promise: Promise<T>;
  resolve(value: T | PromiseLike<T>): void;
  reject(reason?: unknown): void;
}

function createDeferred<T>(timeoutMs?: number): Deferred<T> {
  let resolve!: (value: T | PromiseLike<T>) => void;
  let reject!: (reason?: unknown) => void;
  const promise = new Promise<T>((res, rej) => {
    resolve = res;
    reject = rej;
  });

  if (timeoutMs !== undefined) {
    const timer = setTimeout(() => reject(new Error(`timed out after ${timeoutMs}ms`)), timeoutMs);
    const clear = () => clearTimeout(timer);
    promise.then(clear, clear);
  }

  return { promise, resolve, reject };
}

const workerReady = createDeferred<string>(5_000);
setTimeout(() => workerReady.resolve('worker online'), 50);
workerReady.promise.then((message) => console.log(message)); // worker online