Improve Typescript definition for MithrilRoutes

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.
This commit is contained in:
Matt Tracy 2015-09-29 14:05:53 -04:00
parent 6f15a9b148
commit 26f68744b5

10
mithril.d.ts vendored
View file

@ -431,10 +431,10 @@ declare module _mithril {
* @param defaultRoute The route to start with.
* @param routes A key-value mapping of pathname to controller.
*/
<T extends MithrilController>(
(
rootElement: Element,
defaultRoute: string,
routes: MithrilRoutes<T>
routes: MithrilRoutes
): void;
/**
@ -447,7 +447,7 @@ declare module _mithril {
* m("a[href='/dashboard/alicesmith']", {config: m.route});
* ```
*/
<T extends MithrilController>(
(
element: Element,
isInitialized: boolean,
context?: MithrilContext,
@ -876,12 +876,12 @@ declare module _mithril {
/**
* This represents a key-value mapping linking routes to components.
*/
interface MithrilRoutes<T extends MithrilController> {
interface MithrilRoutes {
/**
* The key represents the route. The value represents the corresponding
* component.
*/
[key: string]: MithrilComponent<T>;
[key: string]: MithrilComponent<MithrilController>;
}
/**