Repository navigation
Ability to get generic type from typeof and infer #33185
Description
TypeScript version: 3.6.2
Search Terms
typeof generic function return generic type
infer arguments from generic function
Suggestion
I ran into the issue described at #32170, where the answer was
I expect what you're trying to do is refer to a specific call instantiation of a generic function, which isn't currently possible.
The feature request is to make this possible.
Examples
In the following code, dragHelperReact is a helper function for implementing draggable functionality, which also abstracts over Mouse vs Touch events (since Pointer Events are not supported in older browsers). It takes as input three callbacks; since those callbacks have fairly lengthy signatures, I want to infer their argument types. The difficulty is that React.SyntheticEvent is a generic type, which allows narrowing of e.currentTarget.
import * as React from "react";
function dragHelper<T>(
move: (e: MouseEvent | TouchEvent, coords: {x: number; y: number; dx: number; dy: number}) => void,
down: (
e: React.MouseEvent<T> | React.TouchEvent<T>,
coords: {x: number; y: number},
upHandler: (e: MouseEvent | TouchEvent) => void,
moveHandler: (e: MouseEvent | TouchEvent) => void
) => void,
up: (e: MouseEvent | TouchEvent) => void
) {
return {
onMouseDown: () => {
/* implementation */
}
};
}
type Args<T extends (...args: any) => any> = T extends (...args: infer A) => ReturnType<T> ? A : void;
type Move = typeof dragHelperReact extends (down: infer M, move: infer D, up: infer U) => void ? M : never;
type Down = typeof dragHelperReact extends (move: infer M, down: infer D, up: infer U) => void ? D : never;
type Up = typeof dragHelperReact extends (move: infer M, down: infer D, up: infer U) => void ? U : never;
// these work
type MoveArgs = Args<Move>;
type UpArgs = Args<Up>;
// I want to do something like this; it does not work.
type DownArgs<T> = Args<Down<T>>;
// This is a workaround I tried; it does not work either.
type DownArgs<T> = Down extends (e: any, ...args: infer U) => void ? [React.MouseEvent<T> | React.TouchEvent<T>, ...U] : never;
// This workaround works.
type DownArgs<T> = Down extends (e: any, ...args: infer U) => void ? [React.MouseEvent<T> | React.TouchEvent<T>, U[0], U[1], U[2]] : never;
class Example extends React.Component {
down(...[e, coords, upHandler, downHandler]: DownArgs<HTMLDivElement>) {
// e.currentTarget is narrowed to HTMLDivElement
}
move(...[e, coords]: MoveArgs) {}
up(...[e]: UpArgs) {}
render() {
return (
<div {...dragHelper(this.move, this.down, this.up)}/>
);
}
}Checklist
My suggestion meets these guidelines:
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
- This feature would agree with the rest of TypeScript's Design Goals.
Activity
jcalz commentedon Sep 2, 2019
You can kind of get this with some dummy code that appears at runtime, thanks to the recent support for higher order function inference. Note that I'm going to use a non-react-specific example here.
Say you have a generic function like this:
and you want some way to represent the argument callbacks' arguments without having the
Ttype parameter resolved tounknown. Well, you can declare this function and class, neither of which will actually be used at runtime:And suddenly the types you want are available:
And you can fill in the
Twith something other thanunknown:The type of
paramsuses higher order function inference to take in a generic functionfand spit out another generic function whose return type is the same asf's input parameter tuple. (Note that you don't want to try to useparams()at runtime, even though it will be emitted, since it's justdeclared into existence, and no reasonable implementation ofparams()could exist.) And the type ofSomeGeneric<T>lets you transfer the type parameter from the generic function (which has a concrete type) to a generic type. It's weird, I know.suchipi commentedon Jan 23, 2020
Ran into this, too:
I don't know how to best handle specific instantiations of generic functions on their own (eg
typeof something<number>, but maybeReturnTypeandParameterscould accept optional additional type parameters for this?Xunnamius commentedon Sep 5, 2020
Also ran into this trying to get the parameters of a default import function.
thany commentedon Dec 2, 2020
Just ran into this problem the other day.
How could you possibly have
typeofnot return a generic type for a generic function? This seems like a pretty severe bug to me, as it introduces type inconsistency, technically speaking.ahejlsberg commentedon Jan 27, 2022
With #47607 it is possible to refer to specific instantiations of generic functions.
mikeaustin commentedon Jun 9, 2023
Somewhat related, and I did extensive Googling, I just can't find how to apply a generic parameter default type when using typeof. Am I missing something basic?
Without passing the generic parameter type 'div', you get the generic function, which results in Omit<any, 'ref'> an no props being checked at all.