Files
scriptc/tests/corpus/6403-error-code-write.ts
T
Malte UblandClaude Opus 5.5 7d1d440b7b Adapt method overrides across ABI differences, write Error codes, and close wrapper-reachable gaps
Overrides whose parameters widen and whose results convert into the
inherited ones keep the inherited slot, which converts around the
override's own body; Error values take code writes, `in` tests and
Object.assign; and a few gaps behind generic wrappers close:

- Method overrides: each inherited argument converts into the
  override's parameter (equal, wider, or a checked extraction from an
  `any` slot) and the override's result converts into the inherited one
  (narrower, a convertible union, a widening Promise payload, anything
  convertible over an `any` base). The slot %C.m keeps the inherited
  ABI and forwards to %C.m$own; TypeScript callers typed with the
  subclass call $own directly when nothing overrides below. Async
  overrides of sync and async bases, and JavaScript overrides of
  builtin or TypeScript bases (zod's ZodError.toString) are included;
  a toString slot converts its result with ToString.
- `err.code = value` on Error values in TypeScript: strings set the
  runtime code slot (and the error's checked-dynamic node, if any); other
  values clear it and keep the dynamic own property.
- `'key' in err` with any receiver: declared members answer true;
  otherwise a class test over the declaring subclasses, a runtime lookup
  of the error's own properties, or an Object.prototype member.
- `Object.assign(err, ...sources)` onto an Error target copies onto the
  error, re-reads name, message, stack, cause and code into the typed
  slots, and returns the error.
- `Error & { ... }` intersections whose extra members have checkable
  types are Error values; those members read and write the error's own
  properties, each read checked at its declared type.
- Catch bindings typed `any` read as checked-dynamic values (member
  reads and writes, assertions); a multi-statement type guard with an
  `unknown` parameter takes the caught value as an ordinary argument;
  Error members of a binding narrowed to an Error-extending interface
  read through the same value.
- Lambda parameters typed `any` by their default are checked-dynamic
  slots; forwarded-only rest tuples with optional positions forward
  their slots; `value as never` from an untyped value into a typed
  parameter takes the checked extraction to the parameter's type.
- Lexical `this` stops at generic instance frames, so an instance no
  longer sees the receiver of the caller that triggered it. The fence
  for `this` captured through a plain function now points at the
  `this` keyword.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 22:28:43 -07:00

40 lines
1.3 KiB
TypeScript

// Own properties written on Error values typed with an intersection.
const coded = new Error("c") as Error & { code?: string };
coded.code = "E1";
console.log(coded.code, "code" in coded);
function has(err: Error): boolean {
return "code" in err;
}
console.log(has(coded), has(new Error("plain")));
try {
throw coded;
} catch (e) {
console.log((e as Error & { code?: string }).code);
}
// System-error members first, then the code: reads stay consistent.
type SysError = Error & { code?: string; errno?: number; syscall?: string; path?: string };
function sysError(message: string, code: string | undefined): SysError {
const err = new Error(message) as SysError;
err.errno = -2;
err.syscall = "open";
err.path = "/missing";
err.code = code;
return err;
}
const enoent = sysError("no such file", "ENOENT");
console.log(enoent.code, enoent.errno, enoent.syscall, enoent.path, "code" in enoent);
console.log(enoent.message, enoent instanceof Error);
const uncoded = sysError("no code", undefined);
console.log(uncoded.code, uncoded.syscall);
// A code that is only known at runtime.
function withCode(err: Error & { code?: string }, code: unknown): void {
err.code = code as string;
}
const late = new Error("late") as Error & { code?: string };
withCode(late, "ELATE");
console.log(late.code);
late.code = "EAGAIN";
console.log(late.code, late.message);