Skip to content

@property option for runtime schema validation #5102

@gordonbrander

Description

@gordonbrander

Should this be an RFC?

  • This is not a substantial change

Which package is this a feature request for?

Lit Core (lit / lit-html / lit-element / reactive-element)

Description

It would be useful to have a concise way to do runtime validation of property values before they are set.

Strawperson:

@property({
  parse: (value: unknown) => {
      // ... do some schema validation
      return value
  }
});

I find myself wanting something like this for properties that hold complex types (e.g. component models), particularly since html tagged templates are unable to infer required TypeScript types for properties. IIRC there is prior art for validation with some built-in elements, which will ignore sets for values that are of the wrong type.

Validation should of course be optional, and the default behavior should be pass-through, as before.

Possible approaches I can think of:

A parse function that returns value | undefined:

type Parse<T> = (value: unknown) => T | undefined;

// Usage:
// @property({ parse })

A parse function that throws:

type Parse<T> = (value: unknown) => asserts value is T;

// Usage:
// @property({ parse })

Either parse approach integrates fairly easily with validation libraries like Zod, and allows for coercion. Alternatively, a predicate?

A validation function that returns a boolean:

type Validate<T> = (value: unknown) => value is T;

// Usage:
// @property({ validate })

Alternatives and Workarounds

You can manually write the accessors, but it requires some additional ceremony.

#count = 0;

@property()
set count(value: number) {
  if (typeof value !== "number") {
    throw new TypeError("count must be a number");
  }
  this.#count = value;
}
get count() {
  return this.#count;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions