Created
January 31, 2023 17:39
-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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