Skip to content

[feat] Props data checker/converter #13124

@adiguba

Description

@adiguba

Describe the problem

Sometimes it can be useful to validate or convert the value of a prop before using it.

Some examples :

  • convert a string to another type (eg: number).
  • replace incorrect values ​​with a default value.
  • respect other criteria such as a min/max.
  • I want to use a different type internally an externally (ex date string format and Date object)

And of course I would like to keep this responsive and recheck the prop every time it is changed from outside


Currently I see two way to do this.


  1. If the prop is read-only, I can use a $derived() state :
<script>
	let {
		// the prop "value" is renamed "propValue" inside the component
		value : propValue = 0
	} = $props();

	// the "propValue" is converted to Number, into a "value" state
	const value = $derived(Number(propValue ));
</script>

This is fine, even if it involves duplicating the props.


  1. If the prop is modified inside the component, I have to use a $effect.pre() to validate the value on any update :
	let {
		value = 0
	} = $props();


	$effect.pre( () => {
		const value_as_number = Number(value);
		if (value_as_number !== value) {
			value = value_as_number;
		}
	});
	
</script>

This solution is more verbose, and the effect is re-executed even when I change the value inside my component (useless).

Describe the proposed solution

I think it would be nice to have a way to replace the prop's default value with a custom function.
Something like this (using a $pipe rune for the exemple - but the name should be reviewed) :

<script>
	let {
		// when the prop "value" is updated by the caller,
		// it will be converted to number
		value = $pipe( Number )
	} = $props();
</script>

The $pipe() rune requires a function as a parameter, which will be used to convert the value of the prop.
There is no default value, it is up to the function to handle the undefined case...

Of course, we should have the same with $bindable(), something like this :

<script>
	let {
		// when the prop "value" is updated by the caller,
		// it will be converted to number
		// (and binded to the caller with his new value)
		value = $bindable.pipe( Number )
	} = $props();
</script>

$bindable.pipe() should accept a second function, allowing the reverse conversion to be done when the value is passed back to the caller

<script>
	let {
		// when the prop "date" is updated by the caller, 
		// it will be converted to a Date object
		// And it will be binded to the caller as an ISO string.
		date = $bindable.pipe(
			// Date is converted from string to Date
			(d)=>new Date(Date.parse(d)),
			// Date is binded to caller as string
			(d)=>d.toISOString()
		)
	} = $props();
</script>

Caveat :

I don't known how to handle this with TypeScript, as the prop should have two distinct type.
From the caller perspective it should be a string, but in the component it should be a Date

It would be nice to have a simple way to declare this, like something like that :

type Props = {
  date: PROP<string, Date>
};

But I don't know if it's easily achievable...

Importance

nice to have

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions