As an application becomes complex, it may be quite unsafe to define props in an informal way. This leads to two issues:
- hard to tell how a component should be used, by looking at its code.
- unsafe, it is easy to send wrong props into a component, either by refactoring a component, or one of its parents.
A props type system solves both issues, by describing the types and shapes of the props. Here is how it works in Owl:
props
key is a static key (so, different fromthis.props
in a component instance)- it is optional: it is ok for a component to not define a
props
key. - props are validated whenever a component is created/updated
- props are only validated in
dev
mode (see config page) - if a key does not match the description, an error is thrown
- it validates keys defined in (static)
props
. Additional keys given by the parent will cause an error.
For example:
class ComponentA extends owl.Component {
static props = ['id', 'url'];
...
}
class ComponentB extends owl.Component {
static props = {
count: {type: Number},
messages: {
type: Array,
element: {type: Object, shape: {id: Boolean, text: String }
},
date: Date,
combinedVal: [Number, Boolean]
};
...
}
- it is an object or a list of strings
- a list of strings is a simplified props definition, which only lists the name
of the props. Also, if the name ends with
?
, it is considered optional. - all props are by default required, unless they are defined with
optional: true
(in that case, validation is only done if there is a value) - valid types are:
Number, String, Boolean, Object, Array, Date, Function
, and all constructor functions (so, if you have aPerson
class, it can be used as a type) - arrays are homogeneous (all elements have the same type/shape)
For each key, a prop
definition is either a boolean, a constructor, a list of constructors, or an object:
- a boolean: indicate that the props exists, and is mandatory.
- a constructor: this should describe the type, for example:
id: Number
describe the propsid
as a number - a list of constructors. In that case, this means that we allow more than one
type. For example,
id: [Number, String]
means thatid
can be either a string or a number. - an object. This makes it possible to have more expressive definition. The following sub keys are then allowed (but not mandatory):
type
: the main type of the prop being validatedelement
: if the type wasArray
, then theelement
key describes the type of each element in the array. If it is not set, then we only validate the array, not its elements,shape
: if the type wasObject
, then theshape
key describes the interface of the object. If it is not set, then we only validate the object, not its elements,validate
: this is a function which should return a boolean to determine if the value is valid or not. Useful for custom validation logic.
Examples:
// only the existence of those 3 keys is documented
static props = ['message', 'id', 'date'];
// size is optional
static props = ['message', 'size?'];
static props = {
messageIds: {type: Array, element: Number}, // list of number
otherArr: {type: Array}, // just array. no validation is made on sub elements
otherArr2: Array, // same as otherArr
someObj: {type: Object}, // just an object, no internal validation
someObj2: {
type: Object,
shape: {
id: Number,
name: {type: String, optional: true},
url: String
]}, // object, with keys id (number), name (string, optional) and url (string)
someFlag: Boolean, // a boolean, mandatory (even if `false`)
someVal: [Boolean, Date], // either a boolean or a date
otherValue: true, // indicates that it is a prop
kindofsmallnumber: {
type: Number,
validate: n => (0 <= n && n <= 10)
},
size: {
validate: e => ["small", "medium", "large"].includes(e)
},
};