Skip to content

Commit

Permalink
Merge pull request #332 from tildeio/looser-route-generics
Browse files Browse the repository at this point in the history
Loosen route generics
  • Loading branch information
wagenet authored Feb 9, 2022
2 parents 7bd909d + bf4b815 commit 7c61cf0
Show file tree
Hide file tree
Showing 13 changed files with 53 additions and 58 deletions.
1 change: 0 additions & 1 deletion lib/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export {
export { default as TransitionState, TransitionError } from './transition-state';
export {
default as InternalRouteInfo,
IModel,
ModelFor,
Route,
RouteInfo,
Expand Down
18 changes: 9 additions & 9 deletions lib/router/route-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export type IModel = {} & {

export type ModelFor<T> = T extends Route<infer V> ? V : never;

export interface Route<T extends IModel = {}> {
export interface Route<T = unknown> {
inaccessibleByURL?: boolean;
routeName: string;
_internalName: string;
Expand Down Expand Up @@ -56,11 +56,11 @@ export interface RouteInfoWithAttributes extends RouteInfo {
attributes: any;
}

type RouteInfosKey = InternalRouteInfo<Route<{}>>;
type RouteInfosKey = InternalRouteInfo<Route>;

let ROUTE_INFOS = new WeakMap<RouteInfosKey, RouteInfo | RouteInfoWithAttributes>();

export function toReadOnlyRouteInfo<R extends Route<{}>>(
export function toReadOnlyRouteInfo<R extends Route>(
routeInfos: InternalRouteInfo<R>[],
queryParams: Dict<unknown> = {},
includeAttributes = false
Expand Down Expand Up @@ -200,7 +200,7 @@ function attachMetadata(route: Route, routeInfo: RouteInfo) {
return Object.assign(routeInfo, metadata);
}

export default class InternalRouteInfo<R extends Route<{}>> {
export default class InternalRouteInfo<R extends Route> {
private _routePromise?: Promise<R> = undefined;
private _route?: Option<R> = null;
protected router: Router<R>;
Expand Down Expand Up @@ -265,7 +265,7 @@ export default class InternalRouteInfo<R extends Route<{}>> {
}

// SAFETY: Since this is just for lookup, it should be safe
let cached = ROUTE_INFOS.get((this as unknown) as InternalRouteInfo<Route<{}>>);
let cached = ROUTE_INFOS.get((this as unknown) as InternalRouteInfo<Route>);
let resolved = new ResolvedRouteInfo<R>(
this.router,
this.name,
Expand All @@ -277,7 +277,7 @@ export default class InternalRouteInfo<R extends Route<{}>> {

if (cached !== undefined) {
// SAFETY: This is potentially a bit risker, but for what we're doing, it should be ok.
ROUTE_INFOS.set((this as unknown) as InternalRouteInfo<Route<{}>>, cached);
ROUTE_INFOS.set((this as unknown) as InternalRouteInfo<Route>, cached);
}

return resolved;
Expand Down Expand Up @@ -422,7 +422,7 @@ export default class InternalRouteInfo<R extends Route<{}>> {
}
}

export class ResolvedRouteInfo<R extends Route<{}>> extends InternalRouteInfo<R> {
export class ResolvedRouteInfo<R extends Route> extends InternalRouteInfo<R> {
isResolved: boolean;
context: ModelFor<R> | undefined;
constructor(
Expand All @@ -448,7 +448,7 @@ export class ResolvedRouteInfo<R extends Route<{}>> extends InternalRouteInfo<R>
}
}

export class UnresolvedRouteInfoByParam<R extends Route<{}>> extends InternalRouteInfo<R> {
export class UnresolvedRouteInfoByParam<R extends Route> extends InternalRouteInfo<R> {
params: Dict<unknown> = {};
constructor(
router: Router<R>,
Expand Down Expand Up @@ -496,7 +496,7 @@ export class UnresolvedRouteInfoByParam<R extends Route<{}>> extends InternalRou
}
}

export class UnresolvedRouteInfoByObject<R extends Route<{}>> extends InternalRouteInfo<R> {
export class UnresolvedRouteInfoByObject<R extends Route> extends InternalRouteInfo<R> {
serializer?: SerializerFunc<ModelFor<R>>;
constructor(
router: Router<R>,
Expand Down
21 changes: 9 additions & 12 deletions lib/router/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import RouteRecognizer, { MatchCallback, Params, QueryParams } from 'route-recog
import { Promise } from 'rsvp';
import { Dict, Maybe, Option } from './core';
import InternalRouteInfo, {
IModel,
ModelFor,
Route,
RouteInfo,
Expand Down Expand Up @@ -32,7 +31,7 @@ import {
promiseLabel,
} from './utils';

export interface SerializerFunc<T extends IModel> {
export interface SerializerFunc<T> {
(model: T, params: string[]): Dict<unknown>;
}

Expand All @@ -41,7 +40,7 @@ export interface ParsedHandler {
names: string[];
}

export default abstract class Router<R extends Route<{}>> {
export default abstract class Router<R extends Route> {
private _lastQueryParams = {};
log?: (message: string) => void;
state?: TransitionState<R> = undefined;
Expand Down Expand Up @@ -303,7 +302,7 @@ export default abstract class Router<R extends Route<{}>> {
let lastArg = modelsArray[modelsArray.length - 1];
let queryParams: Dict<unknown> = {};

if (lastArg !== undefined && lastArg.hasOwnProperty('queryParams')) {
if (lastArg && Object.prototype.hasOwnProperty.call(lastArg, 'queryParams')) {
// We just checked this.
// TODO: Use an assertion?
queryParams = (modelsArray.pop() as { queryParams: QueryParams }).queryParams as Dict<
Expand Down Expand Up @@ -1042,12 +1041,10 @@ export default abstract class Router<R extends Route<{}>> {
}
}

function routeInfosEqual<
T1 extends IModel,
R1 extends Route<T1>,
T2 extends IModel,
R2 extends Route<T2>
>(routeInfos: InternalRouteInfo<R1>[], otherRouteInfos: InternalRouteInfo<R2>[]) {
function routeInfosEqual<R1 extends Route, R2 extends Route>(
routeInfos: InternalRouteInfo<R1>[],
otherRouteInfos: InternalRouteInfo<R2>[]
) {
if (routeInfos.length !== otherRouteInfos.length) {
return false;
}
Expand All @@ -1061,7 +1058,7 @@ function routeInfosEqual<
return true;
}

function routeInfosSameExceptQueryParams<R1 extends Route<{}>, R2 extends Route<{}>>(
function routeInfosSameExceptQueryParams<R1 extends Route, R2 extends Route>(
routeInfos: InternalRouteInfo<R1>[],
otherRouteInfos: InternalRouteInfo<R2>[]
) {
Expand Down Expand Up @@ -1110,7 +1107,7 @@ function paramsEqual(params: Dict<unknown> | undefined, otherParams: Dict<unknow
return true;
}

export interface RoutePartition<R extends Route<{}>> {
export interface RoutePartition<R extends Route> {
updatedContext: InternalRouteInfo<R>[];
exited: InternalRouteInfo<R>[];
entered: InternalRouteInfo<R>[];
Expand Down
2 changes: 1 addition & 1 deletion lib/router/transition-intent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import TransitionState from './transition-state';

export type OpaqueIntent = TransitionIntent<any>;

export abstract class TransitionIntent<R extends Route<{}>> {
export abstract class TransitionIntent<R extends Route> {
data: {};
router: Router<R>;
constructor(router: Router<R>, data: {} = {}) {
Expand Down
2 changes: 1 addition & 1 deletion lib/router/transition-intent/named-transition-intent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { TransitionIntent } from '../transition-intent';
import TransitionState from '../transition-state';
import { isParam, merge } from '../utils';

export default class NamedTransitionIntent<R extends Route<{}>> extends TransitionIntent<R> {
export default class NamedTransitionIntent<R extends Route> extends TransitionIntent<R> {
name: string;
pivotHandler?: Route;
contexts: ModelFor<R>[];
Expand Down
2 changes: 1 addition & 1 deletion lib/router/transition-intent/url-transition-intent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import TransitionState from '../transition-state';
import UnrecognizedURLError from '../unrecognized-url-error';
import { merge } from '../utils';

export default class URLTransitionIntent<R extends Route<{}>> extends TransitionIntent<R> {
export default class URLTransitionIntent<R extends Route> extends TransitionIntent<R> {
preTransitionState?: TransitionState<R>;
url: string;
constructor(router: Router<R>, url: string, data?: {}) {
Expand Down
8 changes: 4 additions & 4 deletions lib/router/transition-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interface IParams {
[key: string]: unknown;
}

function handleError<R extends Route<{}>>(
function handleError<R extends Route>(
currentState: TransitionState<R>,
transition: Transition<R>,
error: Error
Expand All @@ -30,7 +30,7 @@ function handleError<R extends Route<{}>>(
);
}

function resolveOneRouteInfo<R extends Route<{}>>(
function resolveOneRouteInfo<R extends Route>(
currentState: TransitionState<R>,
transition: Transition<R>
): void | Promise<void> {
Expand All @@ -49,7 +49,7 @@ function resolveOneRouteInfo<R extends Route<{}>>(
return routeInfo.resolve(transition).then(callback, null, currentState.promiseLabel('Proceed'));
}

function proceed<R extends Route<{}>>(
function proceed<R extends Route>(
currentState: TransitionState<R>,
transition: Transition<R>,
resolvedRouteInfo: ResolvedRouteInfo<R>
Expand Down Expand Up @@ -80,7 +80,7 @@ function proceed<R extends Route<{}>>(
return resolveOneRouteInfo(currentState, transition);
}

export default class TransitionState<R extends Route<{}>> {
export default class TransitionState<R extends Route> {
routeInfos: InternalRouteInfo<R>[] = [];
queryParams: Dict<unknown> = {};
params: IParams = {};
Expand Down
2 changes: 1 addition & 1 deletion lib/router/transition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export const QUERY_PARAMS_SYMBOL = `__QPS__-2619863929824844-32323`;
@param {Object} error
@private
*/
export default class Transition<R extends Route<{}>> implements Partial<Promise<R>> {
export default class Transition<R extends Route> implements Partial<Promise<R>> {
[STATE_SYMBOL]: TransitionState<R>;
from: Maybe<RouteInfoWithAttributes> = null;
to?: RouteInfo | RouteInfoWithAttributes = undefined;
Expand Down
4 changes: 2 additions & 2 deletions tests/query_params_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
trigger,
} from './test_helpers';

let router: Router<Route<{}>>, handlers: Dict<Route>, expectedUrl: Maybe<string>;
let router: Router<Route>, handlers: Dict<Route>, expectedUrl: Maybe<string>;
let scenarios = [
{
name: 'Sync Get Handler',
Expand Down Expand Up @@ -52,7 +52,7 @@ scenarios.forEach(function (scenario) {
didTransition() {}
willTransition() {}
triggerEvent(
handlerInfos: RouteInfo<Route<{}>>[],
handlerInfos: RouteInfo<Route>[],
ignoreFailure: boolean,
name: string,
args: any[]
Expand Down
25 changes: 12 additions & 13 deletions tests/router_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ import {
trigger,
} from './test_helpers';

let router: Router<Route<{}>>;
let router: Router<Route>;
let url: string | undefined;
let routes: Dict<Route>;

function isPresent(maybe: Maybe<PublicRouteInfo>): maybe is PublicRouteInfo {
return maybe !== undefined && maybe !== null;
}

let serializers: Dict<SerializerFunc<Dict<unknown>>>, expectedUrl: Maybe<string>;
let serializers: Dict<SerializerFunc<unknown>>, expectedUrl: Maybe<string>;
let scenarios = [
{
name: 'Sync Get Handler',
Expand Down Expand Up @@ -105,7 +105,7 @@ scenarios.forEach(function (scenario) {
this.updateURL(name);
}
triggerEvent(
handlerInfos: RouteInfo<Route<{}>>[],
handlerInfos: RouteInfo<Route>[],
ignoreFailure: boolean,
name: string,
args: any[]
Expand Down Expand Up @@ -158,7 +158,7 @@ scenarios.forEach(function (scenario) {
});
});

function routePath(infos: RouteInfo<Route<{}>>[]) {
function routePath(infos: RouteInfo<Route>[]) {
let path = [];

for (let i = 0, l = infos.length; i < l; i++) {
Expand Down Expand Up @@ -2619,7 +2619,7 @@ scenarios.forEach(function (scenario) {
}),
};
router.triggerEvent = function (
handlerInfos: RouteInfo<Route<{}>>[],
handlerInfos: RouteInfo<Route>[],
ignoreFailure: boolean,
name: string,
args: any[]
Expand Down Expand Up @@ -3011,7 +3011,6 @@ scenarios.forEach(function (scenario) {
);
assert.ok(!router.isActive('adminPost'), 'The adminPost handler is inactive');
assert.ok(
// @ts-expect-error BUG: The types don't allow for `null` to be passed here. This behavior seems to be undocumented.
!router.isActive('showPost', null),
'The showPost handler is inactive with a null context'
);
Expand Down Expand Up @@ -4933,9 +4932,7 @@ scenarios.forEach(function (scenario) {
router.getRoute = function (name) {
count++;

return Promise.resolve(scenario.getRoute.call(null, name)).then(function (
handler: Route<{}>
) {
return Promise.resolve(scenario.getRoute.call(null, name)).then(function (handler: Route) {
assert.equal(count, handlerCount);
return handler;
});
Expand Down Expand Up @@ -4997,16 +4994,18 @@ scenarios.forEach(function (scenario) {
if (scenario.async) {
serializers = {
parent: function (obj) {
let castObj = obj as Dict<unknown>;
// TODO: Review this
return {
one: obj.one,
two: obj.two,
one: castObj.one,
two: castObj.two,
};
},
child: function (obj) {
let castObj = obj as Dict<unknown>;
return {
three: obj.three,
four: obj.four,
three: castObj.three,
four: castObj.four,
};
},
};
Expand Down
18 changes: 9 additions & 9 deletions tests/test_helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ function assertAbort(assert: Assert) {
// the backburner queue. Helpful for when you want to write
// tests that avoid .then callbacks.
function transitionTo(
router: Router<Route<{}>>,
router: Router<Route>,
path: string | { queryParams: Dict<unknown> },
...context: any[]
) {
Expand All @@ -62,18 +62,18 @@ function transitionTo(
return result;
}

function transitionToWithAbort(assert: Assert, router: Router<Route<{}>>, path: string) {
function transitionToWithAbort(assert: Assert, router: Router<Route>, path: string) {
router.transitionTo(path).then(shouldNotHappen(assert), assertAbort(assert));
flushBackburner();
}

function replaceWith(router: Router<Route<{}>>, path: string) {
function replaceWith(router: Router<Route>, path: string) {
let result = router.transitionTo.apply(router, [path]).method('replace');
flushBackburner();
return result;
}

function handleURL(router: Router<Route<{}>>, url: string) {
function handleURL(router: Router<Route>, url: string) {
let result = router.handleURL.apply(router, [url]);
flushBackburner();
return result;
Expand All @@ -88,7 +88,7 @@ function shouldNotHappen(assert: Assert, _message?: string) {
};
}

export function isExiting(route: Route | string, routeInfos: RouteInfo<Route<{}>>[]) {
export function isExiting(route: Route | string, routeInfos: RouteInfo<Route>[]) {
for (let i = 0, len = routeInfos.length; i < len; ++i) {
let routeInfo = routeInfos[i];
if (routeInfo.name === route || routeInfo.route === route) {
Expand Down Expand Up @@ -163,9 +163,9 @@ export class TestRouter<R extends Route = Route> extends Router<R> {
}
}

export function createHandlerInfo(name: string, options: Dict<unknown> = {}): RouteInfo<Route<{}>> {
class Stub extends RouteInfo<Route<{}>> {
constructor(name: string, router: Router<Route<{}>>, handler?: Route) {
export function createHandlerInfo(name: string, options: Dict<unknown> = {}): RouteInfo<Route> {
class Stub extends RouteInfo<Route> {
constructor(name: string, router: Router<Route>, handler?: Route) {
super(router, name, [], handler);
}
getModel(_transition: Transition) {
Expand All @@ -185,7 +185,7 @@ export function createHandlerInfo(name: string, options: Dict<unknown> = {}): Ro
}

export function trigger(
handlerInfos: RouteInfo<Route<{}>>[],
handlerInfos: RouteInfo<Route>[],
ignoreFailure: boolean,
name: string,
...args: any[]
Expand Down
Loading

0 comments on commit 7c61cf0

Please sign in to comment.