Files
scriptc/tests/diagnostics/record-width.ts
T
Chris Tate a0d2ce24f5 Record width composes with hybrid shapes, and per-field lifts cover unknown, upcasts, and clean function adapters
- recordWidthPlan narrows index-signature sources into declared-only targets (declared fields copy off their struct slots, the overflow drops with the width); absent-field completion declines there — the overflow could hold the key at runtime, and a completed undefined would drop the value Node keeps
- the overflow capture initializes same-named declared target fields directly off the source, admitting REQUIRED declared members; literal writes to names the target does not declare go overflowOnly, and a dispatch-writes gate keeps the validator's collision rule for the rest
- widthLiftPlan gains dynIn (a typed field into an 'unknown' slot — the static→DOM deep copy), upcast (derived into base, the prefix-layout reinterpret), and funcAdapt (funcCoerceAdapter's CLEAN subset — the stranded trap-only dispositions stay top-level, never inside a width plan)
- SC2002 now names the first blocking rule per record pair (describeRecordWidthBlocker mirrors the planners' gates) and its message/hint/registry entry speak the width-copy truth — the manifest flips the one entry name
- corpus 2556/2557 pin both hybrid directions and the new lifts byte-identical vs Node on both backends; diagnostics fixture record-width pins the residue's pointed messages
2026-07-23 20:36:50 -05:00

42 lines
1.5 KiB
TypeScript

// SC2002: the RESIDUE of the width-copy family — record shapes that
// neither match exactly nor width-coerce. Each fence names the first
// blocking rule. (The width copies themselves — field-subset narrowing,
// hybrid captures, per-field lifts — are differential corpus.)
// An index-signature source cannot complete an absent optional field:
// the overflow could hold the key at runtime, and a completed undefined
// would drop that value.
type Loose = { [k: string]: number };
type PickedA = { a?: number };
const loose: Loose = { a: 1 };
const picked: PickedA = loose;
console.log(picked);
// A method field whose parameter shapes relate only BIVARIANTLY: neither
// parameter type converts into the other mechanically, so the field does
// not lift.
type SrcM = { m(x: { a: number }): void; extra: number };
type DstM = { m(x: { a: number; b: number }): void };
const srcM: SrcM = {
m: (x) => {
console.log(x.a);
},
extra: 1,
};
const dstM: DstM = srcM;
console.log(dstM);
// A source field that cannot enter the target's value slot (a Map has no
// DOM conversion into an 'unknown' signature slot).
type WithMap = { m: Map<string, number>; n: number };
const withMap: WithMap = { m: new Map(), n: 1 };
const bag: Record<string, unknown> = withMap;
console.log(bag);
// A declared target field that cannot take a runtime key collision from
// the signature slot (its type is narrower than the slot's).
type Odd = { a?: boolean; [k: string]: boolean | string | undefined };
const plain = { b: "hi" };
const odd: Odd = plain;
console.log(odd);