language-tools
language-tools copied to clipboard
typescript doesn't work on svelte reactive variables
Describe the bug
When using a reactive value to validate a condition on another variable (e.g. an isLoaded variable) typescript displays an error.
This is very annoying on typescript projects.
Reproduction
<script lang="ts">
let foo: string | undefined = undefined
function someFunction(param: string) {
return param
}
$: bar = foo
</script>
{#if bar}
if `bar` is truthy then foo must be of type `string`...
{someFunction(foo)}
Argument of type 'string | undefined' is not assignable to parameter of type
'string'. Type 'undefined' is not assignable to type 'string'.ts(2345)
{/if}
Logs
No response
System Info
Linux + VSCode
Severity
annoyance
when combined with #7754, it gets really annoying since it removes an easy workaround
The example you have is similar to this in plain typescript:
let foo: string | undefined = undefined
function someFunction(param: string) {
return param
}
let bar = foo;
() => {
if (bar) {
someFunction(foo);
}
}
As you can see in the typescript playground. This also has the same error.
In most cases, Typescript's type guard only narrows down the variable you check, not the one you have assigned with the same value. What's your use case for this? Do you have a more complex case where you can't just test the variable you want to use?
If I make a reactive variable $: isLoaded = var1 & var2 & var3 and then wrap the template with {#if isLoaded}``{/if}, I'll get undefined errors everytime I'll try to use var1, var2 or var3 inside the conditional statement.
And since it's not even possible to use the ! inside the template to tell typescript that it is defined it gets very annoying and the only solution is to prepare all the variables inside the <script> tags even when it makes no sense to do so.
EDIT:
About your example, you are right TS does throw an error there, however in your example bar is a variable that could be changed at runtime to equal something else than foo.
In svelte reactive variables (as far as I know) are always equal to the same thing, when you set $: isLoaded = var1 & var2 & var3 you can't say that it is now equal to var4.
That's why even it may work the same under the hood, I do think they are syntactically different.
And not being able to workaround this by using ! is really painful.
In svelte reactive variables (as far as I know) are always equal to the same thing, when you set $: isLoaded = var1 & var2 & var3 you can't say that it is now equal to var4.
Yes, you can do that. It just makes no sense to do it in most cases.
Indeed this kind of error is not going to happen in the runtime. But it is more complex for the type checker to check if it's possible. This is also true in TypeScript itself. The control flow analysis has its limitations.
Anyway, I not trying to say it's wrong to make it work. It would be great if it just work. But I can't think of a way to make it work. If you can think of a semantically similar TypeScript code we can see if it could work. Otherwise, I don't think this will be changed anytime soon.
In the meanwhile, You can workaround it with
- an extra check,
{#if isLoaded}
{foo && someFunction(foo)}
{/if}
Inline the expression.
{#if var1 && var2 && var3}
{someFunction(var2)}
{/if}
You can also create a type predict function to do a more complex check. Or a helper function to do the type assert, non-null assert outside of the markup
function nonNullAssert<T>(a: T | undefined | null): T {
return a!;
}