Add Overloads, OverloadParameters, OverloadReturnType#1399
Conversation
Create a union of all overload signatures of a given function type. TypeScript's built-in `Parameters` and `ReturnType` only work with the last overload — this type extracts all of them. The implementation correctly distinguishes between implicit `this` and explicit `this: unknown` overloads via a two-pass approach. Resolves sindresorhus#868 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Neither tsd's `expectType` nor type-fest's `IsEqual` can distinguish `() => void` from `(this: unknown) => void`. Add a test-local `IsEqualStrict` helper that extracts the `this` parameter via a sentinel type, and use it in tests that verify this distinction. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move `SetReturnType`, `SetParameterType`, and `FunctionOverloads` from Utilities into a new Function section, matching their `@category` tags. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Naming suggestion
|
- CollectOverloads now builds a tuple instead of a union, preserving declaration order. Duplicates from the termination lag are removed by dropping the first element at return. - Replace recursive tuple iterations (MatchesAnyOverload, ExtractExplicitUnknownThisOverloads, IsImplicitThisOverload, OverloadsTupleToUnion) with a single mapped type (OverloadsToFunctions). - DistinguishUnknownThisOverloads now returns a tuple of function types; FunctionOverloads indexes with [number] to produce the union. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the two-pass approach with a simpler design: - `HasExplicitThis`: detects implicit vs explicit `this: unknown` by intersecting a `(this: Nothing, ...)` sentinel from the right — implicit `this` absorbs it (TS deduplication), explicit `this: unknown` doesn't. - `LastOverload`: extracts the last overload as a standalone function with correct `this` handling. - `CollectOverloads`: uses `LastOverload` directly, no intermediate `[This, Params, Return]` tuples or secondary function type. Also: - Export `OverloadsToTuple` (tuple counterpart to `FunctionOverloads`) - Fix `Parameters` extraction for `readonly` arrays - Add detailed internal documentation of TypeScript's overload enumeration behavior (deduplication rules, implicit `this` semantics) - Update tests to target `OverloadsToTuple` with tuple expectations Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Move `HasExplicitThis`, `LastOverload`, `CollectOverloads`, and internal `Parameters` to `source/internal/function.d.ts` - Deduplicate JSDoc: TS overload enumeration behavior documented once in the internal file; `HasExplicitThis` and `CollectOverloads` refer to it instead of repeating - Fix stale "in this file" reference in `FunctionOverloads` JSDoc - Add "Known limitations are the same as FunctionOverloads" to `OverloadsToTuple` JSDoc Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
FunctionOverloads typeOverloadsToUnion and OverloadsToTuple types
OverloadsToUnion and OverloadsToTuple typesOverloadsToUnion, OverloadsToTuple, OverloadParameters, OverloadReturnType
… drop `OverloadsToUnion` - Rename `OverloadsToTuple` → `Overloads` (tuple is the natural representation) - Remove `OverloadsToUnion` (redundant — `Overloads<F>[number]` is equivalent) - Add `OverloadParameters` and `OverloadReturnType` as overload-aware counterparts to the built-in `Parameters` and `ReturnType` Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
OverloadsToUnion, OverloadsToTuple, OverloadParameters, OverloadReturnTypeOverloads, OverloadParameters, OverloadReturnType
|
declare function genericLast(input: string): 1;
declare function genericLast<Type>(input: Type): Type;
type Result = Overloads<typeof genericLast>;
// actual: [(this: unknown, input: unknown) => unknown]
// expected: [(input: string) => 1, (input: unknown) => unknown]Same thing happens when the generic overload is in the middle of the list: declare function genericMiddle(input: string): 1;
declare function genericMiddle<Type>(input: Type): Type;
declare function genericMiddle(input: number): 2;
type Result = Overloads<typeof genericMiddle>;
// actual: [(this: unknown, input: unknown) => unknown, (input: number) => 2]
// expected: [(input: string) => 1, (input: unknown) => unknown, (input: number) => 2]I reran this locally with |
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ncement Rewrite `CollectOverloads` internals to handle generic overloads that previously terminated the iteration, losing all preceding overloads. - Replace `LastOverload` with `NthLastOverload<F, N>`: uses a multi-signature pattern (MaxOverloadPatterns=4 slots) to extract the Nth-from-last overload, with `HasExplicitThis` generalized to check any position. - Replace convergence-based loop with effect-observation loop: each iteration checks whether intersecting ExtractedN onto AllOverloads changes the view at position N. If it does (effect), intersect and continue. If not (no effect, e.g. aliasing generic overloads), output without intersecting and advance N. - Add `() => Unique` sentinel boundary: `CollectOverloads` prepends the sentinel before the original overloads so `NthLastOverload` can detect when all original overloads have been exhausted. - Add `FuncToTupleForCompare` for this-aware function comparison, reused by test `IsEqualStrict`. - Update Known limitations: generic type parameters are replaced by their upper bound; functions with <4 generic overloads are fully extracted. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use a default type parameter (`AllOverloads = (() => Unique) & F`) to prepend the sentinel, eliminating the separate wrapper type. Update doc to reflect the unified signature. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Also, looks like the explicit type T = Overloads<{<T>(x: T): T}>;
//=> [(this: unknown, x: unknown) => unknown] |
Added to known limitations |
|
Right. Same underlying limitation, just bounded by a larger constant now. Quick context first: those repros were against the version of the code at the time the review landed, which used a 1-slot Why it remains structural: the technique matches the function against a target shape whose slots are non-generic call signatures introduced by So the limitation is bounded by a constant rather than absent. Closing it (rather than just pushing the constant up further) would need TypeScript itself to grow a way to capture a single, possibly-generic call signature through conditional inference, such as |
Fixes #868
Fixes #585
Closes #1264
Summary
Adds three types for working with overloaded function signatures:
Overloads<F>— extracts all overloads as a tuple, preserving declaration orderOverloadParameters<F>— overload-awareParameters(returns a union of all parameter tuples)OverloadReturnType<F>— overload-awareReturnType(returns a union of all return types)TypeScript's built-in
ParametersandReturnTypeonly work with the last overload; these types extract all of them. To get a union of overloads instead of a tuple, useOverloads<F>[number].Improvements over #1264
FunctionOverloadstoOverloadsand changed the return type from a union to a tuple — overloads are ordered, so a tuple is the natural representation. Analogous toParameters/ReturnTypein naming styleOverloadParametersandOverloadReturnTypeas direct replacements forParametersandReturnTypeon overloaded functionsHasExplicitThiscorrectly distinguishes implicitthisfrom explicitthis: unknownby exploiting TypeScript's overload deduplication rulesFunctionOverloadstype #1264 where encountering an overload with the same parameter list as a previous one would terminate iteration early, missing remaining overloadsCollectOverloadsuses an effect-observation loop with N-advancement to extract overloads preceding generic overloads, which the previous convergence-based approach could not reachthissemantics)Known limitations
<T>becomesunknown,<T extends string>becomesstring)this(no annotation) is indistinguishable from explicitthis: unknown, so the output always includesthis: unknownthis, all three of(This, Parameters, Return)must match to be deduplicated. However, when one or both have implicitthis(no annotation), only(Parameters, Return)is compared — meaning an implicit-thisoverload and an explicit-thisoverload with the same params/return are treated as duplicates, and whichever appears first suppresses the other