-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Type narrowing with assertions containing truthy literals #41503
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
I'm still not quite understanding why you would write |
It's definitely kind of strange. The goal, like this comment, is to have assertions and other code that can be stripped out in a production build. Here's a contrived example: let list: number[] | undefined;
export function setup() {
list = [];
}
export function append(value: number) {
if ("DEBUG_TOKEN" && !list) {
throw new Error("You must call `setup` before appending values");
}
list.push(value);
}
export function cleanup() {
list = undefined;
} Then, for a production build, you replace export function append(value) {
list.push(value);
} I find myself using this pattern in many cases rather than the non-null assertion operator so that it's explicit why I expect the value to be non-null, and if for some reason it is null, you get a nice explanative error in development environments. Besides non-null, we use this pattern for some assertion functions as well. One of them requires a recursive tree walk in a fairly hot codepath. It's nice that we can provide these errors during development, but not pay the cost of the check when running in production. It would be really nice if we didn't have to sprinkle if ("DEBUG_TOKEN" && !list) {
throw new Error("You must call `setup` before appending values");
}
list = list!; or if ("DEBUG_TOKEN" && !someExpensiveAssertion(value)) {
throw new Error("A helpful error");
}
value = value as TypeThatTheAssertionSaidItWas; Especially because tools like |
I don't know why, but putting the literal first makes way more sense to me 😅 |
I just came across something similar, but not sure whether it's the same use case: function a ( name ?: string )
{
let theName = name ?? '' !== '' ? name : 'foo' ;
} In this scenario I would have expected that typescript would have limited The I have simplified this scenario, but a temporary work around is: let theName = name ?? '' !== '' ? name ?? '' : 'foo' ; but I really shouldn't have to add the extra |
@houghtonap narrowing can't perform that kind of higher-order reasoning It seems like the easier form of that is let theName = name?.length ? name : 'foo'; Here, TS knows that only a not- |
Wait, this is just let theName = name || "foo"; since |
TypeScript Version: 4.2.0-dev.20201109
Search Terms:
type narrowing truthy
type narrowing assertion
conditional throw
Code
Expected behavior:
In all three functions typescript would know
foo
is a number after the assertion.Actual behavior:
In the last example,
testWithTruthyLiteral
, typescript complainsfoo
may beundefined
. This is the same behavior for all known truthy values except for literaltrue
.Playground Link:
https://www.typescriptlang.org/play?ts=4.2.0-dev.20201109#code/GYVwdgxgLglg9mABFApgZygRgBTDnAfgC5EwQBbAIxQCcBKRAbwChFEZhFsBCPOBqAAsacAO6kU4gKI0RNbHQDczVohoooIGkj4A6KHABiMAB4oAJguUBfFaEiwEydFABMufMVIVq9Jqo4uXnxEADJQ5BoQFAFhMQlpWTh5JRU2dU1tRD0DYzNLVNtme2h4JFQMAGYPQhIyKloGFjZAnj4wiIAiDBpO2JFxMElEGTkrNLUNLR18fSNTC3GiksdylwAWGq963yaAzjaQ8MRMfvihxLHU1Qzp7NnchYKbO3BSpwqoAFYtup9G-wtA7BOAdJjWM6DYajZLjG5TLI5eb5JbMIA
Related Issues:
#29323
It sounds like from #29323 (comment) that determining truthiness of expressions could lead to circular dependencies between compiler passes, but that literal
true
has been special cased. This should be able to be extended to truthy literals without the circular dependency concern.And for context, like others that have brought up similar issues, this stems from wanting a truthy token that can be used for a minification hook without affecting the types or code execution.
The text was updated successfully, but these errors were encountered: