solidjs / solid

A declarative, efficient, and flexible JavaScript library for building user interfaces.
https://solidjs.com
MIT License
32.05k stars 914 forks source link

Excluding undefined in <Show>'s `when` doesn't result in its child knowing that the variable won't be undefined #1055

Closed ppuzio closed 2 years ago

ppuzio commented 2 years ago

Describe the bug

Hi, I've checked that this apparently hasn't been reported before. In the documentation it was said that <Show> is supposed to be the better way for templating conditional expressions, but I stumbled upon some unwanted behaviour.

Let's say I have some data that could be of type T | undefined. Normally, I'd write an expression like {data !== undefined && <p>{data}</p>}, but since the docs advised me to go with

<Show when={data !== undefined}>
  <p>{data}</p>
</Show>

I decided to follow that approach. Unfortunately it seems, that {data} in the second case's child is still of type T | undefined. Here's a codesandbox example, choose the App.tsx file and hover over {data} in both cases to see what I mean.

My question is - do you think it even is possibilie to e.g. allow when to accept an argument like

{
  data: T | K,
  compared: K,
  operation: "equal" | "notEqual"
}

that would set the child type accordingly (so for example

{
  data: Math.random() > 0.5 ? "test" : undefined,
  compared: undefined,
  operation: "equal"
}

would result in data having type "test" and

{
  data: Math.random() > 0.5 ? "test" : undefined,
  compared: undefined,
  operation: "notEqual"
}

would result in data having type undefined? Right now sticking with <Show> in this case would mean I'd have to either use a non-null assertion or an unnecessary undefined check.

I realise that this might be difficult since accepts a children Element, if it's not possible, maybe it would be a good idea to mention in the documents?

Your Example Website or App

https://codesandbox.io/s/show-not-working-like-and-7c5xqn?file=/src/App.tsx:213-317

Steps to Reproduce the Bug or Issue

  1. Go to the Sandbox example
  2. Hover over {data} in line 9 and check the type (will say "test")
  3. Hover over {data} in line 11 and check the type (will say "test" | undefined)

Expected behavior

I think that the child's {data} type should either be "test" or it would be worth mentioning in the docs that when doesn't grant the type exclusion that ternary or binary operator would.

Screenshots or Videos

No response

Platform

Additional context

No response

edemaine commented 2 years ago

This is discussed in the TypeScript guide. No, it's not possible, because TypeScript doesn't understand that the children render only when the Show argument is true. (I believe it's also possible for this to be violated briefly in some obscure circumstances...) If you use the "keyed" (callback) form of Show, it can and does work, but this is usually not a good idea because it removes fine-grained reactivity. See the discussion in the guide (and let me know if you think it's unclear in any way).

FaberVitale commented 2 years ago

@ppuzio how about this instead

<Show when={data} fallback={<div>data is falsy</div>}>
  {(data) => <p>{data} is truthy</p>}
</Show>
The Show control flow is used to conditional render part of the view: 
it renders children when the when is truthy,

https://www.solidjs.com/docs/latest/api#%3Cshow%3E

ppuzio commented 2 years ago

@edemaine didn't see this part of documentation, I guess it explains it all. Maybe it would make sense to link it in the <Show> section 🤔. Thanks anyway!