-
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
Infer arrow function type guard type for specific simple cases #38390
Comments
Notably, this would be a breaking change const a = [1, "foo", 2, "bar"].filter(x => x instanceof "string");
// Currently legal
a.push(10); |
Yes, looks like it would, as would any suggestion about type guard inference. Neither do I really have any ideas of how to avoid that. The most sane one would probably be some kind of a syntactical marker for "treat this function as a type guard, but infer the type if possible, and error if not". Or maybe just an "as narrow a type as you can infer" assertion for expressions, similar to (a part of) what const f1 = x => typeof x === "string"; // (x: any) => boolean;
const f2 = (x => typeof x === "string") as narrow; // (x: any) => x is string
// or maybe even
const a = [1, "foo", 2, "bar"].filter(x => x instanceof "string") as narrow; // string[] But this goes way out of scope of this feature request. So yeah, this request was probably a bad idea then. |
The only way to avoid this being a breaking change would be to introduce a not-in-javascript operator that changes the behaviour of the type inference OR to add in a typescript compiler |
@iliazeus What about this syntax meaning // a is a string[]
const a = [1, "foo", 2, "bar"].filter((x): x is ? => x instanceof "string");
// b is a (string | number)[]
const b = [1, "foo", 2, "bar"].filter(x => x instanceof "string"); The only new syntax is the We can optionally use something other than the Thoughts @iliazeus @RyanCavanaugh |
This would be a very valuable step to take, but I agree with the inference cautions. There needs to be something syntactical that marks that you're intending predicate inference. As far as // a is a string[]
const a = [1, "foo", 2, "bar"].filter(infer (x) => x instanceof "string");
// OR...
// a is a string[]
const a = [1, "foo", 2, "bar"].filter(predicate (x) => x instanceof "string");
// b is a (string | number)[]
const b = [1, "foo", 2, "bar"].filter(x => x instanceof "string"); |
Personally I don't feel the desire for type hint syntax. A new Anecdotally I have come across code like this a few times: declare let someArray: (string | null)[];
declare function useString(s: string): void;
someArray
.filter(v => v !== null)
.forEach(v => {
useString(v!); // unnessesary null assertion to stop strictNullError
}); With added syntax required I can see people still being inclined to continue to just add the null assertion, this new 'infer type guard' syntax will possibly be seen as 'advanced syntax' that not everyone is aware of. When code is written in a structured style TS infers the type guard without syntax: for (const v of someArray) {
if (v !== null) {
useString(v); // value is inferred correctly without type syntax
}
} This all said, I do feel that the infererence should only be done for inline callbacks where the function lists a type guard as one of the possible parameters. Otherwise I could see it being a performance issue on large code bases. // no type guard infered
const f = (v: any) => typeof v === 'string;
// type guard infered because of filter's type being:
// filter<S extends T>(cb: (value: T) => value is S): S[];
arr.filter(v => typeof v === 'string) |
Hm, so now that TS 4.4 added #44730, could this happen too? They seem at least vaguely related to this non-expert... |
I can't wait for this <3 I am curious, what kind of feedback is needed? |
re @RyanCavanaugh 's breaking change example: Likewise, as somebody who never mutates data in their code, a --strictInference flag sounds great, as I tend to only ever wish for more inference. |
If implemented would this flag also cover this case? #14826 |
I know that this is blocked by introducing breaking changes. But has anybody a real world example of production code, that would break? It seems like a close to unreachable corner case. Secondly if somebody where to reach this corner case they could very quickly resolve it and would notice it right away when they upgrade typescript. This question has been asked so many times and so many people spend time discussing it and having a bad typescript experience. So I argue that this is a case where the right solution is to introduce a breaking change. |
There's a lot of |
IMO thoughts regarding a breaking changes are valid, but options like a compiler flag (+ gracefully defaulting to it in a few more major versions) looking like a fair tradeoff, especially given the improved DX. |
I've also run into this a number of times. In the past, I just used a The breaking changes don't seem like they would cause problems for many valid cases, and a feature flag seems like a reasonable workaround (though to be fair, I'm not familiar with TS breaking change or feature flag policies—anyone know where I can learn more?). |
Search Terms
arrow function type guard
Suggestion
Specific simple cases of arrow functions of a single argument should be considered type guards:
There are more general requests like #16069, but I beleive this one should be easier to implement, and it still covers a lot of use cases.
Use Cases
filter
with heterogenous arrrays:Specifically,
filter
ing outnull
orundefined
elements:Limiting the number of arguments of a more complex type guard:
Examples
Checklist
My suggestion meets these guidelines:
The text was updated successfully, but these errors were encountered: