-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Rule proposal: no-inferrable-types
equivalent for function return types
#2673
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
no-inferrable-types
equivalent for function return types
no-inferrable-types
equivalent for function return typesno-inferrable-types
equivalent for function return types
someone have this settings work? |
I opened this issue when I was a naive child. I now see the error of my ways. |
@vegerot I believe it is still a valid addition. Why do you think this would be a bad idea now? I know there are cases where the type needs to be explicit for the return to be correctly 'filtered.' But there are straightforward cases, like the ones you mentioned, that would be great. Less clutter in the code for obvious things. |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
I opened this issue when I was young and dumb. I would never use such a rule |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
The rule shouldn't be about preventing you from specifying the return type. It's supposed to prevent you from explicitly writing what Typescript already knows. |
The trouble is... TS will basically always come up with a return type, no matter what you return. It's just the union of whatever you return. For example, function foo() {
if (Math.random() < 0.5) {
return 'string';
} else {
return 42;
}
}
foo;
//^? function foo(): "string" | 42 So, "prevent you from explicitly writing what Typescript already knows" implies no explicitly specified return types, ever. Whether or not one prefers or dislikes inferred return types (disclaimer - I prefer them always to be explicit), one would need to come up with some understandable criteria that ban some but not all return type annotations, in order for this issue to be actionable. No such criteria have been proposed in this thread yet. Perhaps someone would like to have a stab at it? If not, I think we should close this issue. |
@kirkwaiblinger That means the rule would error if you write I think this rule has a lot of value in itself; I'm a big +1 as a user, though not sure about complexity. |
I see.... so you're thinking "If the return type is the same as what would have been inferred, ban it. Otherwise allowed."? |
Coming over from #9764 (comment): now that we can use // I would hope this would error?
function giveText(): string {
return 'abc' as const;
} |
No because it would be intended. function mayReturnStringOrNumber(): string | number {
return "Currently is just a constant: will return number later";
} Here, if the rule forces you to remove the type, then later the addition of I don't think we can even use "X is assignable to Y and Y is assignable to X" without shooting down a lot of valid code. For exmaple, |
Honestly I'm a -1 on this rule because it's so vague. For example interface T { a: string }
function foo(arg: string): T {
return { a: arg };
} Should this report? This applies to things like type Path = string
function asPath(arg: string): Path {
if (someComplicatedLogicIsFalse()) {
return coerceArgToPath(arg);
}
return arg;
} Should the rule report here? Well maybe in future we wanted to make I just think there are so many cases that are ambiguous and you would end up with surprising results. Additionally in FOUR YEARs this request has garnered just 8 unique reactions. So yeah, big -1 from me. |
Personally, my thoughts here align with @bradzacher's |
@Josh-Cena is a +1, so that leaves @JoshuaKGoldberg. |
What if we made the rule only report if:
The former point solves for wider declarations than inferred types (#2673 (comment), I still think there's value in a rule to flag flagrantly unnecessary return types. e.g.: function hasBooleanMatch(values: boolean[]): boolean {
return values.some(Boolean);
} |
In this function, function isBoolean(v: unknown): boolean {
return typeof v === 'boolean';
} But if you remove the return type now the return type is inferred as See specialisation of this rule in #9764 declare function querySelector<T>(selector: string): T;
interface MyNode { type: 'node' }
function myFunction1(): MyNode {
return querySelector('node');
}
function myFunction2() {
// ^?
return querySelector('node');
} playground This is a surprising result that we've similarly seen from function myFunction1(): string {
return 'a';
}
function myFunction2(): 'a' {
return 'a';
}
function myFunction3() {
// ^?
return 'a';
} Which annotation is "the most correct"? Both are correct and technically by definition both are inferable. My point: But in 4 years we haven't even had TEN community interactions with this issue. Hence my conclusion is that the value is much, much lower than the very high cost of implementation and what will likely be a high cost of maintenance (at least in the short term as we iron out the documentation and edge cases). |
Before inferred predicates, it would error if you annotated
I agree it's surprising but we already have similar behavior. I don't think we should not build a rule just because of certain limitations that users are already aware of.
That depends on what TS thinks? If TS infers |
Well funny story cos the answer is both! Or rather "either depending on the context of the return value". declare const x: "a";
function f() {
// ^? () => "a"
return x;
}
function g() {
// ^? () => string
return "a";
} This surprising nature is the sort of logic that we would need to encode into the rule. Note that it is not evidently clear from the type objects alone that this is the case and instead we would need to do syntactic checks. This is the sort of case that can seem surprising and buggy to users.
Similar, buggy behaviour that we just haven't gotten around to fixing yet. A new rule would be required to not have the same bug given it's well known, of course. My comment was just intended to be some examples of hairy cases. There will be many more that would be uncovered through implementation and more still through initial user testing. I just don't think the user interest in such a rule existing (a small percentage going by the community interactions) is large enough to outweigh the likely large cost of design, implementation and support for such a rule. Either way - I've said my piece. I'm a strong -1 and there isn't anything that is going to convince me otherwise barring a overwhelming outpouring of support from the community in favour of it. |
If it's significantly more difficult than "ask TS what would the inferred type be if there's no type annotation" then I also don't think there's significant ROI. I would love to be a user of this rule though. |
This is not possible. TS cannot do speculative querying at all and instead we can only ask it for the types based on the code as it is written. So that means we must query the functions control flow graph to collect all its termination points so that we can guess at what TS will use as the return type for each one, then we can manually recreate the resulting union type and compare it against the annotated return type. Which is a whole thing that I haven't even gone into or even done before in a rule -- control flow analysis is complicated. All the examples we've been looking at are simple zero branch, single return functions. But like here's the next level of complexity function foo(b: boolean): string | number {
if (b) {
return 1;
}
return 'a';
} Then there's At a high level this rule would be:
Step 2 we've never done before. |
...at this point I'm fine with dropping the issue. I still would really really like to have the rule exist, but Brad's points on the difficulties of it have scared me away from wanting to tackle it in typescript-eslint. π£ |
^Exactly same thought. |
We have consensus. To anybody reading this and feeling π on the decision: we're not denying that the rule can exist! Just that we, as a community open source project, have nowhere near enough time to maintain it. You're welcome to use the docs in https://typescript-eslint.io/developers/custom-rules to build the rule yourself. In fact, I'd request someone please build this rule & prove us wrong. It'd be a great one to have exist. π Thanks all! |
Uh oh!
There was an error while loading. Please reload this page.
tl;dr I would like a rule to ban inferable function return types, similar to how
no-inferrable-types
acts on variables.Expected Result
If I had
I would expect all inferable types to be banned
Actual Result
Instead, since that second rule that doesn't exist, I'm stuck with
Additional Info
I would like a rule to ban inferable function return types, similar to how
no-inferrable-types
acts on variables.Versions
@typescript-eslint/eslint-plugin
latest
@typescript-eslint/parser
latest
TypeScript
latest
ESLint
latest
node
latest
The text was updated successfully, but these errors were encountered: