-
Notifications
You must be signed in to change notification settings - Fork 12.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add type operators that can answer "given function type T, what is the type of its return value when arguments P are passed in?" #40179
Comments
I have to rescind that statement as I found an example that doesn't currently work. type Test = {
x: boolean;
[K: string]: number | boolean;
}
let boolean: ValueForKey<Test, 'x'> = true;
// @ts-expect-error
boolean = 42
// Type 'boolean' is not assignable to type 'never'.(2322)
let shouldBeNumberOrBoolean: ValueForKey<Test, 'y'> = true
^^^^^^^^^^^^^^^^^^^^^^^
// Type 'number' is not assignable to type 'never'.(2322)
shouldBeNumberOrBoolean = 42;
^^^^^^^^^^^^^^^^^^^^^^^
let numberOrBoolean: Test['y'] = true;
numberOrBoolean = 42; The implication being that |
That was as close as I got to a solution using the current state. It works in most cases. type Calleable = (...args: any[]) => any;
type CallOverloads<T> = T extends
{
(...args: infer A1): infer R1,
(...args: infer A2): infer R2,
(...args: infer A3): infer R3,
(...args: infer A4): infer R4,
}
? [A1, (...args: A1) => R1] | [A2, (...args: A2) => R2] | [A3, (...args: A3) => R3] | [A4, (...args: A4) => R4]
: T extends
{
(...args: infer A1): infer R1,
(...args: infer A2): infer R2,
(...args: infer A3): infer R3,
}
? [A1, (...args: A1) => R1] | [A2, (...args: A2) => R2] | [A3, (...args: A3) => R3]
: T extends
{
(...args: infer A1): infer R1,
(...args: infer A2): infer R2,
}
? [A1, (...args: A1) => R1] | [A2, (...args: A1) => R2]
: T extends (...args: infer A1) => infer R1
? [A1, (...args: A1) => R1]
: never;
type Overload<T extends Calleable, TArgs> = Extract<CallOverloads<T>, [TArgs, any]>[1];
type ParameterOverloads<T extends Calleable> = CallOverloads<T>[0];
type TFN1 =
{
(): void,
(value: string): number,
(value: number): boolean,
(value: number, options: object): object,
}
type T01 = Overload<TFN1, []> // (): void
type T02 = Overload<TFN1, [string]> // (value: string): number
type T03 = Overload<TFN1, [number]> // (value: number): boolean
type T04 = Overload<TFN1, [number, object]> // (value: number, options: object): object
type T05 = ParameterOverloads<TFN1> // [] | [value: string] | [value: number] | [value: number, options: object]
type TFN2 =
{
(): void,
(value: boolean): string,
(value: number, options?: object): object,
}
type T11 = Overload<TFN2, []> // (): void
type T13 = Overload<TFN2, [number, object | undefined]> // never
// Works, but hard to reproduce when the types is provided based on user input
type T12 = Overload<TFN2, [number, object?]> // (value: number, options?: object): object
declare function call<TArgs extends ParameterOverloads<TFN2>>(...args: TArgs): ReturnType<Overload<TFN2, TArgs>>;
call(); // void
call(true) // string
call(1, {}) // never
call(1, undefined) // never |
Here's another use-case: function usesOverloadedFunction<T>(arg: T) // : inferred return type is whatever the last overload of
// `overloadedFunction` is when applied to `...args: [T]`.
{
return overloadedFunction(arg);
} Expected: function usesOverloadedFunction<T>(arg: T) // : infers `(typeof overloadedFunction)(T)`
{
return overloadedFunction(arg);
} |
Would love to see a solution to this. this is one of the reasons why i don't like to work with the standard NodeJS EventEmitter. these often easily have 10+ overloads for all the eventnames which were all manually typed. That's great when you are trying to harcode an event, but any kind of elegant type inference impossible at the moment. Specific example are event.once(emitter, name[, options]) // => return a Promise
event.on(emitter, name[, options]) // => return an AsyncIterable Both of them are great when you are using a lot of server.listen(8124)
await once(server, 'listening') server.close()
await once(server, 'close') A solution to this migth also be a stepping stone for problems related to generic functions, smarter |
Cross-linking #17961 |
From the user's point of view, this function is very useful, I hope the official can be more from the user's point of view |
Cross-linking to #52035 (comment) and specifically the term "call types" |
Search Terms
overload infer function operator arguments parameters return
Suggestion
Use Cases
This is a proposal that would help with overloaded function issues like #26591 and would help my type testing library be able to support checks like
expectTypeOf(fn).toBeCallableWith(args)
.Note that though
ValueForKey
could be implemented as:The following will only work for non-overloaded functions:
since both the inference from
Parameters<T>
and the type definition will only consider one of the overloads.What I'm asking is not for inference to consider multiple overloads, which would be nice but might complexify the implementation of type inference, but direct support for
paramsof
andT(A1, A2, ...A)
type syntax which behaves correctly with overloaded functions.Examples
Also
Given the above, it would also make sense to have something like
where
new T
takes the constructor type ofT
and turns it into a function typeChecklist
My suggestion meets these guidelines:
The text was updated successfully, but these errors were encountered: