In the latter case, the type already structurally satisfied that type.
Changing m.redraw.strategy simplifies the definition file, and aligns with
the docs.
MithrilPromise returns the value it holds and not another promise that
holds the value. MithrilPromiseProperty was adjusted to act as a nested
MithrilPromise.
`MithrilVirtualElement` was recently converted to be generic over `<T extends
MithrilController>`. The element's `children` may then contain other
`MithrilVirtualElement<T>` or `MithrilComponent<T>` elements.
Unfortunately, in common usage of 'm()' this can cause problems with Mithril's type
inference system. If a type is not explicitly provided to `m()`, the "T" of the
returned MithrilVirtualElement<T> will be inferred from its children. The
candidates for T will be the concrete types of the child MithrilComponent<T>; however, if no
candidate type is a subset of *every* candidate type, then TypeScript will be
unable to infer a valid type, and the call to `m()` will not compile.
To improve this behavior, this commit makes MithrilVirtualElement non-generic;
it's child collection can now contain MithrilComponent<MithrilController>, which
matches all valid MithrilComponent. This the same underlying issue as the
previous commit, which made MithrilRoutes non-generic.
The motivation for fixing this is threefold:
1. Because `m()` has so many overloads, in the case where a call to `m()` will
not compile due to the above issue, the syntax error provided by TypeScript is
not helpful.
2. In many cases, users will be calling `m<MithrilController>()` in their code
simply to get it to compile, which provides no additional utility over a
non-generic type and adds significant boilerplate.
3. Explicitly specifying subtypes to `m()` calls provides little utility; at
best, it can check that contained components implement some common interface.
However, type assertions like that can be provided in other places without
having to attach the information to the MithrilVirtualElement.
The previous definition of MithrilRoutes was generic over <T extends
MithrilController>, with the MithrilRoutes containing a collection of
MithrilComponent<T>.
However, this presents problems with TypeScript's type inference in many common
situations. For example, consider this usage of m.route():
```
m.route(document.body, "/a", {
"/a": ComponentA,
"/b": ComponentB,
})
```
Both `ComponentA` and `ComponentB` implement `MithrilComponent<T extends
MithrilController>`, with each component having a different concrete controller
type (`ControllerA` and `ControllerB`).
However, unless ControllerA's type is a subset of ControllerB's type (or
vice-versa), TypeScript will be unable to infer the type of the MitrilRoutes<T>
returned by m.route(). To get this to compile, a third type which *is* a
subset of both ControllerA and ControllerB would need to be explicity provided:
```
// Both ControllerA and ControllerB implement MithrilController
m.route<MithrilController>(document.body, "/a", {
"/a": ComponentA,
"/b": ComponentB,
})
```
This commit makes MithrilRoute non-generic, and instead of containing
MithrilComponent<T extends MithrilRoute>, it contains
MithrilComponent<MithrilController>, which will match all valid
MithrilComponents.
The motivation for this change is twofold:
1. In the case where m.route() does not compile due to type-inference failure,
the resulting syntax error from Typescript is very unhelpful because m.route()
has a number of overloads. Leaving this as is will result in confusion for
downstream users.
2. An assumption that vanishingly little utility is provided by defining
`MithrilRoutes<T extends MithrilController>` over a type more specific than
MithrilController. At most, it could be used to assert that all of your routed
Components meet a more specialized interface, but that same type check could be
accomplished without having to augment MithrilRoutes with that information.
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.