You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Infer arrow function type guard type for specific simple cases #38390
Specific simple cases of arrow functions of a single argument should be considered type guards:
constf1=x=>xtypeof"string";// immediately returns a typeof expression with a literal rhsconstf2=x=>xinstanceofFoo;// immediately returns an instanceof expressionconstf3=x=>x==="foo";// immediately returns a strict (non-)equality comparison with a literalconstf4=x=>x==null;// immediately returns a non-strict (non-)equality comparison with a null literalconstf5=x=>isFoo(x);// immediately returns another type guard
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:
// currently: (string | number)[]// would be: string[]consta=[1,"foo",2,"bar"].filter(x=>xinstanceof"string");
Specifically, filtering out null or undefined elements:
Limiting the number of arguments of a more complex type guard:
// isFoo: (x: any, strict?: boolean) => x is Fooconstb1=a.filter(x=>isFoo(x));constb2=a.filter(x=>isFoo(x,true));// compare:constb3=a.filter(isFoo);// No overload matches this call.
Examples
Checklist
My suggestion meets these guidelines:
This wouldn't be a breaking change in existing TypeScript/JavaScript code
This wouldn't change the runtime behavior of existing JavaScript code
This could be implemented without emitting different JS based on the types of the expressions
This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
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 as const does.
constf1=x=>typeofx==="string";// (x: any) => boolean;constf2=(x=>typeofx==="string")asnarrow;// (x: any) => x is string// or maybe evenconsta=[1,"foo",2,"bar"].filter(x=>xinstanceof"string")asnarrow;// 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 --strictInference opt-in flag. I don't know what the Typescript appetite for either would be. Or what the priority of this would be.
// a is a string[]consta=[1,"foo",2,"bar"].filter((x): x is ? =>xinstanceof"string");// b is a (string | number)[]constb=[1,"foo",2,"bar"].filter(x=>xinstanceof"string");
The only new syntax is the x is ? predicate, which would fail to compile if the predicate type cannot be inferred. This makes it opt-in and a non-breaking change.
We can optionally use something other than the ?. I also like _.
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 x is ?, I don't think that communicates what's happening very clearly. I would advocate a keyword that is more explicit and clear:
// a is a string[]consta=[1,"foo",2,"bar"].filter(infer(x)=>xinstanceof"string");// OR...// a is a string[]consta=[1,"foo",2,"bar"].filter(predicate(x)=>xinstanceof"string");// b is a (string | number)[]constb=[1,"foo",2,"bar"].filter(x=>xinstanceof"string");
Reacted by Corentin Girard, Torvin, Michael Peirce, smiley-uriux and Michael Pitts
Personally I don't feel the desire for type hint syntax.
A new --stictXXXX flag added seems inline with other breaking changes to TS's type inference. It would be interesting if someone could produce some stats on the number of times this would trigger a breaking change when sampled on a group of open-sources repos, also if the error actually seemed to catch a potential bug.
Anecdotally I have come across code like this a few times:
declareletsomeArray: (string|null)[];declarefunctionuseString(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(constvofsomeArray){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 inferedconstf=(v: any)=>typeofv=== 'string;
// type guard infered because of filter's type being:// filter<S extends T>(cb: (value: T) => value is S): S[];arr.filter(v=>typeofv=== 'string)
Reacted by Ilia Pozdnyakov, Daniel Lamando, Patrick Remy, Julian Schmidt, Daniel Cousens, Drew Lee (이드로), Robbie Ostrow, Michelle, Alexander Khoroshikh, Jacques Favreau and 1 more
re Ryan Cavanaugh (@RyanCavanaugh) 's breaking change example:
it seems to me that if a is readonly then there would be no breaking change. Maybe we could simply infer / refine only in that case then?
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.
Reacted by Mathias Lykkegaard Lorenzen, Lev Izraelit, Robbie Ostrow and agrippa
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.
Reacted by Daniel Lamando, Torvin, Julian Schmidt, domi, Jeremie J., Samuel Fernández, clover caruso, Florian Veys, Eugene Zaretskiy, Jack Taylor and 10 more
There's a lot of instanceof "string" in the example code here and it gave me a brief feeling of vertigo. "Oh no, is this the Mandela Effect? Does instanceof "string" work in some new universe in which I've found myself?" But no, I just checked, I'm in the right place; "abc" instanceof "string" produces a TypeError at runtime. Maybe this issue has been transported from another plane of existence?
Reacted by Daniel Lamando, Bruce Pascoe, katini, agrippa, Sung Jeon, Jakob Guddas, Andrii Dieiev, Maciek Sakrejda, Matt Kantor, Dan Vanderkam and 3 more
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.
Reacted by Jacques Favreau, Maciek Sakrejda, michaelo-numerik, Nato Boram and Adam Shaylor
I've also run into this a number of times. In the past, I just used a ! assertion to work around it. Now I write a user type predicate. But it'd be nice not to have to do that for simple cases.
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?).
Activity
RyanCavanaugh commentedon May 8, 2020
Notably, this would be a breaking change
iliazeus commentedon May 8, 2020
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
as constdoes.But this goes way out of scope of this feature request.
So yeah, this request was probably a bad idea then.
robertmassaioli commentedon May 9, 2020
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
--strictInferenceopt-in flag. I don't know what the Typescript appetite for either would be. Or what the priority of this would be.vultix commentedon Jun 1, 2020
Ilia Pozdnyakov (@iliazeus) What about this syntax meaning
inferred type predicate?The only new syntax is the
x is ?predicate, which would fail to compile if the predicate type cannot be inferred. This makes it opt-in and a non-breaking change.We can optionally use something other than the
?. I also like_.Thoughts Ilia Pozdnyakov (@iliazeus) Ryan Cavanaugh (@RyanCavanaugh)
isaiahtaylorhh commentedon Jun 22, 2020
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
x is ?, I don't think that communicates what's happening very clearly. I would advocate a keyword that is more explicit and clear:acutmore commentedon Nov 18, 2020
Personally I don't feel the desire for type hint syntax.
A new
--stictXXXXflag added seems inline with other breaking changes to TS's type inference. It would be interesting if someone could produce some stats on the number of times this would trigger a breaking change when sampled on a group of open-sources repos, also if the error actually seemed to catch a potential bug.Anecdotally I have come across code like this a few times:
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:
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.
haltman-at commentedon Aug 6, 2021
Hm, so now that TS 4.4 added #44730, could this happen too? They seem at least vaguely related to this non-expert...
ffMathy commentedon Sep 15, 2021
I can't wait for this <3
I am curious, what kind of feedback is needed?
asktree commentedon Mar 13, 2022
re Ryan Cavanaugh (@RyanCavanaugh) 's breaking change example:
it seems to me that if
ais readonly then there would be no breaking change. Maybe we could simply infer / refine only in that case then?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.
Tiedye commentedon Jun 2, 2022
If implemented would this flag also cover this case? #14826
PMLP-novo commentedon Aug 23, 2022
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.
jcalz commentedon Sep 17, 2022
There's a lot of
instanceof "string"in the example code here and it gave me a brief feeling of vertigo. "Oh no, is this the Mandela Effect? Doesinstanceof "string"work in some new universe in which I've found myself?" But no, I just checked, I'm in the right place;"abc" instanceof "string"produces aTypeErrorat runtime. Maybe this issue has been transported from another plane of existence?TheAlexLichter commentedon Jul 11, 2023
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.
msakrejda commentedon Sep 8, 2023
I've also run into this a number of times. In the past, I just used a
!assertion to work around it. Now I write a user type predicate. But it'd be nice not to have to do that for simple cases.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?).