Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[Proposal]  $Props  —  Define props with classes#16228

Refzlund started this conversation inIdeas
Discussion options

Class props

The ability to re-use, type and extend component props — all in one go.

Introduction

Imagine you have aBase.svelte component

<scriptlang='ts'module>exportinterfaceBaseProps {        value:unknownreadonly required:false    }</script><scriptlang='ts'>let {        value=bindable(),        required=false,        ...    }:BaseProps=$props()</script>...

And we make use of that for a bunch of other components such asDerived.svelte

<scriptlang='ts'module>import {typeBaseProps,defaultasBase }from'./Base.svelte'interfaceDerivedPropsextendsBaseProps {... }</script><scriptlang='ts'>let {        value=bindable(),        required=false,        ...,...rest    }:DerivedProps&HTMLDivElementProps=$props()$effect(()=> {value... })</script><Basebind:value,    {required}>    <div {...rest}>        ...

Two things are happening here:

  1. We need to continouslybind:value
  2. We need to specify what properties are forwarded to<Base>

Another downside, is that if we want to "expose" these props via ex.setContext, oradd them to a class that manages some logic we'd have to provide a new object;

<scriptlang='ts'>import {LogicHandler }from'./logic-handler.svelte.ts'const {        value=$bindable(),        required=false,        ...    }:BaseProps=$props()const context= {get value() {returnvalue },set value(v) {value=v },get required() {returnrequired },...    }setContext('Base',context)</script>

And so we repeat ourselves A LOT.


Proposal

I propose that we establish a$Props /SvelteProps class that can be extended to define a$Prop-interface.

<scriptlang='ts'module>classBaseProps<T>extends$Props {        value:T// getter & setterreadonly required:boolean=false// getter    }</script>

Tip

Alternative syntax

<scriptlang='ts'module>classBaseProps<T>extendsSvelteProps {        value:T=$bindable()readonly required:boolean=false    }</script>

We'd use this as normal, but having props already be defined as readonly or $bindable.

<scriptlang='ts'>typeT=$$Genericconst {        value,        required    }:BaseProps<T>=$props(BaseProps)</script>

The idea, however. Is that$props(...) now returns an instance ofBaseProps, which we can reference. This is where the power comes in:

<scriptlang='ts'>import {LogicHandler }from'./logic-handler.svelte.ts'typeT=$$Genericconst props:BaseProps<T>=$props(BaseProps)const handler=newLogicHandler(props)$effect(()=> {handler.reactiveProperty })setContext('Base',base)</script>

A MAJOR shortcut!

That's not all. Let's extend this badboy

<scriptlang='ts'module>import {BaseProps,defaultasBase }from'./Base.svelte'classDerivedPropsextendsBaseProps {readonly name:string...    }</script>

We're simply extending the BaseProps, and we include$binding()'s just like that. We can then use a$Props static function to separate theBaseProps from everything else.

<scriptlang='ts'>const props:DerivedProps&HTMLDivElementProps=$props(DerivedProps)const baseProps=BaseProps.derived(props)/*        equivalent to @example        const baseProps = {            get value() { return props.value },            set value(v) { props.value = v },            get required() { return props.required }        }*/basePropsinstanceofBaseProps// true</script><Base {...baseProps}>    ...

By using additional utility functions target on which element@attach goes on (via.$attachments), and potentially keys not specified by the child classes of$Props.

<scriptlang='ts'>const props:DerivedProps&HTMLInputElementProps=$props(DerivedProps)const baseProps=BaseProps.derived(props)const inputProps=DerivedProps.rest(props)// extracts keys not defined within the classes</script><div {...props.$attachments}>    <Base {...baseProps}>        <inputbind:value={props.value} {...inputProps} />    </Base></div>

We now have an impactful, systematically structured, reactive system relating to the props, that we can pass on to various logical classes.

It's easy to read, and in my experience levels up the props-game.


Further

Can we go further — should be go further?

As an additional sparkling idea, we could via the$Props handle the incoming and outgoing dataflow in the same mannerism, asbind:value={() => getter, (v) => setter}.

<scriptlang='ts'module>classPropsextends$Props {// We can a `private` or `protected` property, that acts just like any other Component prop (as discussed so far)        #creationDate:Date// We can then define custom getters and setters/**            Equivalent to@example$derived(this.#creationDate == undefined || this.#creationDate instanceof Date ? this.#creationDate : new Date(this.#creationDate.toString()))*/get creationDate() {returnthis.#creationDate==undefined||this.#creationDateinstanceofDate?this.#creationDate:newDate(this.#creationDate.toString())        }/**            Equivalent to@examplebind:value={...,(value) => value == undefined || value instanceof Date ? value : new Date(value.toString())}*/set creationDate(value) {this.#creationDate=value==undefined||valueinstanceofDate?value:newDate(value.toString())        }    }</script>

This is last part for custom getters and setters is not core of this proposal. It's an additional way this methodology could benefit the developer with more control.

You must be logged in to vote

Replies: 0 comments

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Category
Ideas
Labels
None yet
1 participant
@Refzlund

[8]ページ先頭

©2009-2025 Movatter.jp