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
Invalid type checking on union types returned getters #56149
typeSomeType={prop: "prop1"}|{prop: "prop2"};classSomeClass{a=0;getsomeType(): SomeType{returnthis.a%2===0 ? {prop: "prop1"} : {prop: "prop2"};}}constsomeClass=newSomeClass();if(someClass.someType.prop==="prop1"){console.log("prop1");someClass.a=Math.random()*10;// error comes in the following line//This comparison appears to be unintentional because the types '"prop1"' and '"prop2"' have no overlapif(someClass.someType.prop==="prop2"){console.log("prop2");}}
🙁 Actual behavior
When using union types as returned the type of a getter, TypeScript shows errors that cannot be certain about. I am not very good at putting the error into words, please check the sample code given above. It is very easy to understand. Sorry for not explaining properly.
🙂 Expected behavior
TypeScript should not check union-based returned types from getters conditionally.
Still bad at explaining this ☹️ But I hope you can easily get an idea about the expected behavior by looking at the code
TypeScript should not check union-based returned types from getters conditionally.
TypeScript does not distinguish between getters and properties. They're both properties in the type system, so there's nothing to cause different behaviour.
TypeScript does not distinguish between getters and properties.
Shouldn’t they be treated differently? Getters have functional bodies. Return value of a getter cannot be predicted by looking at previous call on that getter. Isn’t that right?
🤷♂️ Changing it to not narrow will break the code of other people. While safer, it will undoubtly create more friction and troubles in the end. Getters are very likely to always return the same type (without any assignment in between) on subsequent calls, but having a getter randomly return different types is uncommon (and honestly quite smelly).
These days I am creating a programming language. That is where I caught this. I have added a minimal code from it to explain the use case. In the following code, I am using a getter that returns different values occasionally. It is very helpful.
exporttypeToken=|{type: "keyword";value: "let"|"const";}|{type: "string"|"declaration";value: string;}|{type: "operator";value: "="|"+";};exportdefaultclassPasser{privatepos=0;constructor(privatereadonlytokens: Token[]){}privategettoken(){returnthis.tokens[this.pos];}privatemove(){this.pos++;}/** * Pass the tokens. */publicpass(){while(this.pos<this.tokens.length){if(this.token.type==="keyword"){if(this.token.value==="let"){this.move();if(this.token.type==="declaration"){// error shows up here// getting variable namethis.move();if(this.token.type==="operator"){// and here herethis.move();// saving variable// goes like this...}}}}// again lots of conditional statements using this.token....this.move();}}}
If I did not have the getter, I would have to write this.tokens[this.pos].type or this.tokens[this.pos].value in lots of places. It can be tedious. right? 🙂
My idea is that, no matter what the getter will return a value of type Token. I can see that TS is saying the type of token in the third error is never. Which can never be true. It is always going to be a Token. Please correct me if I am wrong. I am trying to contribute. No offense 🙂
We fully understand that there is a trade-off involved here; TypeScript's own parser uses a function call token() to retrieve the local so it isn't narrowed.
TypeScript's own parser uses a function call token() to retrieve the local so it isn't narrowed.
I didn’t quite catch that. What “local” does the token function return.
If there is a trade off how about adding another option in tsconfig to disable conditional type checking on getters? So developers can turn it on off on-demand. I would love to cooperate on that feature.
option ... to disable conditional type checking on getters
This is not really feasible anyway, because TS doesn't generally distinguish between accessors and regular properties in type-land. { get foo() { return 42; } } is inferred as { readonly foo: number }, the information that foo is a getter is completely lost
I knew getters are treated the same way as read-only variables. I never questioned it because I hadn't come to this kind of scenario before.
Now I understand that TS treats them the same way because it is the typical use case.
Getters are very likely to always return the same type (without any assignment in between) on subsequent calls, but having a getter randomly return different types is uncommon (and honestly quite smelly).
But even TSC itself can be benefited if they are treated differently. Adding an option in tsconfig would be nice. Then it won't break the existing code and people like me and you who develop compilers/interpreters can code happily ever after 😇 But as you said,
We're not interested in doubling the configuration space of the checker at this time.
I hope this feature will be available in the future... And I love to contribute when you decide to implement it because I already contributed to it in the issues section 😁
Activity
MartinJohns commentedon Oct 18, 2023
Duplicate of #9998 / #46771.
TypeScript does not distinguish between getters and properties. They're both properties in the type system, so there's nothing to cause different behaviour.
LassazVegaz commentedon Oct 18, 2023
Shouldn’t they be treated differently? Getters have functional bodies. Return value of a getter cannot be predicted by looking at previous call on that getter. Isn’t that right?
MartinJohns commentedon Oct 18, 2023
🤷♂️ Changing it to not narrow will break the code of other people. While safer, it will undoubtly create more friction and troubles in the end. Getters are very likely to always return the same type (without any assignment in between) on subsequent calls, but having a getter randomly return different types is uncommon (and honestly quite smelly).
LassazVegaz commentedon Oct 19, 2023
These days I am creating a programming language. That is where I caught this. I have added a minimal code from it to explain the use case. In the following code, I am using a getter that returns different values occasionally. It is very helpful.
If I did not have the getter, I would have to write
this.tokens[this.pos].typeorthis.tokens[this.pos].valuein lots of places. It can be tedious. right? 🙂My idea is that, no matter what the getter will return a value of type
Token. I can see that TS is saying the type of token in the third error isnever. Which can never be true. It is always going to be aToken. Please correct me if I am wrong. I am trying to contribute. No offense 🙂RyanCavanaugh commentedon Oct 19, 2023
We fully understand that there is a trade-off involved here; TypeScript's own parser uses a function call
token()to retrieve the local so it isn't narrowed.LassazVegaz commentedon Oct 19, 2023
I didn’t quite catch that. What “local” does the token function return.
If there is a trade off how about adding another option in tsconfig to disable conditional type checking on getters? So developers can turn it on off on-demand. I would love to cooperate on that feature.
RyanCavanaugh commentedon Oct 19, 2023
Instead of
we do
which doesn't meaningfully impact performance (we've measured to a very high degree).
We're not interested in doubling the configuration space of the checker at this time.
fatcerberus commentedon Oct 20, 2023
Also
This is not really feasible anyway, because TS doesn't generally distinguish between accessors and regular properties in type-land.
{ get foo() { return 42; } }is inferred as{ readonly foo: number }, the information thatfoois a getter is completely lostLassazVegaz commentedon Oct 20, 2023
I knew getters are treated the same way as read-only variables. I never questioned it because I hadn't come to this kind of scenario before.
Now I understand that TS treats them the same way because it is the typical use case.
But even TSC itself can be benefited if they are treated differently. Adding an option in tsconfig would be nice. Then it won't break the existing code and people like me and you who develop compilers/interpreters can code happily ever after 😇 But as you said,
I hope this feature will be available in the future... And I love to contribute when you decide to implement it because I already contributed to it in the issues section 😁
typescript-bot commentedon Oct 23, 2023
This issue has been marked as "Duplicate" and has seen no recent activity. It has been automatically closed for house-keeping purposes.