Skip to content

Instantly share code, notes, and snippets.

@IgnusG
Created January 31, 2023 17:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save IgnusG/b43b559a47a2e3e504dd8808f241ce73 to your computer and use it in GitHub Desktop.
Save IgnusG/b43b559a47a2e3e504dd8808f241ce73 to your computer and use it in GitHub Desktop.
Get parameters and their associated return types from overloaded signatures (does not work for generics)
/* eslint-disable @typescript-eslint/no-explicit-any */
type OverloadedProps<Signature> = Pick<Signature, keyof Signature>;
type OverloadedSignatureToUnionStep<
Signature,
PartialOverloadedSignature = unknown,
> = CurrentPiano extends FivePianos
? PartialOverloadedSignature
: Signature extends (
...args: infer SignatureParameters
) => infer SignatureReturnType
? PartialOverloadedSignature extends Signature
? // stop recursion once PartialOverloadedSignature accumulates the entire Signature
never
:
| OverloadedSignatureToUnionStep<
PartialOverloadedSignature & Signature,
PartialOverloadedSignature &
((...args: SignatureParameters) => SignatureReturnType) &
OverloadedProps<Signature>,
>
// add the selected signature into the union
| ((...args: SignatureParameters) => SignatureReturnType)
: never;
/**
* Given an overloaded function declaration, returns all constituent signatures as a union
*
* @see https://github.com/microsoft/TypeScript/issues/32164#issuecomment-1146737709
*/
type OverloadedSignatureToUnion<Signature extends (...args: any[]) => any> =
// remove the () => never signature we added in the beginning
Exclude<
// create an intersection of signatures
OverloadedSignatureToUnionStep<(() => never) & Signature>,
Signature extends () => never ? never : () => never
>;
/**
* Places T in a contra-variant position
* - `extends any` distributes unions from T
* */
type Contra<T> = T extends any ? (arg: T) => void : never;
/**
* Infers a previously wrapped T from a contra-variant position
* - infer returns intersections in this case
* - `[T] extends` prevents union distribution - matches on whole type
* */
type InferContra<T> = [T] extends [(arg: infer I) => void] ? I : never;
/**
* Given a union type T, returns one of its elements
* - double `Contra` call creates a function in a contra-variant position with `T` in a contra-variant position as well
* - first `InferContra` creates an intersection of these functions (intersections of functions do not collapse to never)
* - second `InferContra` then picks only one of the functions from the intersection and returns its contra-variant T (thus returning only 1 element from T)
*
* @see https://www.hacklewayne.com/typescript-convert-union-to-tuple-array-yes-but-how
*/
type PickOne<T> = InferContra<InferContra<Contra<Contra<T>>>>;
type ReturnTypeForParameters<
Signature extends (...args: any[]) => any,
SignatureParameters,
> = PickOne<Signature> extends infer SingleSignatureOverload
? SingleSignatureOverload extends (...args: any[]) => any
? Exclude<Signature, SingleSignatureOverload> extends never
? // the currently picked SingleSignatureOverload is the last one
SignatureParameters extends Parameters<SingleSignatureOverload>
? // the SignatureParameters match the ones from the currently picked SingleSignatureOverload - we found a matching return type
ReturnType<Signature>
: // the picked parameters did not match the last overload neither
never
: SignatureParameters extends Parameters<SingleSignatureOverload>
? // the SignatureParameters match the ones from currently picked SingleSignatureOverload - we found a matching return type
ReturnType<SingleSignatureOverload>
: // the currently picked SingleSignatureOverload did not match any parameters, exclude it from the signature and continue searching
ReturnTypeForParameters<
Exclude<Signature, SingleSignatureOverload>,
SignatureParameters
>
: never
: never;
/** Given an overloaded signature, returns a union of Parameters from all overloads */
export type OverloadedParameters<Signature extends (...args: any[]) => any> =
Parameters<OverloadedSignatureToUnion<Signature>>;
/** Given an overloaded signature and one of its Parameter overloads, returns the ReturnType of that function overload */
export type OverloadedReturnType<
Signature extends (...args: any[]) => any,
SignatureParameters extends OverloadedParameters<Signature>,
> = ReturnTypeForParameters<
OverloadedSignatureToUnion<Signature>,
SignatureParameters
>;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment