On any JS project my preference is to always use TypeScript, as it greatly increases my productivity (intellisense, autocompletion) while eliminating 90% of the bugs right there, at design time.

Over time I’ve gathered some useful snippets and patterns, out of which I’d like to share a couple I use most with you.

Hope you find these patterns useful as I do.

Connected Class Component

A Redux connected component has mapped properties which include mapped store properties and store actions. Wouldn’t it be nice if we could have these properties type-checked and auto-completed? Here is how I do it:

///////////////////
// Clock.ts
///////////////////
import { ConnectedComponentProps } from "./prop-utils";
import * as counterActions from "./actions";
import { connect } from "react-redux";
import { IRootState } from "./store";
import { Dispatch } from "redux";
import * as React from 'react';

// Store state mapping
const mapStateToProps = (state: IRootState) => ({
    counterValue: state.counter,
});

// Actions mapping
const mapDispatchToProps = (dispatch: Dispatch) => ({
    increment: () => dispatch(counterActions.incrementCounter()),
});

// Own component props
interface ICounterProps {
    counterTitle: string;
}

// Combine own props with store state and store actions
type CounterProps = ConnectedComponentProps<
    typeof mapDispatchToProps,
    typeof mapStateToProps,
    ICounterProps>;

class Counter extends React.Component<CounterProps> { // Note use of CounterProps
    constructor(props: CounterProps) {
        super(props);
    }

    render() {
        // Strong typing of store-bound and own props
        const {counterValue, counterTitle, increment} = this.props;

        return <div onClick={increment}>
            ${counterTitle}: {counterValue}
        </div>;
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(Counter);

Below are the TypeScript utility types that perform the magic:

///////////////////
// prop-utils.ts
///////////////////
type FunctionType = (...args: any) => any;

type VoidFunc = () => {};

export type ConnectedComponentProps<
    StoreActions extends FunctionType = VoidFunc,
    StoreProps extends FunctionType = VoidFunc,
    OwnProps = {}> = ReturnType<StoreActions> & ReturnType<StoreProps> & OwnProps;

Note, that as expected, when using this component we only get its own properties available and checked:

///////////////////
// Dashboard.ts
///////////////////
import * as React from 'react';
import CounterConnected from "./Counter";

export const Dashboard: React.FunctionComponent = () => {
    // Only own props are available and checked
    return <CounterConnected counterTitle={'Counter'} />
};

Enum Keys in Object Literal

TypeScript enums are oftentimes used as mapped data keys, as in the example below – an imaginary SPA’s routes having a mapping route (enum key)route entry.

The two utility types below are used to enforce using only the enum keys in our mapping – either all of them (type RoutesMap) or just some of them (RoutesMapPartial).

type RoutesMap<R> = {[key in keyof typeof AppRoutes]: R };
type PartialRoutesMap<R> = Partial<RoutesMap<R>>;

const enum AppRoutes {
    HOME = 'HOME',
    LOGIN = 'LOGIN',
    DASHBOARD = 'DASHBOARD',
}

interface RouteEntry {
    path: string;
}

// All enum keys are required
export const allRoutes: RoutesMap<RouteEntry> = {
    [AppRoutes.HOME]: { path: '/home' },
    [AppRoutes.LOGIN]: { path: '/login' },
    [AppRoutes.DASHBOARD]: { path: '/dashboard'},
};

// Only some enum keys are required
export const someRoutes: PartialRoutesMap<RouteEntry> = {
    [AppRoutes.LOGIN]: { path: '/login' },
};

Thanks for reading through, share if you care 🙂

– Zacky