can-define
can-define copied to clipboard
Add "function" type to can-define
Passing functions is much more common with CanJS. I'd like to add a "function" type so these properties can be clearly identified.
Instead of:
{
update: "any"
}
We'd be able to write:
{
update: "function"
}
Or:
import {MaybeFunction} from "can";
{
update: MaybeFunction
}
Questions
What should happen if people pass something other than null
, undefined
, or a function
?
- Warn and convert to
null
? - Throw?
- Warn and convert to a no-op?
-
Convert to a no-op that warns?
Type = DefineMap.extend({func: "function"}); var instance = new Type({func: "NOT A FUNCTION"}); instance.func() //-> console.warns( "func was set to 'NOT A FUNCTION'.");
How should we handle default values?
Should a default look like this to be consistent?
DefineMap.extend({
update: {
type: "function",
default() {
return function() { ... }
}
}
});
What should we do with methods that are set?
var Type = DefineMap.extend({
update(){}
});
var instance = new Type();
instance.update = function(){} // throws "Cannot add property update, object is not extensible"
We probably should not make update()
a settable property because that would mean we have to make .update()
always go through a getter and call Observation.add()
. It's probably not worth the performance cost.
I don't know what the existing behavior for a type mismatch is, but I would expect it to throw.
@thomaswilburn the existing behavior is coercion. For example,
var MyMap = DefineMap.extend({age: "number"});
new MyMap({age: "21"}).age //-> 21;
new MyMap({age: "foo"}).age //-> NaN
We don't throw. For that, there is this proposal: https://github.com/canjs/can-define/issues/334
How would you define the default value for this?
DefineMap.extend({
update: {
type: "function",
default() {
return function() { ... }
}
}
});
vs. how you would do it now:
DefineMap.extend({
update() {
// ...
}
});
@matthewp we could special case a function
type's default
. But that feels weird.
What should happen if people pass something other than null, undefined, or a function?
I think the value should still be coerced (I assume something like new Function(4)
, assume 4
was the value passed), and we should warn the User. If we can tell them the the component, property name, and incorrect value passed that would be awesome.
How should we handle default values?
I think a special syntax (update() {}
) would be okay but is not necessary at this stage (in general I find the less special cases the better -- less cognitive overhead, less surprises, less required documentation). default
for this type should perform as it does for all the other types.
DefineMap.extend({
update: {
type: "function",
default() {
return function() {};
}
}
});
What should we do with methods that are set?
I /do not/ think methods should be able to be redefined. If you want to change out functions on a DefineMap, make it a property and not a method.