orenelbaum / babel-plugin-solid-undestructure

A Babel plugin for SolidJS that allows you to destructure component props without losing reactivity.
96 stars 3 forks source link

possible to provide a compile-time alternative to `splitProps`? #6

Open Enteleform opened 2 years ago

Enteleform commented 2 years ago

Current Limitation

Using const to define a component prevents utilization of TypeScript's namespaces to encapsulate runtime entities, only types can be defined with the namespace.

import {component} from "babel-plugin-solid-undestructure"

export const Greeting = component(({name, greeting="Hello"}:Greeting.Props) => <h1>{greeting} {name}</h1>)

export namespace Greeting{
  export type Props = {
    name:      string
    greeting?: ("Hello" | "Howdy")
  }

  /* Error: Cannot redeclare block-scoped variable 'Greeting'.ts(2451) */
  //export function alert({name, greeting="Hello"}:Props){
  //  window.alert(`${greeting} ${name}`)
  //}
}

Proposed Solution

An undestructure compile-type function annotation that can be used within a function component would allow full utilization of TypeScript's namespaces.

This could also allow solid-undestructure's functionality to be utilized in non-component utility functions. I'm still not 100% clear on the use cases for this, but from @ryansolid's comment @ Discord, it seems that it could prevent reactivity from being broken in certain scenarios.

image

import {undestructure} from "babel-plugin-solid-undestructure"

export function Greeting(props:Greeting.Props){
  /* Preferable to `splitProps` in some cases,
  *  because it exposes variables directly into the scope
  *  rather than encapsulating them, and also doesn't require
  *  explicit definition of keys to be split. */
  const {name, greeting="Hello"} = undestructure(props)
  return <h1>{greeting} {name}</h1>
}

export namespace Greeting{
  export type Props = {
    name:      string
    greeting?: ("Hello" | "Howdy")
  }

  /* Works, no error. */
  export function alert({name, greeting="Hello"}:Props){
    window.alert(`${greeting} ${name}`)
  }
}
orenelbaum commented 2 years ago

Thank you for opening this issue. I was considering a CTF like you proposed for a while. It already exists in Solid Labels. This case however seems like it could be solved with a pragma annotation, which is another feature that is under consideration and probably the next feature that I'm going to implement.

// @component
export function Greeting({name, greeting="Hello"}: Greeting.Props) {
  return <h1>{greeting} {name}</h1>
}

export namespace Greeting{
  export type Props = {
    name:      string
    greeting?: ("Hello" | "Howdy")
  }

  /* Works, no error. */
  export function alert({name, greeting="Hello"}:Props){
    window.alert(`${greeting} ${name}`)
  }
}

It can maybe be solved with other features as well but like I said pragama is probably the next feature I was going to implement anyway.

As for the CTF, I don't want to call it undestructure yet because I'm not sure about the name, and either way I'm not even sure if I want to add it yet. I'll probably add it at some point if I already added other features I want or if anyone wants to make a pull request (the source code is not TS yet, but I think that it's relatively accessible). But I'm honestly not completely sure yet I even want it to be part of this project. I will probably prioritize it more if I hear about use cases and see that people want this feature. I think that stores could maybe be the main use case, not in order to not lose reactivity, but instead in order to destructure into the scope without subscribing immediately. But it seems a bit like the kind of thing that it's like why would you even bother at this point. The main reason I feel like it makes sense at the component level is because it's one place where it feels very natural in Solid, especially if you are used to it from React, and basically it's just this specific use case that will usually be pretty common throughout your app since you're using components and props everywhere, and it works very well with the Component type so you don't even have to change anything about your code usually.

Enteleform commented 2 years ago

Oh cool, wouldn't have thought to look for that sort of thing @ solid-labels.

I like the pragma implementation you're working on. What do you think of @undestructure? Or something similarly descriptive that doesn't directly reference component. Asking because this functionality could be used for non-component utility functions as well. Maybe it could be useful to have both @component & @undestructure, even if they're both aliases for the same thing?

orenelbaum commented 2 years ago

Yeah I'm still not 100% sure about the @component name. I think that I will probably go for either just @component or both @component and @destructure. Generally I prefer avoiding using the word "undestructure" too much because while I thought that it would be both descriptive and slightly entertaining as the name of the package, it is kind of a dumb word that can also be a bit confusing for people who are not familiar with it. The pragma is specific to components though, which is part of the reason I'm not sure about introducing @destructure as an alias. A general purpose function will have some differences.