Many times in my components/pages I need to use this pattern:
{#if player.name}
{player.name}
{:else}
None
{/if}
So I created a new component called MaybeNone
:
<script lang="ts">
export let condition = false;
</script>
{#if !condition}
None
{:else}
<slot />
{/if}
to use like this:
<MaybeNone condition={!!player.name}>
Name is: {player.name}
</MaybeNone>
but sometimes I need to use methods like:
{#if player.name}
{calculate_player_name(player)}
{:else}
None
{/if}
with:
export const calculate_player_name = (
player: Pick<Player, 'name' | 'nickname'>
): string => {
// calculate result...
return result;
};
player?: {
name?: string | null;
nickname?: string | null;
team?: string | null;
id: string;
createdAt: Date;
} | null;
}>;
And now the usage of the MaybeNone
component is not good:
<MaybeNone condition={!!player.name}>
<!-- There is an error here because I need to check player.name before calling `calculate_player_name()` obviously -->
{calculate_player_name(player)}
</MaybeNone>
and I'm forced to use instead:
<MaybeNone condition={!!player.name}>
{#if player.name}
{calculate_player_name(player)}
{/if}
</MaybeNone>
because the Typescript error is of course:
Type 'undefined' is not assignable to type 'Pick<Player, "name" | "nickname">'.ts(2345)
which makes the usage of MaybeNone
useless or not so good as I thought.
Can you suggest an alternative? A fix?
Thanks for your amazing help!
CodePudding user response:
Would approach this via a generic component that has slot properties, something like this:
<script lang="ts">
type T = $$Generic;
export let maybe: T | null | undefined;
// Workaround for type not being narrowed correctly in parent
$: notNull = maybe as T;
</script>
{#if maybe}
<slot value={notNull} />
{:else}
None
{/if}
Which then can be used along the lines of:
<MaybeNone maybe={player} let:value>
{calculate_player_name(value)}
</MaybeNone>
This should work according to the types of the function, but they probably are inaccurate if you need to check that name
is defined as well. Making sure that this is the case would be a bit more verbose:
<MaybeNone maybe={player?.name == null ? null : player} let:value>
{calculate_player_name(value)}
</MaybeNone>
(If player
itself can be undefined
/null
, there really should be a ?.
on property access. TS's strict null checks will cause errors otherwise.)