-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Narrowing types based on property access #46219
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
Duplicate of #39065. You're wrongly assuming that just because a truthy |
declare const f: Foo | Foo[] If |
Not necessarily: interface Foo {
foo: string;
}
interface Bar {
foo: number;
bar: string;
}
// not a valid `Bar` because `Bar` calls for `foo: number`.
// it IS a valid `Foo`, though!
const thing = { foo: "pig", bar: "cow" };
doIt(thing);
function doIt(foobar: Foo | Bar)
{
if ('bar' in foobar) {
foobar.foo; // hover here - TS thinks it's a `Bar` and therefore that `foobar.foo` is a number
}
} |
Yes, but, in this case, |
As I said, based in type |
It's completely possible for |
This is reason why |
Yes. Look the playground link and do this. You will get a compiler error |
|
class Foo {
bar: object = {}
meth() {}
}
const f: Foo | Foo[] = Object.assign([], { bar: {} });
console.log(Array.isArray(f))
console.log('bar' in f) Will print out Also just pointing out that you can edit your comments, no need to add comment over comment. |
|
You are assing |
I give up. |
Yes, I mentioned this unsoundness with the |
You can do |
Only if we had Exact Types. #12936. Spoiler alert: We don't. |
You want a type-based system not to consider the type because you can create "patches" to circumvent the typing |
The compiler is wrong seeing it, as my example demonstrated. It sees it as |
And here's an example without type FooArrayWithBarProperty = Foo[] & { bar?: string }
const fooArrayWithBarProperty: FooArrayWithBarProperty = []
fooArrayWithBarProperty.bar = 'hello!'
const ff: Foo | Foo[] = fooArrayWithBarProperty;
if ('bar' in ff) {
ff;
} |
Solution. Do right and don't use |
Try to call |
Honestly, this is getting ridiculous. It's clear that you don't know what the heck you're talking about. In the brief example you can't call If you take this behaviour out of the equation (e.g. by returning the value from a function) you will get the issue mentioned: class Foo {
bar: object = {}
meth() {}
}
type FooArrayWithBarProperty = Foo[] & { bar?: string }
const ff: Foo | Foo[] = getValue();
if ('bar' in ff) {
// ff is narrowed to "Foo", but the value is actually a "Foo[]".
// Calling meth() will result in an error.
ff.meth();
}
function getValue(): Foo | Foo[] {
const fooArrayWithBarProperty: FooArrayWithBarProperty = [];
fooArrayWithBarProperty.bar = 'hello!';
return fooArrayWithBarProperty;
} |
I understand these discussions can get frustrating - I encourage everyone to cool down for a bit. @MartinJohns and @fatcerberus are correct in their assessment. This is a duplicate of #39065, a duplicate of #38842, and probably more. We've made the design decision not to allow this code for the exact reasons discussed in this issue along with those issues. Object types are open (i.e. "width" subtyping) which means any object could have a property that it doesn't declare, and it's unsafe to narrow based on that. |
I'm not saying that a type cannot have additional properties (I said the exact opposite here myself). I'm saying the compiler is correct when handling This was explained here: #15256 (comment)
Yes, it is a duplicate and, even if it wasn't, the issue is incoherent and must be closed |
Maybe I might not have expressed myself well before because my english is more google than me. Sorry by the possible language mistakes. |
Suggestion
π Search Terms
type narrowing based on property checks, similar to
in
operator.β Viability Checklist
My suggestion meets these guidelines:
β Suggestion
Lot's of JS code uses duck typing. For example:
π Motivating Example
playground
π» Use Cases
We can currently do something similar that actually works:
playground
But using a property check would allow us to write code like we can (and like currently exists) in plain JS. The
f.bar
check would behave similar to the'bar' in f
check, and would narrow the type.This would need to be limited to certain property types. For example, if
Foo.bar
were a number, then a value of0
can make it result in afalse
even if the property exists. In this case TS would need to throw an error and would not be able to perform type narrowing in such cases.If the type of
Foo.bar
is something that is always truthy (f.e.bar: Record<Foo, Bar>
is always truthy) then type narrowing can be performed. The system would have to clearly report errors in cases when type narrowing can not be confidently performed based on the encountered type.The text was updated successfully, but these errors were encountered: