GeezerVison is showing github.com — Can't infer return type of generic function · Issue #55435 · microsoft/TypeScript
Can't infer return type of generic function #55435Description🔎 Search Terms
infer generic function return type
🕗 Version & Regression Information
- Before version
3.5.1 the syntax wasn't supported
- Before and during version
4.8.4 there was an additional bug for which the type of a (In the example) was [ true, true ]
5.2.0-beta doesn't fix the issue
⏯ Playground Link
https://www.typescriptlang.org/play?ts=5.1.6#code/FAehAIHUHsCcGsDO5C8G4WR3gG9jl+ALgJ4AOApuAMIAWpAxvADwAqANOAArikAe+pAdgBNkACm4AucAEN+hAJTgAvAD5wAI2jQANqRmrFHLrwHDwYyUwUqCsAK7kA-DfvhJAMylbEpANzAceGBQcEjgAO4AlvhU4LTQALbEOtxGxLCkiIgR0PyIAbhEZOAAkogActD4AIzM+pQ09MxsDACqyubgLVaqLUZ8QshV4E4eXuSS+Hakyn54BCTkUkrgANol5ZU1AEzKbKUV1QxVqgC6s4EQuAB6Dv5zQTAIyOn4trD8EfwA5gQ04J-EWz4fLzIqlJhTWrLah0RiscC1DqWJSqJgzEGFchqZZrcGQyb2XbrCH2Bijbync64ILXW4AX38QQAItAMvwAOT4cIhMwAIVsP2IUlg+DkWBBzNZiA5XPg-GgYX++GQAKBGIWlE8WgYADE+iZkDrbPxaPhsvw2ABBWBfZA8fqmdJSQQ5LSEcDGuUK-grE51PX2g1mAB0oeFtsk1tt3X+-DcpFgnWGSck-FIADcE1TQeRaNCtQxERJwMjrPC1kM-RLLuAbncLuADlxM-xwDlpG20wBaIWwKTxUh8RNZRJaCJuCKkQTgTOwLI5dVFChamoB4wDcBGk1mnJsNHLNcO0TF0uqT7xxO9Jy9VMZrOL8jTgzLrRaGpFiwx+HHat4Ou-lk2U5bkEHCKIYjiUceFSdJMnNPI5kxLh81fd80SRGM1nhLZwBONgf3uGt-0I4InnCGhW2iBNFnSGVwC+AQEwiWgELwJDShqfdnwLDoqhjAlSDYLDcPRRCNTcHF1hqASiQ4o5KV-WlgDpIA
💻 Code
// Works 👍
{
type Check<T, P extends (x: any) => boolean> = P extends (x: T) => true ? true : false;
// Works with complex expressions
type IsNot1<T> = Check<T, <U>(x: U) => U extends 1 ? false : true>;
type a = [ IsNot1<2>, IsNot1<1> ];
// ^? type a = [ true, false ]
// Works returning the input
type IsTrue<T> = Check<T, <T>(x: T) => T>;
type b = [ IsTrue<true>, IsTrue<false> ];
// ^? type b = [ true, false ]
}
// Doesn't work (Bug part)
{
// Doesn't know its input
type Call<F extends Function, Args extends readonly unknown[]> = F extends (...args: Args) => infer U ? U : never;
type c = Call<<T>(x: T) => T, [ 1 ]>
// ^? type c = unknown
// Not even on a one-parameter simplified version
type Call1<F extends Function, T> = F extends (x: T) => infer U ? U : never;
type d = Call1<<T>(x: T) => T, 1>
// ^? type d = unknown
// Doesn't work with complex expressions
type e = Call1<<T>(x: T) => [ T, 2 ], 1>
// ^? type e = [ unknown, 2 ]
// Works when there aren't generics
type Is1<T> = Call<(x: 1) => true, [ T ]>;
type f = [ Is1<true>, Is1<1> ];
// ^? type f = [ never, true ]
}
🙁 Actual behavior
Types c, d and e[0] are unknown
🙂 Expected behavior
Types c, d and e[0] should have all been 1.
I tried to infer the return type of some generics functions when they got supplied with certain types, I can check wether the return type is something else (true inside Check in this example) but I cannot actually get it
Additional information about the issue
No response Activityjcalz commented on Aug 19, 2023 TS doesn't have true call types of the sort you'd need for this to work.
Looks like a duplicate of #52035 (or at least this comment) and refers back to #6606. AFatNiBBa commented on Aug 20, 2023 I think this is something that has to be looked into because it has great potential and it almost works.
Consider the following:
type FilterObject<T, P extends (v: any, k: any, t: any) => boolean> = { [k in keyof T as P extends (v: T[k], k: k, t: T) => true ? k : never]: T[k] };
type Test = { ab: 1, ac: 2, cb: 3, ad: 1, g: "g" };
type a = FilterObject<Test, (v: 1) => true>;
// ^? type a = { ab: 1, ad: 1 }
type b = FilterObject<Test, <K>(_: any, k: K) => K extends `a${string}` ? false : true>;
// ^? type b = { cb: 3, g: "g" }
type c = FilterObject<Test, <V, K>(v: V, k: K) => K extends V ? true : false>;
// ^? type c = { g: "g" }
It understands what the return type is, so why can't it infer it fatcerberus commented on Aug 20, 2023 It seems like you want specifically #40179 Reacted by Joe Calzaretta, Sean Alunni and Martin Johns jcalz commented on Aug 20, 2023 Yeah #40179 was the one I was searching for but "call types" wasn't mentioned there steveluscher commented on Aug 22, 2023 I'm also blocked from making one of my more complicated types generic because of this. The satisfies keyword is able to expand all of the possible return types of doThing below, so can we make infer do the same?
type WrapConfig = { wrap?: boolean };
type ReturnValue<TWrapConfig extends WrapConfig | void, T> =
TWrapConfig extends object
? TWrapConfig['wrap'] extends true ? T[]
: T
: T;
function doThing<TWrapConfig extends WrapConfig | void = void>(
config?: TWrapConfig
): ReturnValue<TWrapConfig, string> {
return (config?.wrap ? ['hi'] : 'hi') as ReturnValue<TWrapConfig, string>;
}
// Test that the function return type is parametric on the shape of the arguments
doThing({ wrap: true }) satisfies string[];
doThing({ wrap: false }) satisfies string;
doThing({}) satisfies string;
doThing() satisfies string;
// Test that the function, without type parameter, spans the union of return types
doThing satisfies ((...args: any[]) => string[] | string);
// Try to infer the return type without supplying the type parameter
type DoThingReturn = typeof doThing extends (...args: any[]) => infer R ? R : unknown;
(null as unknown as DoThingReturn) satisfies string[] | string; // ERROR
Output
"use strict";
function doThing(config) {
return ((config === null || config === void 0 ? void 0 : config.wrap) ? ['hi'] : 'hi');
}
// Test that the function return type is parametric on the shape of the arguments
doThing({ wrap: true });
doThing({ wrap: false });
doThing({});
doThing();
// Test that the function, without type parameter, spans the union of return types
doThing;
null; // ERROR
Compiler Options
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictPropertyInitialization": true,
"strictBindCallApply": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"alwaysStrict": true,
"esModuleInterop": true,
"declaration": true,
"target": "ES2017",
"jsx": "react",
"module": "ESNext",
"moduleResolution": "node"
}
}
Playground Link: Provided typescript-bot commented on Aug 25, 2023 This issue has been marked as "Duplicate" and has seen no recent activity. It has been automatically closed for house-keeping purposes. locked as resolved and limited conversation to collaborators on Oct 22, 2025
MetadataMetadataDevelopmentNo branches or pull requestsIssue actions
|