Skip to content

Unexpected change in DOMException.stack type #2174

@Cherry

Description

@Cherry

In workers-types <=4.20240512.0, the following code worked without issue, causing no type errors to be raised at build time.

But since 4.20240524.0, an error is now raised:

Argument of type 'string | undefined' is not assignable to parameter of type 'string | Record<string, unknown>'.
  Type 'undefined' is not assignable to type 'string | Record<string, unknown>'.
class CustomError extends Error {
	data: Record<string, unknown>;
	constructor(message: string, data: Record<string, any>) {
		super(message);
		this.data = data;
	}
}

function handleError(err: Record<string, unknown> | string) {
	// do something
}

export default {
	async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
		try {
			// some code that might throw a custom error
			throw new CustomError('example', { code: 123 });

			// or some code that might throw a DOMException
			throw new DOMException('test');
		} catch (err) {
			if (err instanceof CustomError) {
				handleError(err.data);
			} else if (err instanceof DOMException) {

				// previously, `.stack` was defined as `any` by DOMException, so it passed the type check in handleError
				// this is no longer the case, and it inherits `string | undefined` from `Error` as its not defined explicitly anymore
				// so the following line will cause a TS build error
				handleError(err.stack);
			} else {
				// handle other errors
			}
		}
		return new Response('Hello World');
	},
};

This appears to be due to stack being omitted from DOMException, so it's now just inherited from the base Error class. Which results in it being stack?: string, so effectively string | undefined.

Previously, workers-types defined readonly stack: any in DOMException but that's no longer present as of 4.20240524.0, resulting in not only a type change, but also a property that was previously no longer optional, now being optional. I suspect this is due to changes in #2139.

DOMException reference

I've included a reference diff of what DOMException looks like as output in workers-types between these two versions.

workers-types 4.20240512.0

declare class DOMException extends Error {
  constructor(message?: string, name?: string);
  /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException/message) */
  readonly message: string;
  /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException/name) */
  readonly name: string;
  /**
   * @deprecated
   *
   * [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException/code)
   */
  readonly code: number;
  readonly stack: any;
  static readonly INDEX_SIZE_ERR: number;
  static readonly DOMSTRING_SIZE_ERR: number;
  static readonly HIERARCHY_REQUEST_ERR: number;
  static readonly WRONG_DOCUMENT_ERR: number;
  static readonly INVALID_CHARACTER_ERR: number;
  static readonly NO_DATA_ALLOWED_ERR: number;
  static readonly NO_MODIFICATION_ALLOWED_ERR: number;
  static readonly NOT_FOUND_ERR: number;
  static readonly NOT_SUPPORTED_ERR: number;
  static readonly INUSE_ATTRIBUTE_ERR: number;
  static readonly INVALID_STATE_ERR: number;
  static readonly SYNTAX_ERR: number;
  static readonly INVALID_MODIFICATION_ERR: number;
  static readonly NAMESPACE_ERR: number;
  static readonly INVALID_ACCESS_ERR: number;
  static readonly VALIDATION_ERR: number;
  static readonly TYPE_MISMATCH_ERR: number;
  static readonly SECURITY_ERR: number;
  static readonly NETWORK_ERR: number;
  static readonly ABORT_ERR: number;
  static readonly URL_MISMATCH_ERR: number;
  static readonly QUOTA_EXCEEDED_ERR: number;
  static readonly TIMEOUT_ERR: number;
  static readonly INVALID_NODE_TYPE_ERR: number;
  static readonly DATA_CLONE_ERR: number;
}

workers-types 4.20240524.0

declare class DOMException extends Error {
  constructor(message?: string, name?: string);
  /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException/message) */
  readonly message: string;
  /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException/name) */
  readonly name: string;
  /**
   * @deprecated
   *
   * [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException/code)
   */
  readonly code: number;
- readonly stack: any;
  static readonly INDEX_SIZE_ERR: number;
  static readonly DOMSTRING_SIZE_ERR: number;
  static readonly HIERARCHY_REQUEST_ERR: number;
  static readonly WRONG_DOCUMENT_ERR: number;
  static readonly INVALID_CHARACTER_ERR: number;
  static readonly NO_DATA_ALLOWED_ERR: number;
  static readonly NO_MODIFICATION_ALLOWED_ERR: number;
  static readonly NOT_FOUND_ERR: number;
  static readonly NOT_SUPPORTED_ERR: number;
  static readonly INUSE_ATTRIBUTE_ERR: number;
  static readonly INVALID_STATE_ERR: number;
  static readonly SYNTAX_ERR: number;
  static readonly INVALID_MODIFICATION_ERR: number;
  static readonly NAMESPACE_ERR: number;
  static readonly INVALID_ACCESS_ERR: number;
  static readonly VALIDATION_ERR: number;
  static readonly TYPE_MISMATCH_ERR: number;
  static readonly SECURITY_ERR: number;
  static readonly NETWORK_ERR: number;
  static readonly ABORT_ERR: number;
  static readonly URL_MISMATCH_ERR: number;
  static readonly QUOTA_EXCEEDED_ERR: number;
  static readonly TIMEOUT_ERR: number;
  static readonly INVALID_NODE_TYPE_ERR: number;
  static readonly DATA_CLONE_ERR: number;
}

You could argue that the types have just been wrong up to this point, and that's especially fair with the change from any to string. Is there ever a case where a DOMException could be thrown without a stack property though, thus marking this as now optional instead of required?

Metadata

Metadata

Labels

typesRelated to @cloudflare/workers-types

Type

No type

Projects

Status

Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions