Language-tools: Typescript control flow inside if/{#if} not working when using $store

Created on 27 Aug 2020  ·  15Comments  ·  Source: sveltejs/language-tools

When you have a store defined as follows:

import { readable, Readable } from "svelte/store"

const post: Readable<Post|null> = readable(null, /* some code that loads the post asynchronously */)

And then in your template you write:

<h1>{$post.title}</h1>

Then the typescript checker rightly complains Object is possibly 'null'. Then I was hoping that I could make that error disappear by doing this:

{#if $post !== null}
    <h1>{$post.title}</h1>
{/if}

But that has no effect whatsoever. Which kind of makes sense. I understand that typescript may not understand that that is an if-statement.

I can imagine that this would actually not be trivial to implement, but I don't know, it would be really nice.

Fixed bug

Most helpful comment

In the meantime, Reassigning to a local variable can be a workaround:

$: storeValue = $store

All 15 comments

The problem is that

{#if $post !== null}
    <h1>{$post.title}</h1>
{/if}

gets (by us, through svelte2tsx, for all the intellisense goodness) translated to

if (__sveltets_store_get(post) !== null){<>
    <h1>{__sveltets_store_get(post).title}</h1>
</>}

As you can see the store access gets translated to a __sveltets_store_get function, and TS's control flow therefore cannot determine this is not undefined/null. To fix this, we would need to translate this in a way that the first __sveltets_store_get is assigned to a variable, which is then used within the if-scope.

Okay, so this sounds doable? 😇

I think so

Just a comment to say I ran into the same issue when trying to add Typescript support to the standard Sapper template:

From my post on the Discord #sapper channel:

I have a question:

Does Svelte Typescript support extend to HTML blocks like {#each cat as cats}?

The screenshot is from svelte-check after I turned on Typescript for the standard Sapper template.

Screen Shot 2020-09-03 at 9 03 34

Here is the relevant code-snippet from src/routes/blog/index.svelte:

<ul>
    {#each posts as post}
        <!-- we're using the non-standard `rel=prefetch` attribute to
                tell Sapper to load the data for the page as soon as
                the user hovers over the link or taps it, instead of
                waiting for the 'click' event -->
        <li><a rel='prefetch' href='blog/{post.slug}'>{post.title}</a></li>
    {/each}
</ul>

And the associated errors from svelte-check (even with strict: false in tsconfig.json):

/Users/paul/dev/solidrevolution_svelte-sapper/src/routes/blog/index.svelte:32:42
Error: Property 'slug' does not exist on type 'unknown'. (ts)
                waiting for the 'click' event -->
        <li><a rel='prefetch' href='blog/{post.slug}'>{post.title}</a></li>
    {/each}


/Users/paul/dev/solidrevolution_svelte-sapper/src/routes/blog/index.svelte:32:55
Error: Property 'title' does not exist on type 'unknown'. (ts)
                waiting for the 'click' event -->
        <li><a rel='prefetch' href='blog/{post.slug}'>{post.title}</a></li>
    {/each}

Can't tell what the problem is when I don't know the type of posts. But you probably need to do some type assertion to the posts variable in the script tag so that the post is not unknown.

As long as the list in the each block implement ArrayLike interface it should be able to infer element type from it.

In the meantime, Reassigning to a local variable can be a workaround:

$: storeValue = $store

Things to keep in mind when implementing this by extracting the store value into a variable:

  • Renaming the store variable should still work afterwards
  • "$x is not a store" type error should still work afterwards

I'm experiencing the same issue, but without store.

Simple test case:

<script lang="typescript">
    interface TestObject {
        author?: string;
    }
    export let test: TestObject | null;
</script>

{#if test}
    <div>
        {#if test.author}
            <div>Author: {test.author}</div>
        {/if}
    </div>
{/if}

On line {#if test.author} it shows error "Object is possibly null".

Is it the same issue or a different bug?

In strict mode this is correct behavior, you typed test as possibly being null, so the error is correct. You need to check for test for being defined first.

In strict mode this is correct behavior, you typed test as possibly begin null, so the error is correct. You need to check for test for being defined first.

There is {#if test} check before that.

Oops, did answer too quickly. Yes this is a different issue, could you open another ticket for that?

Of course. Just wanted to make sure its not a duplicate before opening it :)

Hello everyone! Any updates on this one?

This should be fixed with VS Code Svelte extension version 104.4.4 and svelte-check version 1.1.36. Note that for svelte-check you also need TypeScript 4.2 or later.

Please let me know if it's working as expected now. There are still control flow issues unrelated to store usage, these are tracked in #619 .

This should be fixed with VS Code Svelte extension version 104.4.4 and svelte-check version 1.1.36. Note that for svelte-check you also need TypeScript 4.2 or later.

Please let me know if it's working as expected now. There are still control flow issues unrelated to store usage, these are tracked in #619 .

It's working for me! I can remove my re-assigment hack. Thanks a lot.

Was this page helpful?
0 / 5 - 0 ratings