Skip to content

Commit

Permalink
feat: more types (#1014)
Browse files Browse the repository at this point in the history
* chore: setup tests

* update lock

* switch to babel

* hooks types

* next batch

* couplea globals

* createRender

* fixes

* fix ts build

* fix redirect file

* more fixes

* fixes

* rm comment

* lint
  • Loading branch information
golota60 authored Dec 13, 2022
1 parent a97849e commit 63a44a6
Show file tree
Hide file tree
Showing 19 changed files with 670 additions and 86 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"scripts": {
"tsc": "tsc",
"build:ts": "yarn tsc --outDir lib/cjs --module commonjs && yarn tsc --outDir lib/esm --module es2020 && mv lib/esm/*.d.ts lib && mv lib/cjs/*.d.ts lib",
"build": "rimraf lib && 4c build --types false src && yarn build:pick && yarn build:types && yarn build:ts",
"build": "rimraf lib && 4c build --types false src && yarn build:pick && yarn build:types",
"build:pick": "cherry-pick --cjs-dir cjs --esm-dir esm --cwd lib ../src",
"build:types": "cpy types/*.d.ts lib",
"build:docs": "yarn --cwd www build",
Expand Down
16 changes: 11 additions & 5 deletions src/Redirect.js → src/Redirect.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import RedirectException from './RedirectException';
import { Match, RedirectOptions } from './typeUtils';

export default class Redirect {
constructor({ from, to, status }) {
class Redirect {
constructor({ from, to, status }: RedirectOptions) {
// @ts-ignore
this.path = from;
// @ts-ignore
this.to = to;
// @ts-ignore
this.status = status;
}

render({ match }) {
const { to, status } = this;
render({ match }: { match: Match }) {
const { to, status } = this as any;
let toLocation;

if (typeof to === 'function') {
Expand All @@ -25,5 +29,7 @@ export default class Redirect {
if (__DEV__) {
// Workaround to make React Proxy give me the original class, to allow
// makeRouteConfig to get the actual class, when using JSX for routes.
Redirect.prototype.isReactComponent = {};
(Redirect.prototype as any).isReactComponent = {};
}

export default Redirect;
3 changes: 2 additions & 1 deletion src/RouterContext.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import React from 'react';

export default React.createContext(null);
// TODO: types
export default React.createContext<any>(null);
8 changes: 6 additions & 2 deletions src/StaticContainer.js → src/StaticContainer.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
/* eslint-disable react/prop-types */
import React from 'react';

class StaticContainer extends React.Component {
shouldComponentUpdate({ shouldUpdate }) {
interface Props {
shouldUpdate: boolean;
}

class StaticContainer extends React.Component<Props> {
shouldComponentUpdate({ shouldUpdate }: Props) {
return !!shouldUpdate;
}

Expand Down
12 changes: 11 additions & 1 deletion src/additional.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
/* eslint-disable @typescript-eslint/naming-convention */
/* eslint-disable no-underscore-dangle */
declare let __DEV__: boolean;

declare global {
declare let __DEV__: boolean;
interface Window {
__FOUND_HOT_RELOAD__: boolean;
__FOUND_REPLACE_ROUTE_CONFIG__: (routeConfig: RouteConfig) => void;
}
}

// Force this to be a module
export {};
12 changes: 7 additions & 5 deletions src/createRender.js → src/createRender.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';

import ElementsRenderer from './ElementsRenderer';
import StaticContainer from './StaticContainer';
import { CreateRenderOptions, RenderArgs } from './typeUtils';

/**
* A convenience method for handling the 3 main states a route match might produce.
Expand All @@ -10,17 +11,18 @@ export default function createRender({
renderPending,
renderReady,
renderError,
}) {
return function render(renderArgs) {
const { error, elements } = renderArgs;
}: CreateRenderOptions): (renderArgs: RenderArgs) => React.ReactElement {
return function render(renderArgs: RenderArgs): React.ReactElement {
// TODO: error and elements MIGHT exist, depending on type of renderArgs
const { error, elements } = renderArgs as any;
let element;

if (error) {
element = renderError ? renderError(renderArgs) : null;
element = renderError ? renderError(renderArgs as any) : null;
} else if (!elements) {
element = renderPending ? renderPending(renderArgs) : undefined;
} else if (renderReady) {
element = renderReady(renderArgs);
element = renderReady(renderArgs as any);
} else {
element = <ElementsRenderer elements={elements} />;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import FarceActions from 'farce/Actions';
import { bindActionCreators } from 'redux';
import { Store, bindActionCreators } from 'redux';

import { Router } from './typeUtils';

const NAVIGATION_ACTION_CREATORS = {
push: FarceActions.push,
replace: FarceActions.replace,
go: FarceActions.go,
};

export default function createStoreRouterObject(store) {
const { farce, found } = store;
export default function createStoreRouterObject(store: Store): Router {
// TODO: create an enhanced store type with found and farce maybe?
const { farce, found } = store as any;
const { matcher } = found;

return {
Expand Down
25 changes: 17 additions & 8 deletions src/foundReducer.js → src/foundReducer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { Reducer } from 'redux';

import ActionTypes from './ActionTypes';
import { FoundState } from './typeUtils';

export default function foundReducer(state = null, action) {
// TODO: Re-check types here.
const foundReducer = (
state: any = null,
action: { type: any; payload: any },
) => {
const { type, payload } = action;

switch (type) {
Expand All @@ -14,13 +21,15 @@ export default function foundReducer(state = null, action) {
case ActionTypes.RESOLVE_MATCH:
// It doesn't make sense to resolve a match if there wasn't already an
// unresolved match.
return (
state && {
match: state.match,
resolvedMatch: payload,
}
);
return state
? {
match: state.match,
resolvedMatch: payload,
}
: null;
default:
return state;
}
}
};

export default foundReducer as Reducer<FoundState>;
6 changes: 4 additions & 2 deletions src/getStoreRenderArgs.js → src/getStoreRenderArgs.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import createStoreRouterObject from './createStoreRouterObject';
import getRenderArgs from './getRenderArgs';
import { GetStoreRenderArgsOptions, RenderArgs } from './typeUtils';

// This function returns a promise. It doesn't need to be an async function
// because it doesn't use the promise's value.
export default function getStoreRenderArgs({
store,
getFound = ({ found }) => found,
// TODO: check types of this
getFound = ({ found }: any) => found,
matchContext,
resolver,
}) {
}: GetStoreRenderArgsOptions): Promise<RenderArgs> {
const router = createStoreRouterObject(store);
const match = getFound(store.getState()).resolvedMatch;

Expand Down
4 changes: 3 additions & 1 deletion src/hotRouteConfig.js → src/hotRouteConfig.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export default function hotRouteConfig(routeConfig) {
import { RouteConfig } from './typeUtils';

export default function hotRouteConfig(routeConfig: RouteConfig): RouteConfig {
if (__DEV__ && typeof window !== 'undefined') {
/* eslint-env browser */
/* eslint-disable no-underscore-dangle */
Expand Down
28 changes: 17 additions & 11 deletions src/makeRouteConfig.js → src/makeRouteConfig.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import React from 'react';

function buildRouteConfig(node, routeConfig) {
import { RouteConfig } from './typeUtils';

// TODO: what is even happening in this file? Try to fix as any's(+ maybe simplify)
function buildRouteConfig(
node: React.ReactNode,
routeConfig: any[],
): RouteConfig {
React.Children.forEach(node, (child) => {
// Falsy children get coerced to null. We check for this instead of
// implicit falsiness because we don't want to allow empty strings or 0.
Expand All @@ -12,7 +18,7 @@ function buildRouteConfig(node, routeConfig) {
throw new TypeError(`\`${child}\` is not a valid React element`);
}

let Type = child.type;
let Type = child.type as React.JSXElementConstructor<any>;
const { children, ...props } = child.props;

if (Type === React.Fragment) {
Expand All @@ -21,31 +27,31 @@ function buildRouteConfig(node, routeConfig) {
}

if (__DEV__) {
if (Type.prototype.constructor !== Type) {
if ((Type as any).prototype.constructor !== Type) {
// Unwrap proxies from react-proxy. This isn't strictly necessary.
// eslint-disable-next-line no-param-reassign
Type = Type.prototype.constructor;
Type = (Type as any).prototype.constructor;
} else if (
// eslint-disable-next-line no-underscore-dangle
Type.__reactstandin__getCurrent
(Type as any).__reactstandin__getCurrent
) {
// Unwrap proxies from react-stand-in.
// eslint-disable-next-line no-param-reassign
Type = Object.getPrototypeOf(Type);
}
}

const route = new Type(props);
const route = new (Type as any)(props);

if (children) {
if (React.isValidElement(children) || Array.isArray(children)) {
// eslint-disable-next-line no-use-before-define
route.children = makeRouteConfig(children);
route.children = buildRouteConfig(children, []);
} else {
const routeGroups = {};
Object.entries(children).forEach(([groupName, groupRoutes]) => {
const routeGroups: Record<string, RouteConfig> = {};
Object.entries(children).forEach(([groupName, groupRoutes]: any[]) => {
// eslint-disable-next-line no-use-before-define
routeGroups[groupName] = makeRouteConfig(groupRoutes);
routeGroups[groupName] = buildRouteConfig(groupRoutes, []);
});

route.children = routeGroups;
Expand All @@ -61,6 +67,6 @@ function buildRouteConfig(node, routeConfig) {
/**
* Create a route configuration from JSX configuration elements.
*/
export default function makeRouteConfig(node) {
export default function makeRouteConfig(node: React.ReactNode): RouteConfig {
return buildRouteConfig(node, []);
}
Loading

0 comments on commit 63a44a6

Please sign in to comment.