-
Notifications
You must be signed in to change notification settings - Fork 12.8k
ReturnType not being evaluated when used in function definition generics alias #41391
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
Comments
Why do you need so many extra type params? This is more than enough, function extract3<M extends Record<string, () => any>> (_map : M) : {
[k in keyof M] : ReturnType<M[k]>
} {
return {} as any;
} |
If we change your example a little, you'll see that your initial Notice Notice
|
@AnyhowStep In a strict environment with enforced types, and Your example will only return Looking at your screenshots, both are strings, what is not correct. { [x: string]: string; } // from your screenshot, not the actual real type. The expected behavior is: type Parameter = {
foo: () => number;
bar: () => string;
};
type ActualDesiredReturn = {
foo: number;
bar: string;
}; And it is useful to have a reference to the return type for the same reason as any other type in the generics, reuse the constructed type elsewhere in the function body or pass it to another place. |
That is just done to focus on the types, instead of having to create the implementation of the function. I don't see the reason for the complexity. What you want can trivially be written as:
|
I'm aware of alternatives, but none of the already suggested ones solves the problem or prove that it is not an issue. Lets see a more complex example: const randomFn = <T>(): T | null => null;
export default function makeSelector<T>(context: T) {
return <
Keys extends keyof Selectors,
Selectors extends Record<string, (context: T) => unknown>,
TRet extends { [K in Keys]: ReturnType<Selectors[K]> }
>(
selectors: Selectors,
): TRet => { // HERE if I replace the named type TRet with { [K in Keys]: ReturnType<Selectors[K]> } it magically works 🤷🏻♂️
// actual implementation
// data fetching and parsing and then pass it to another fn
const ret = randomFn<TRet>();
return ret;
};
}
const selector = makeSelector({ unrelated: true, nothingSpecial: false });
const { foo, bar } = selector({ foo: () => 0, bar: () => '' }); As explained in the issue description, if I delete the another playground with a working example. |
Your code is failing because TypeScript can't infer the type of That's why it works if you remove the type parameter and actually provide the return type. This is working as intended. |
Not fully, that's why you get the
Look at the code of |
But then, why does it work if I remove the named Why does the named one fail while the anonymous work? anonymous and named have the same signature. Both of the information will be lazy-inferred only at the function call, not at the creation. |
Because then there is no type to infer. The compiler knows the exact type. The compiler doesn't need to figure out "what is the type, and does it extend this other type".
You're assuming |
I might be underestimating it, |
The first two type arguments can easily be inferred from your input argument (both
It's unrelated to |
I see your point now. But how is it different, if we move the definition from the generic argument to the return type? |
Because then there is no type inference necessary anymore. The compiler doesn't need to figure out what The difference is:
|
Nice, thanks for the patience and the extremely detailed explanation. What we do now? Or, Especially when we are working with selectors like @fluentui/react-context-selector that is always returning We want to get derived data from the Context, like sums, Dates from a string, etc., not only properties existent in the Context. |
It sounds to me that you don't actually want a generic type, but you just want to reference your rather large type using a local type alias. For this there's #40780 / #30979. But there hasn't been any progress, and likely there won't be in any foreseeable future. For now you should simply just use |
I know. I'm saying that you've structured your types in a way that doesn't make sense. |
Having type parameters that cannot be inferred/constrained from the function parameters is also actually an anti-pattern. Assume your desired type parameter function extract<
M extends Record<string, () => any>,
Ret extends {
foo : number,
bar : string,
}
> (_map : M) : Ret {
return {} as any;
}
const obj = {
foo: () => 1,
bar: () => 'bar',
} as const
const { foo, bar } = extract<
typeof obj,
{
foo : 32,
bar : "lol",
}
>(obj); Notice that even though You pointed out that one "should not typecast something to |
Doesn't seem like there's a bug here. Thanks for the good discussion everyone. |
This issue has been marked as 'Question' and has seen no recent activity. It has been automatically closed for house-keeping purposes. If you're still waiting on a response, questions are usually better suited to stackoverflow. |
Scenario:
Given an indexed object of functions that return different value types, generate a new object with the same indexes and the return value of the functions.
Failing example:
TypeScript is not re-evaluating the Types when calling the function with predictable values:

It can predict the functions and their return types, but it is not evaluating
ReturnType<...>
Workaround example:
The proper types are correctly inferred when using an anonymous type directly in the return type of the function:

This workaround is not desired given the need for using the Type definition inside the body of the function.
TypeScript Version: 4.0.3
Search Terms:
Code
Expected behavior:
Variable
foo
should be of typenumber
Variable
bar
should be of typestring
Actual behavior:
All variables are of type
any
Playground Link:
Playground Link or a CodeSandbox Link - Both with the same behavior.
The text was updated successfully, but these errors were encountered: