-
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
Function type parenthesized when used in a union type prevents usage of conditional function returns #56705
Comments
This is working as intended. You get an error, because your function can return either a The type If you meant to write a function that returns a |
In my above given example, returning either a string or a callable function is exactly what it intends to do. I will illustrate the intent with a more relatable (simplified) use example; Given the data structure: type data = Record<string, {value: any, children: data}> I want to avoid retrieving things like so: data['level1'].children['level2'].children['level3'].value and instead do: getData('level1')('level2')('level3')() I may also want to make some more decisions along the data retrieval path. This can be implemented in native JS; const inputData = {
level1: {
value: 'level1',
children: {
level2: {
value: 'level2',
children: {
level3: {
value: 'level3',
},
},
},
},
},
};
function getData(key) {
return ((data) => fork.bind(null, data))(inputData[key]);
function fork(data, key) {
return ((children, val) => {
/** Do some interesting stuff here */
return !!key ? fork.bind(null, children[key]) : val;
})(data.children, data.value);
}
}
console.log(
getData('level1')(),
getData('level1')('level2')(),
getData('level1')('level2')('level3')(),
);
// level1 level2 level3 The above is valid working code which does not need protection from runtime errors. There is enough space for input validation and error handling within the code. As can be seen in this playground, which recreates the above both with and without overloading, Typescript prevents it from running without error suppression using It is apparent that the compiler (in this scenario) is insisting on wrapping a return function type in parenthesis, which means that the type is inconsistent with what the compiler is actually doing. This is both opinionated, and imo, a bug. |
I have no clue what you're trying to say with this, but I'm 99% sure it's a red herring. The two problems you have are:
|
I will clarify: The compiler wants me to change the typing to You can see this on lines [32-34] and [48-50] of the playground example. As I illustrated in the JS example given above, this is valid running code and I would like to use Typescript to write this code without resorting to |
You can’t call the function because it’s typed as |
It's asking you to clarify a syntactic ambiguity that other humans will have a problem with, not "change the typing"
I really can't tell that it is. Either this function can return a string or a function or it always returns a function. If the former, then it's not safe to unconditionally invoke its result. If the latter, then your annotated return type is wrong. TS doesn't have a mechanism to say "This function will return whatever type you hope it will"; this isn't how code works. |
The native code example I gave (without typing) works perfectly well. If you are saying that this is not achievable in TS without Link to the native code running: https://onecompiler.com/javascript/3zvr43fha An here is a TS Playground of the TS code compiling and running (albeit with |
You can certainly write meaningful types for these sorts of things. I'm not working through your example because this is not a support forum, but something like this should get you headed in the right direction: interface TreeBase {
value?: string;
children: Record<string, TreeBase>;
}
interface Getter<T extends TreeBase>{
(): T["value"];
<K extends keyof T["children"] & string>(node: K): Getter<T["children"][K]>;
} |
@nmain , That results in Typescript that compiles and runs. Thank you. I will argue that it is a circuitous workaround (however clever it is) as opposed to just being able to declare the type for what it is up front, ie: The need to wrap the returned function in parenthesis, ie: It is my opinion that end user experience issues, including that of consuming developers, can be bugs, and that this one is a bug. |
It seems you're still confusing two entirely different issues. One is the parsing ambiguity, which the parenthesis solves. It does not change the rest of the code or behavior in any way. The actual issue with the type |
This was done on purpose because the type Projects making decisions differently is not "bug". |
It changes the code behavior, as the code becomes un-callable. You can see this on lines [32-34] and [48-50] of this playground example.
Fair enough. I did not expected such an a opinion to be embedded into a compiler. I know enough of type systems in a few languages to get by, but I am not at all an expert. The point I am making is that the code runs, it can compile without type errors (thanks again @nmain), but the implementation is circuitous and counter intuitive (at least for this human). Do with that what you feel. I feel it is a bug, but also do as you feel if you want to close this issue. |
It does not change the behavior - a type of the form |
🔎 Search Terms
"Function type notation must be parenthesized when used in a union type"
🕗 Version & Regression Information
⏯ Playground Link
https://www.typescriptlang.org/play?#code/GYVwdgxgLglg9mABMOAnA1gCgM5wLYCmAagIYA2IBA-AFyLZSoxgDmAlAN4BQiviqBKCFRIAhKNyFSFAoir18xcpUR0UGLgF8uXCAlxkCAOjJwWmdVgDkkpTKts2AbkQB6V4igALGNkQB3NHRsLndEAAEobABaGBYwNAJdfThDEzMLIMw2TBtFAHlvAlRpSgdnNw9vXwCg7AAaRAAjEChPL1Q4fz8STz89PAAHGENURGLO1B1XACoZnhmArwIkAElPVABPTzhEPQJiiFkivbQBaE9NwYJGmAInBcQrZ8ejN9V6RmYWRAAfREwADdyLRPkxWGwALwAPkQJDAm0ezysj0e6xYgnasgGw1G41QkzoAAMAGLgaDwJBQK6yBJQEiwBCIPAgBjNWSDEgCMBFbAwABeBAAJksVohWcLEMw4eKwJTLtcjJgAIwAZgAHABWNhEhauIA
💻 Code
🙁 Actual behavior
The compiler prevented a programming pattern available in native ES.
Coercing the type to the correct form led to another TS error.
🙂 Expected behavior
I expected the compiler to allow all ES patterns without needing to use
//@ts-ignore
Additional information about the issue
No response
The text was updated successfully, but these errors were encountered: