haunted icon indicating copy to clipboard operation
haunted copied to clipboard

Type coercion and other property goodies

Open jpray opened this issue 6 years ago • 11 comments

LitElement has a lot of flexibility in how properties are handled: https://lit-element.polymer-project.org/guide/properties

The following options are available:

  • converter: Convert between properties and attributes.
  • type: Use LitElement’s default attribute converter.
  • attribute: Configure observed attributes.
  • reflect: Configure reflected attributes.
  • noAccessor: Whether to set up a default property accessor.
  • hasChanged: Specify what constitutes a property change.

The only 2 features that I'm missing when using haunted are

  • "type" to coerce attributes (String) to String, Number, Boolean, Array, and Object properties
  • "reflect" to specify whether to reflect property updates back to the attribute

Of those 2, "type" is the most common need for me. I also think handling of default values for properties/attributes might be a bit awkward currently.

What might a good approach be for accomplishing some of the above in haunted?

For reference, here's a simple use case, maintaining state in a property instead of in useState(): https://codesandbox.io/s/z275vomrqp

jpray avatar Mar 06 '19 12:03 jpray

Pinning this one so others see it. I have some thoughts on this but will be on vacation for the rest of the week. Plan to come back to it after that.

matthewp avatar Mar 06 '19 13:03 matthewp

A little preview: I've been thinking something like React's propTypes might be appropriate for us. In fact, might prop-types even work? I have never tried using it in a non-React setting.

Reflecting I agree is a need, we can look into how to do that.

matthewp avatar Mar 06 '19 13:03 matthewp

Using Reacts prop-types might be hard as they only offer validation. They are functions that doesn't reflect the information that you enter into them. You can't even call them directly:

Invariant Violation: Calling PropTypes validators directly is not supported by the `prop-types` package. Use `PropTypes.checkPropTypes()` to call them. Read more at http://fb.me/use-check-prop-types

Another issue is that I don't think you want the same composability and and flexibility for what will essentially have to be serializable to a string.

So I think it makes sense to provide a separate implementation.

Maybe something like this:

import { html } from 'https://unpkg.com/lit-html/lit-html.js'

import { 
  component, 
  stringProperty,
  integerProperty,
  enumProperty,
  objectProperty
} from "https://unpkg.com/[email protected]/haunted.js";

const User = ({ name, age, type, meta }) => html`
  <div data-version=${meta.version} class="user ${type.toLowerCase()}">${name} ${age}</div>
`;

User.attributes = {
  name: stringProperty.isRequired,
  age: integerProperty(0, 120),
  type: enumProperty('ADMIN', 'END_USER')
}

User.properties = {
  meta: objectProperty({
    version: stringProperty.isRequired
  }).isRequired
}

customElements.define("x-user", component(User));

Here the attributes only allow property descriptors that is string serializable.

It would be really great if you can iterate the properties and actually extract the information, that would allow creating property-based tests generating valid attributes and properties.

sunesimonsen avatar Mar 20 '19 21:03 sunesimonsen

That not bad. Fwiw I'd like to make this a separate module to prevent extra bundle size for people not using it.

matthewp avatar Mar 21 '19 00:03 matthewp

The thing that gives me pause is that it's unfortunate for every view library to have its own separate typed property support. Couldn't this be done as a class mixin that any WC library could use?

matthewp avatar Mar 21 '19 00:03 matthewp

Just to revisit this brief, is the assumption that a defined property will be coerced to a type? For example if you define a number property and "11" is provided, this will be coerced to 11? If so, you could argue that it's better to throw. I'm not sure if this is how React behaves or not.

matthewp avatar Jul 12 '19 11:07 matthewp

@matthewp React would issue a console.error if you violate the property type. I think it is a bit dangerous to throw, in React that would break the entire rendering tree in the current error boundary. But web-components might behave differently.

Throwing in development would definitely be a good idea though.

sunesimonsen avatar Jul 12 '19 19:07 sunesimonsen

I thought type coercion was just for attributes -> properties. Not for properties themselves...

jpray avatar Jul 12 '19 20:07 jpray

It could be for both, that's up to us to decide.

matthewp avatar Jul 12 '19 20:07 matthewp

I kind of agree with @jpray on

I thought type coercion was just for attributes -> properties. Not for properties themselves...

I don't like the idea of type coercion, but it is a necessary evil for attributes as they can only be entered as strings.

sunesimonsen avatar Jul 14 '19 16:07 sunesimonsen

It might be good if haunted has built-in type converter!

In my case, I always do some type convert for props like Number() / JSON.parse() .

If there is a interface like below without breaking original usage:

customElements.define('hello-app', component(App, {observedAttributes: [{
  name: 'count',
  type: Number
}]}));

and In the App:

function App(count){
  console.log(typeof count); // number
}

Here is the lit-element's approach, they just do some type convertion before update the component.

I thought we can also do some interface preprocessing before observedAttributes and pre-convert attributeChangedCallback.

realdennis avatar Dec 08 '19 17:12 realdennis