Previously, the Typescript definition file did not support parameterized
components. The `component()` function did accept additional arguments, but the
`MithrilComponent<T>` interface did not allow components to have any additional
arguments in either their `controller()` or `view()` methods.
Properly supporting flexible variadic parameters in typescript is somewhat
challenging. Tuple types are close, but would only work if the signature of
`controller` and `view` accepted arrays of arguments:
```
interface MithrilStatic {
component<TController extend controller, TRest extends any[]>(
component: ParameterizedMithrilComponent<TController, TRest>,
...args:TRest
) : TController
}
interface ParameterizedMithrilComponent<TController extend controller, TRest
extends any[]> {
// Doesn't match mithril's component interface; we want to unpack the contents
// of TRest
controller: (args: TRest) => TController,
view: (ctrl: TController, args: TRest)
}
```
Therefore, I have gone with a more traditional method of defining several
overloads of m.component(), each with a different number of extra parameters.
Because of this, the first four parameters to m.component() will be correctly
type checked (i.e. if the Component's controller defines the parameter, it must
be supplied to m.component). Additional parameters beyond the first four are
allowed, but are caught via an `...args:any[]` and thus are not type checked.
Background:
In ES6 we now have `Object.setPrototypeOf` which makes prototype delegation an alternative to *sugary*, fake class coding patterns. This can be accomplished in ES5, but ES6 is much more elegant. Here is a basic example. `new` is never used and `this` is very easy to follow.
Working example with changes to mithril.js on lines 854, 858, 859 *only*.
http://jsbin.com/nopugo/1/edit?js,console,output
```javascript
'use strict'
const m = require('mithril')
const DataModel = {
data: [1,2,3],
getData(){
console.log('getting data from server')
return this.data
},
postData(val){
console.log('sending data to server')
this.data.push(val)
}
}
const ViewModel = {
filterEvenData: function() {
return this.getData().filter((val) => (val % 2 === 0) && (+val !== 0))
},
addNewData: function(val){
console.log('adding new data', val)
this.postData(val)
}
}
var Model = Object.setPrototypeOf(ViewModel, DataModel)
const App = {
controller(){},
view(ctrl){
return m('div', [
m('pre', [ 'EVEN NUMBERS IN DATA\n',
this.filterEvenData().map((key,i) =>`${i+1} = ${key}\n`)
]),
m('div', 'Enter a number'),
m('input[placeholder=number]', {
// addNewData is called with **this** === undefined unless we pass `this` to Function.prototype.call in m.withAttr
onchange: m.withAttr('value', App.addNewData, App),
value: null
}
)
])
}
}
Object.setPrototypeOf(App, Model)
m.mount(document.body, App)
```
When a null component is passed into m.mount(), remove references to the root DOM element from:
- roots
- cellCache
- nodeCache
And remove the associated entries from:
- controllers
- components