Skip to content

Return type inference breaks in function parameter; tooltip also inconsistent #32540

Description

@AnyhowStep

TypeScript Version: 3.5.1

Search Terms:

conditional type, type arg, inference, return type, argument, parameter, generic, callback function

Code

This is literally the minimum repro I could make and it's still too big.

exportinterfaceCompileError<_ErrorMessageTextendsany[]>{/** * There should never be a value of this type */readonly__compileError : never;}/** * Each `string` element represents a column name. * * A "key" is a set of columns that uniquely identifies * a row in a table. */exporttypeKey=readonlystring[];exporttypeExtractSubKey<AextendsKey,BextendsKey>=(AextendsKey ?
(BextendsKey ?
(A[number]extendsB[number] ?
A :
never) :
never) :
never);exporttypeExtractSuperKey<AextendsKey,BextendsKey>=(AextendsKey ?
(BextendsKey ?
(B[number]extendsA[number] ?
A :
never) :
never) :
never);exporttypeFindSubKey<ArrTextendsreadonlyKey[],KeyTextendsKey>=(ExtractSubKey<ArrT[number],KeyT>);exporttypeFindSuperKey<ArrTextendsreadonlyKey[],KeyTextendsKey>=(ExtractSuperKey<ArrT[number],KeyT>);declarefunctionnoSubKey<KeyTextendsKey>(arg : ((c : {x : "x",y : "y",z : "z"})=>KeyT&(FindSuperKey<(("x"|"y")[])[],KeyT>extendsnever ?
unknown :
CompileError<[KeyT,"is a sub key of",FindSuperKey<(("x"|"y")[])[],KeyT>]>))) : void//OK!//Expected: Infer KeyT as "z"[]//Actual : Infer KeyT as "z"[]//Tooltip : noSubKey<"z"[]>noSubKey(c=>[c.z])//OK!//Expected: CompileError<["x"[], "is a sub key of", ("x" | "y")[]]>//Actual : CompileError<["x"[], "is a sub key of", ("x" | "y")[]]>//Error!//Tooltip : noSubKey<readonly string[]>noSubKey(c=>[c.x])//OK!//Expected: CompileError<[("x" | "y")[], "is a sub key of", ("x" | "y")[]]>//Actual : CompileError<[("x" | "y")[], "is a sub key of", ("x" | "y")[]]>//Error!//Tooltip : noSubKey<readonly string[]>noSubKey(c=>[c.x,c.y])//OK!//Expected: Infer KeyT as "z"[]//Actual : Infer KeyT as "z"[]//Tooltip : noSubKey<"z"[]>noSubKey(()=>["z"as"z"]);//OK!//Expected: CompileError<["x"[], "is a sub key of", ("x" | "y")[]]>//Actual : CompileError<["x"[], "is a sub key of", ("x" | "y")[]]>//Tooltip : noSubKey<"x"[]>noSubKey(()=>["x"as"x"]);//OK!//Expected: CompileError<[("x" | "y")[], "is a sub key of", ("x" | "y")[]]>//Actual : CompileError<[("x" | "y")[], "is a sub key of", ("x" | "y")[]]>//Tooltip : noSubKey<("x" | "y")[]>noSubKey(()=>["x"as"x","y"as"y"]);declarefunctionnoSuperKey<KeyTextendsKey>(arg : (((c : {x : "x",y : "y",z : "z"})=>KeyT)&(FindSubKey<(("x"|"y")[])[],KeyT>extendsnever ?
unknown :
CompileError<[KeyT,"is a super key of",FindSubKey<(("x"|"y")[])[],KeyT>]>))) : void//Error!//Expected: Infer KeyT as "z"[]//Actual : CompileError<[readonly string[], "is a super key of", ("x" | "y")[]]>//Tooltip : noSuperKey<readonly string[]>noSuperKey(c=>[c.z])//Error!//Expected: Infer KeyT as "x"[]//Actual : CompileError<[readonly string[], "is a super key of", ("x" | "y")[]]>//Tooltip : noSuperKey<readonly string[]>noSuperKey(c=>[c.x])//Error!//Expected: CompileError<[("x" | "y")[], "is a super key of", ("x" | "y")[]]>//Actual : CompileError<[readonly string[], "is a super key of", ("x" | "y")[]]>//Tooltip : noSuperKey<readonly string[]>noSuperKey(c=>[c.x,c.y])//OK!//Expected: Infer KeyT as "z"[]//Actual : Infer KeyT as "z"[]//Tooltip : noSuperKey<"z"[]>noSuperKey(()=>["z"as"z"]);//OK!//Expected: Infer KeyT as "x"[]//Actual : Infer KeyT as "x"[]//Tooltip : noSuperKey<"x"[]>noSuperKey(()=>["x"as"x"]);//OK!//Expected: CompileError<[("x" | "y")[], "is a super key of", ("x" | "y")[]]>//Actual : CompileError<[("x" | "y")[], "is a super key of", ("x" | "y")[]]>//Tooltip : noSuperKey<("x" | "y")[]>noSuperKey(()=>["x"as"x","y"as"y"]);/** * Seems weird that using the `c` argument results in inference problems. * But using string literals without the `c` argument is okay. */

Expected behavior:

Whether I use the c argument or not when calling noSubKey()/noSuperKey(),
it should always infer the type of KeyT correctly for all cases.

Actual behavior:

noSubKey()

  • With c argument, it correctly infers KeyT in the error message

  • With c argument, it incorrectly infers KeyT in the tooltip

  • With string literals, it correctly infers KeyT in the error message

  • With string literals, it correctly infers KeyT in the tooltip

noSuperKey()

  • With c argument, it incorrectly infers KeyT in the error message

  • With c argument, it incorrectly infers KeyT in the tooltip

  • With string literals, it correctly infers KeyT in the error message

  • With string literals, it correctly infers KeyT in the tooltip

Playground Link:

Playground

Related Issues:

Off the top of my head, I've made similar reports before and other similar repro cases,

#29133

#23689 (comment)

From my personal experience, it feels like the moment you are using the ReturnType<> of a function in a conditional type (directly or indirectly) inside a parameter, you end up having weird issues with inference.

For the longest time, I've been trying to find a solid workaround but nothing seems to quite stick.

Every workaround I can come up with will work in some situation but not in others.


You'll notice that, in this repro, I never even use ReturnType<> on FunctionT.

I use KeyT and make the parameter (c) => KeyT and it works in some cases but breaks in this case for noSuperKey() and works (mostly) fine for noSubKey()

Metadata

Metadata

Assignees

Labels

Needs More InfoThe issue still hasn't been fully clarified

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions