-
-
Notifications
You must be signed in to change notification settings - Fork 4.4k
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
Make TS Discriminating Unions Props Possible #9130
Comments
Please provide a proper reproduction. You code snippets are missing some code or contain bugs - for example, where is |
Updated with iconType exported |
Reproduction https://github.com/arkmech/svelte-discriminating-union |
This is something React has that Svelte does not |
Not sure if solves all the issues but this works. <script context="module" lang="ts">
type Props = {
name: string;
} & (
| { color?: 'red' }
| {
color: 'green';
uniqueProp?: string;
}
);
</script>
<script lang="ts">
type $$Props = Props;
const props = $$props as $$Props;
</script>
<div>{props.name}</div>
{#if props.color === 'red'}
<div>Red</div>
{:else if props.color === 'green'}
<div>Green</div>
<div>{props.uniqueProp}</div>
{/if} |
IMO it's already easy to use discriminated unions in Svelte, here's an example (imagine a blog admin page, needing to show the content for a URL, which could be a file download, an article page, or a redirect to another URL): <script lang="ts">
import type { Content } from '$lib/models'
import ContentDownload from './ContentDownload.svelte'
import ContentPage from './ContentPage.svelte'
import ContentRedirect from './ContentRedirect.svelte'
import ContentUnknown from './ContentUnknown.svelte'
export let content: Content
function component(content: Content) {
switch (content.type) {
case 'Page':
return ContentPage
case 'Redirect':
return ContentRedirect
case 'Download':
return ContentDownload
default:
return ContentUnknown
}
}
</script>
<svelte:component this={component(content)} {content} /> Each type-specific component gets the strongly typed content to do whatever it needs with it. |
Yes but what we mean is type discrimination on "top level", not "Make every prop and wrap it into an object prop, then type discriminate that" |
Yes that "works" but you get type linting when you actually use the component and put props on it on the parent component. |
Hmm, are you saying type linting doesn't work when using this component ?. You do get type errors when you use the component. Here is a REPL example, https://www.sveltelab.dev/0ds6ozd652e1o1e |
For what purpose? If you're using Typescript discriminated types in a project wouldn't it make more sense to ... well, use them? Why start splitting apart the props to pass them separately when you more than likely already have everything as an object anyway (the whole point of discriminated unions). Why wouldn't you pass that object? And if you "must" do it in a single component, you can add a little type casting to get the props: <script lang="ts" context="module">
type NetworkLoadingState = {
state: "loading";
};
type NetworkFailedState = {
state: "failed";
code: number;
};
type NetworkSuccessState = {
state: "success";
response: {
title: string;
duration: number;
summary: string;
};
};
// Create a type which represents only one of the above types
// but you aren't sure which it is yet.
type NetworkState =
| NetworkLoadingState
| NetworkFailedState
| NetworkSuccessState;
</script>
<script lang="ts">
export let value: NetworkState
const asLoading = (value: NetworkState) => value as NetworkLoadingState
const asFailed = (value: NetworkState) => value as NetworkFailedState
const asSuccess = (value: NetworkState) => value as NetworkSuccessState
</script>
{#if value.state === 'loading'}
{@const loading = asLoading(value)}
{loading.state}
{/if}
{#if value.state === 'failed'}
{@const failed = asFailed(value)}
{failed.state}
{failed.code}
{/if}
{#if value.state === 'success'}
{@const success = asSuccess(value)}
{success.state}
{success.response.title}
{success.response.duration}
{success.response.summary}
{/if} |
Using a more straight forward example, to make the issue clearer, and echoing other comments. If you need to type $$Props directly like this, so that the components props can vary, it is most probably a good idea to then just use $$props. You get full typescript safety from within and outside your component this way: <script lang="ts">
type Circle = { shape: 'circle'; radius: number };
type Rectangle = { shape: 'rectangle'; width: number; height: number };
type $$Props = Circle | Rectangle;
$:props = $$props as $$Props;
</script>
{#if props.shape==="circle"}
<div>Circle, radius = {props.radius}</div>
{:else}
<div>Rectangle, width = {props.width}, height = {props.height}</div>
{/if} The only issue, is that it would be better ergonomics if $$props was already typed to $$Props, so that you could simply use $$props directly, and avoid having to write the props^3 line: $:props = $$props as $$Props; |
Thank you everyone that has responded so far. External Component API works When accessing properties on |
Also note that, for the intellisense to work, the |
Svelte 5 will fix this through the |
Describe the problem
It doesn't seem possible to access unique props from discriminated unions
Describe the proposed solution
Attempted to access a unique prop.
Alternatives considered
None
Importance
i cannot use svelte without it
The text was updated successfully, but these errors were encountered: