language-tools
language-tools copied to clipboard
TypeScript incorrectly considers variable as possibly null inside condition with array index access
Describe the bug
TypeScript error "Object is possibly 'undefined' ts(2532)" is shown for nested conditional checks. This is a special case of fixed #619
Reproduction
<script lang="ts">
export let content: {
list: {
image?: {
src: string;
alt: string;
};
}[];
};
export let item = 0;
</script>
<div>
{#if content.list[item]?.image}
<img
src={content.list[item].image.src}
alt={content.list[item].image.alt}
/>
{/if}
</div>
Expected behaviour
No error should be thrown
System Info
Dependencies versions:
"svelte": "^3.48.0", "svelte-check": "^2.7.2", "svelte-preprocess": "^4.10.7", "tslib": "^2.4.0", "typescript": "^4.7.4"
and
Svelte for VS Code v105.18.1
Which package is the issue about?
svelte-check
Additional Information, eg. Screenshots
No response
This is a TypeScript limitation due to the array index access, so there's nothing we can do about it. You get the same error in a regular TS file:
let content: {
list: {
image?: {
src: string;
alt: string;
};
}[];
} = null as any;
let item = 0;
if (content.list[item]?.image) {
content.list[item].image.src; // <- error
}
Hi @dummdidumm, thanks for your answer
The difference is that in TypeScript I can mark them as defined ;)
if (content.list[item]?.image) {
content.list[item]!.image!.src; // <- no error
}
But this is not possible in the markup. I have used another workaround where I store the indexed access in a variable in the code and check that variable instead of the array in the markup 👍
Having a similar problem:
<script lang="ts">
import List from '$components/forms/List.svelte'
type Item = { key: string; value: string }
export let items: Item[]
</script>
<List bind:items let:index>
{#if items[index]} <!-- this check has no effect on the type -->
<!-- Error: Object is possibly 'undefined'. -->
<input bind:value={items[index].key} />
{/if}
</List>
I would expect that:
- the
if
check would work like it does in regularTypeScript
codeitems[12].key // Error if (items[12]) { items[12].key // no Error }
- we can use the
!
operator inside the markup to tell the compiler "I know what I do"items[12]!.key // no Error in TypeScript
<input bind:value={items[index]!.key} /> <!-- Error: Expected }-->
Becasue of the two-way-binding in a loop I can't really store the value in a variable and mark it as defined inside the script tag.
Current workaround:
Instead of marking the items as an array, make them a dynamic tuple with at least a single fixed item.
Then I can cast the index to the fixed value 0
and I can get rid of the error.
<script lang="ts">
import List from '$components/forms/List.svelte'
type Item = { key: string; value: string }
export let items: [Item, ...Item[]] // `tuple` instead of `array`
const typedAs0 = (value: number) => value as 0 // cast to fixed value `0`
</script>
<List bind:items let:index>
{#if items[index]}
<input bind:value={items[typedAs0(index)].key} />
{/if}
</List>