Repository navigation
Fix label names when internalSignature has optional params - #1615
Conversation
|
Reviewed PR #1615 — approved. The one-line fix correctly wraps Task list (5/5 completed)
|
There was a problem hiding this comment.
Reviewed — no issues found.
The fix is correct: Required<Parameters<internalSignature>> ensures the labels tuple passed to applyElementLabels is always fully required, which is the right semantics since parameter optionality in the external signature is determined by distill.In<paramsT> (the first arg), not the implementation's parameter declarations. The recursive [unknown, ...infer labelsTail] destructuring in applyElementLabels can break when the labels tuple has optional elements — this cleanly prevents that.
There was a problem hiding this comment.
Reviewed — no issues found.
The fix is correct: Required<Parameters<internalSignature>> ensures the labels tuple passed to applyElementLabels is always fully required, which is the right semantics since parameter optionality in the external signature is determined by distill.In<paramsT> (the first arg), not the implementation's parameter declarations. The recursive [unknown, ...infer labelsTail] destructuring in applyElementLabels can break when the labels tuple has optional elements — this cleanly prevents that.
internalSignature has optional paramsinternalSignature has optional params
0e6e185 to
c411b27
Compare
…parameter
The fix had no test because tuple labels look untestable - they are cosmetic
and carry no runtime representation. They are still part of the inferred type,
so `attest(...).type.toString` pins them exactly.
The case is narrower than the issue suggests. A contextually inferred parameter
already labels correctly, both alone and alongside others:
type.fn("number?")(n => n) // (n?: number | undefined)
type.fn("string", "number?")((a, b) => {}) // (a: string, b?: number | undefined)
It is annotating the optional parameter that breaks it, since only then does
`Parameters<internalSignature>` carry an optional element - which
`applyElementLabels` cannot match against `labels extends [unknown, ...infer
labelsTail]`, so it drops every label rather than just that one:
type.fn("number?")((n?: number) => n) // (args_0?: number | undefined)
The test uses that last form, with the parameter optional on both sides. The
issue's own repro instead pairs a required `"number"` with an optional `b?`,
which reproduces the bug but asks for something incoherent: the schema requires
the argument, so an implementation marking it optional describes a call that
cannot happen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AvpxQ2tfRbpXYxMH3NbXou
c411b27 to
d84499d
Compare
ssalbdivad
left a comment
There was a problem hiding this comment.
Verified the fix and added the regression test. Labels are cosmetic but still part of the inferred type, so attest(...).type.toString pins them.
|
Thanks @aswinsvijay — merging. I added the regression test and rebased onto One note for the future: the declared type's optionality should match the implementation's. |
Bumps all publishable packages and documents the changes merged since 2.2.4: - preserve escaped backslashes in regex literals (#1634, @spokodev) - fix recursive discriminated unions referenced from a Record (#1641, @xianjianlf2) - prevent a crash validating unions of object arrays (#1638, @xianjianlf2) - merge index-derived props with a declared key of the same name (#1659) - preserve parameter labels when a type.fn implementation annotates an optional param (#1615, @aswinsvijay) #1634 changes behavior for regex literals that contain `\\`: `"/\\\\d/"` previously compiled to the digit class `\d` and now matches a literal backslash followed by "d", matching the equivalent RegExp instance. The changelog calls this out with a migration note. Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Fixes #1614
mainbranchpnpm prCheckslocallyUnsure how this can be tested since tuple labels are not part of the type system.