Skip to content
This repository has been archived by the owner on Dec 13, 2023. It is now read-only.

Opt-in PropTypes implementation #24

Closed
AmaranthineCodices opened this issue Jan 29, 2018 · 6 comments · Fixed by #104
Closed

Opt-in PropTypes implementation #24

AmaranthineCodices opened this issue Jan 29, 2018 · 6 comments · Fixed by #104
Labels
feature: planned A feature that's been accepted into the project's roadmap.

Comments

@AmaranthineCodices
Copy link
Contributor

It would be nice to have a feature similar to React's PropTypes functionality.

Overview

Each stateful component may have a key defined on it - with a symbol, ideally, to avoid name collisions - with a table. This table is structured like this:

{
    PropertyKey = "Vector3",
    OtherProperty = "table",
    ThirdProperty = Roact.PropTypes.array,
    -- ...
}

When the stateful component's props table is changed and DEBUG_ENABLE has been called, Roact will check each key in the props table, match it to the corresponding value in the property types table, and error if the types differ.

The property types need not be limited to simple typeof(value) == expectedType expressions. Custom rules that provide more granular type checking allow for checking if, for example, a table is an array (e.g. ThirdProperty in the above example).

Type rules

A "type rule" is the value of an entry in the property types table. It can be one of three possible types:

  • A string denoting the expected type as returned by typeof.
  • An array denoting one or more possible types that the value may be.
  • A function that takes a single argument: the value to validate. This function must return true or false, with an optional second return value explaining why the value was rejected (unused if the function returns true).

Some built-in rules will be provided, which will usually be functions. Usually these functions will return the actual validators themselves, e.g. PropTypes.enumOf("Font") would return a validator function that returns true if and only if the value is an EnumItem from the Font Enum.

Holes

  • Functional components aren't supported. JS allows you to set arbitrary properties of functions; Lua doesn't, so the only way to support functional components would be to call some sort of manual API to validate the props table.
  • Kinda on the fence about whether this should even be a part of Roact or just a separate library. It would be nice to have Roact integrate with this regardless, though.
@LPGhatguy LPGhatguy added enhancement feature: planned A feature that's been accepted into the project's roadmap. and removed enhancement labels Jan 29, 2018
@LPGhatguy
Copy link
Contributor

Great feature. It can probably be prototyped as a library, maybe one that provides a decorator-like function? It could patch the component's init and willUpdate functions to introduce the checks!

Something like:

local MyComponent = Roact.Component:extend("MyComponent")

local propTypes = {
    foo = "Vector3",
    bar = PropTypes.oneOf("Vector3", "CFrame"),
}

-- implementation of MyComponent

-- We can patch the existing class object...
PropTypes.apply(MyComponent, propTypes)

-- ...or we can create a new component and avoid mutating this one!
MyComponent = PropTypes.apply(MyComponent, propTypes)

return MyComponent

Something I'd like to be able to express with types is "the set of all properties valid for this Roblox instance type." This lines up with a pattern I've noticed in Roact:

local defaultProps = {
    -- 'Legacy' is an awful default font!
    Font = Enum.Font.SourceSans,
    BorderSizePixel = 0,
    BackgroundColor3 = Color3.new(1, 1, 1),
}

local function TextLabel(props)
    return Roact.createElement("TextLabel", merge(defaultProps, props))
end

@AmaranthineCodices
Copy link
Contributor Author

Super prototype: https://github.com/AmaranthineCodices/rbx-prop-types

Docs here: https://amaranthinecodices.github.io/rbx-prop-types/

tl;dr example:

local someRules = {
    requiredString = PropTypes.string,
    optionalString = PropTypes.string.optional,
    shaped = PropTypes.shape {
        num = PropTypes.number,
        udim = PropTypes.UDim,
        sub = PropTypes.shape {
            a = PropTypes.string,
            b = PropTypes.boolean
        }
    }
}

local someData = {
    requiredString = "hello, world!",
    -- optionalString not specified - it's optional!
    -- this shouldn't be here and will cause validation to fail in strict mode!
    unknown = 1,

    shaped = {
        num = 1,
        udim = UDim.new(0, 1),
        sub = {
            a = "hi",
            b = 1,
        }
    },
}

-- you can use `assert` to throw errors when validation fails
assert(PropTypes.validate(someData, someRules))

I want to only do property checking when debug is enabled but I can't access that from outside Roact (#25), so for now it's just on every property change.

@LPGhatguy
Copy link
Contributor

This is still on my mind!

I like going the route of making things required by default, but I'm not sure the best technique to actually structure the PropsType library and where it should live. My gut says it should be part of the core Roact distribution, but I want to make sure we aren't making the same mistake that Facebook made (and later changed) with React.

@AmaranthineCodices
Copy link
Contributor Author

I've found a lot of uses for PropTypes outside of just Roact components, having had a month or so to use it. At this point I'm pretty sure that making it part of the core Roact library would be a mistake. Integration into Roact directly (specifying propTypes on a component, for example) is one thing, but I don't think that bundling it is a good idea.

@LPGhatguy LPGhatguy changed the title Debug-only type checking for props? Opt-in PropTypes implementation May 1, 2018
@LPGhatguy
Copy link
Contributor

I suspect that this will now be very trivial to effectively toggle with the new global configuration API!

@LPGhatguy
Copy link
Contributor

This is next on my plate after #79 merges.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
feature: planned A feature that's been accepted into the project's roadmap.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants