Skip to main content

Useful Patterns by Use Case

Wrapping/Mirroring

Wrapping/Mirroring a HTML Element

Usecase: you want to make a <Button> that takes all the normal props of <button> and does extra stuff.

Strategy: extend React.ComponentPropsWithoutRef<'button'>

// usage
function App() {
// Type '"foo"' is not assignable to type '"button" | "submit" | "reset" | undefined'.(2322)
// return <Button type="foo"> sldkj </Button>

// no error
return <Button type="button"> text </Button>;
}

// implementation
export interface ButtonProps extends React.ComponentPropsWithoutRef<"button"> {
specialProp?: string;
}
export function Button(props: ButtonProps) {
const { specialProp, ...rest } = props;
// do something with specialProp
return <button {...rest} />;
}

See this in the TS Playground

Forwarding Refs: Most use cases will not need to obtain a ref to the inner element. When building reusable component libraries, however, forwardRef is often needed for exposing the underlying DOM node of an inner component to a parent component. Then you can use ComponentPropsWithRef to grab props for your wrapper component. Check our docs on forwarding Refs for more.

Why not ComponentProps or IntrinsicElements or [Element]HTMLAttributes or HTMLProps or HTMLAttributes?

ComponentProps

You CAN use ComponentProps in place of ComponentPropsWithRef, but you may prefer to be explicit about whether or not the component's refs are forwarded, which is what we have chosen to demonstrate. The tradeoff is slightly more intimidating terminology.

More info: https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/forward_and_create_ref/

Maybe React.JSX.IntrinsicElements or [Element]HTMLAttributes

There are at least 2 other equivalent ways to do this, but they are more verbose:

// Method 1: React.JSX.IntrinsicElements
type BtnType = React.JSX.IntrinsicElements["button"]; // cannot inline or will error
export interface ButtonProps extends BtnType {} // etc

// Method 2: React.[Element]HTMLAttributes
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>

Looking at the source for ComponentProps shows that this is a clever wrapper for React.JSX.IntrinsicElements, whereas the second method relies on specialized interfaces with unfamiliar naming/capitalization.

Note: There are over 50 of these specialized interfaces available - look for HTMLAttributes in our @types/react commentary.

Ultimately, we picked the ComponentProps method as it involves the least TS specific jargon and has the most ease of use. But you'll be fine with either of these methods if you prefer.

Definitely not React.HTMLProps or React.HTMLAttributes

This is what happens when you use React.HTMLProps:

export interface ButtonProps extends React.HTMLProps<HTMLButtonElement> {
specialProp: string;
}
export function Button(props: ButtonProps) {
const { specialProp, ...rest } = props;
// ERROR: Type 'string' is not assignable to type '"button" | "submit" | "reset" | undefined'.
return <button {...rest} />;
}

It infers a too-wide type of string for type, because it uses AllHTMLAttributes under the hood.

This is what happens when you use React.HTMLAttributes:

import { HTMLAttributes } from "react";

export interface ButtonProps extends HTMLAttributes<HTMLButtonElement> {
/* etc */
}

function App() {
// Property 'type' does not exist on type 'IntrinsicAttributes & ButtonProps'
return <Button type="submit"> text </Button>;
}

Wrapping/Mirroring a Component

TODO: this section needs work to make it simplified.

Usecase: same as above, but for a React Component you don't have access to the underlying props

import { CSSProperties } from "react";

const Box = (props: CSSProperties) => <div style={props} />;

const Card = (
{ title, children, ...props }: { title: string } & $ElementProps<typeof Box> // new utility, see below
) => (
<Box {...props}>
{title}: {children}
</Box>
);

Strategy: extract a component's props by inferring them

Example:

// ReactUtilityTypes.d.ts
declare type $ElementProps<T> = T extends React.ComponentType<infer Props>
? Props extends object
? Props
: never
: never;

Usage:

import * as Recompose from "recompose";
export const defaultProps = <
C extends React.ComponentType,
D extends Partial<$ElementProps<C>>
>(
defaults: D,
Component: C
): React.ComponentType<$ElementProps<C> & Partial<D>> =>
Recompose.defaultProps(defaults)(Component);

thanks dmisdm

🆕 You should also consider whether to explicitly forward refs:

import { forwardRef, ReactNode } from "react";

// base button, with ref forwarding
type Props = { children: ReactNode; type: "submit" | "button" };
export type Ref = HTMLButtonElement;

export const FancyButton = forwardRef<Ref, Props>((props, ref) => (
<button ref={ref} className="MyCustomButtonClass" type={props.type}>
{props.children}
</button>
));

Polymorphic Components (e.g. with as props)

"Polymorphic Components" = passing a component to be rendered, e.g. with as props

ElementType is pretty useful to cover most types that can be passed to createElement e.g.

function PassThrough(props: { as: React.ElementType<any> }) {
const { as: Component } = props;

return <Component />;
}

You might also see this with React Router:

const PrivateRoute = ({ component: Component, ...rest }: PrivateRouteProps) => {
const { isLoggedIn } = useAuth();

return isLoggedIn ? <Component {...rest} /> : <Redirect to="/" />;
};

For more info you can refer to these resources:

Thanks @eps1lon and @karol-majewski for thoughts!

Generic Components

Just as you can make generic functions and classes in TypeScript, you can also make generic components to take advantage of the type system for reusable type safety. Both Props and State can take advantage of the same generic types, although it probably makes more sense for Props than for State. You can then use the generic type to annotate types of any variables defined inside your function / class scope.

import { ReactNode, useState } from "react";

interface Props<T> {
items: T[];
renderItem: (item: T) => ReactNode;
}

function List<T>(props: Props<T>) {
const { items, renderItem } = props;
const [state, setState] = useState<T[]>([]); // You can use type T in List function scope.
return (
<div>
{items.map(renderItem)}
<button onClick={() => setState(items)}>Clone</button>
{JSON.stringify(state, null, 2)}
</div>
);
}

You can then use the generic components and get nice type safety through type inference:

ReactDOM.render(
<List
items={["a", "b"]} // type of 'string' inferred
renderItem={(item) => (
<li key={item}>
{/* Error: Property 'toPrecision' does not exist on type 'string'. */}
{item.toPrecision(3)}
</li>
)}
/>,
document.body
);

As of TS 2.9, you can also supply the type parameter in your JSX to opt out of type inference:

ReactDOM.render(
<List<number>
items={["a", "b"]} // Error: Type 'string' is not assignable to type 'number'.
renderItem={(item) => <li key={item}>{item.toPrecision(3)}</li>}
/>,
document.body
);

You can also use Generics using fat arrow function style:

import { ReactNode, useState } from "react";

interface Props<T> {
items: T[];
renderItem: (item: T) => ReactNode;
}

// Note the <T extends unknown> before the function definition.
// You can't use just `<T>` as it will confuse the TSX parser whether it's a JSX tag or a Generic Declaration.
// You can also use <T,> https://github.com/microsoft/TypeScript/issues/15713#issuecomment-499474386
const List = <T extends unknown>(props: Props<T>) => {
const { items, renderItem } = props;
const [state, setState] = useState<T[]>([]); // You can use type T in List function scope.
return (
<div>
{items.map(renderItem)}
<button onClick={() => setState(items)}>Clone</button>
{JSON.stringify(state, null, 2)}
</div>
);
};

The same for using classes: (Credit: Karol Majewski's gist)

import { PureComponent, ReactNode } from "react";

interface Props<T> {
items: T[];
renderItem: (item: T) => ReactNode;
}

interface State<T> {
items: T[];
}

class List<T> extends PureComponent<Props<T>, State<T>> {
// You can use type T inside List class.
state: Readonly<State<T>> = {
items: [],
};
render() {
const { items, renderItem } = this.props;
// You can use type T inside List class.
const clone: T[] = items.slice(0);
return (
<div>
{items.map(renderItem)}
<button onClick={() => this.setState({ items: clone })}>Clone</button>
{JSON.stringify(this.state, null, 2)}
</div>
);
}
}

Though you can't use Generic Type Parameters for Static Members:

class List<T> extends React.PureComponent<Props<T>, State<T>> {
// Static members cannot reference class type parameters.ts(2302)
static getDerivedStateFromProps(props: Props<T>, state: State<T>) {
return { items: props.items };
}
}

To fix this you need to convert your static function to a type inferred function:

class List<T> extends React.PureComponent<Props<T>, State<T>> {
static getDerivedStateFromProps<T>(props: Props<T>, state: State<T>) {
return { items: props.items };
}
}

Typing Children

Some API designs require some restriction on children passed to a parent component. It is common to want to enforce these in types, but you should be aware of limitations to this ability.

What You CAN Do

You can type the structure of your children: just one child, or a tuple of children.

The following are valid:

type OneChild = React.ReactNode;
type TwoChildren = [React.ReactNode, React.ReactNode];
type ArrayOfProps = SomeProp[];
type NumbersChildren = number[];
type TwoNumbersChildren = [number, number];
Don't forget that you can also use `prop-types` if TS fails you.
Parent.propTypes = {
children: PropTypes.shape({
props: PropTypes.shape({
// could share `propTypes` to the child
value: PropTypes.string.isRequired,
}),
}).isRequired,
};

What You CANNOT Do

The thing you cannot do is specify which components the children are, e.g. If you want to express the fact that "React Router <Routes> can only have <Route> as children, nothing else is allowed" in TypeScript.

This is because when you write a JSX expression (const foo = <MyComponent foo='foo' />), the resultant type is blackboxed into a generic React.JSX.Element type. (thanks @ferdaber)

Type Narrowing based on Props

What you want:

// Usage
function App() {
return (
<>
{/* 😎 All good */}
<Button target="_blank" href="https://www.google.com">
Test
</Button>
{/* 😭 Error, `disabled` doesnt exist on anchor element */}
<Button disabled href="x">
Test
</Button>
</>
);
}

How to implement: Use type guards!

// Button props
type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
href?: undefined;
};

// Anchor props
type AnchorProps = React.AnchorHTMLAttributes<HTMLAnchorElement> & {
href?: string;
};

// Input/output options
type Overload = {
(props: ButtonProps): React.JSX.Element;
(props: AnchorProps): React.JSX.Element;
};

// Guard to check if href exists in props
const hasHref = (props: ButtonProps | AnchorProps): props is AnchorProps =>
"href" in props;

// Component
const Button: Overload = (props: ButtonProps | AnchorProps) => {
// anchor render
if (hasHref(props)) return <a {...props} />;
// button render
return <button {...props} />;
};

View in the TypeScript Playground

Components, and JSX in general, are analogous to functions. When a component can render differently based on their props, it's similar to how a function can be overloaded to have multiple call signatures. In the same way, you can overload a function component's call signature to list all of its different "versions".

A very common use case for this is to render something as either a button or an anchor, based on if it receives a href attribute.

type ButtonProps = React.JSX.IntrinsicElements["button"];
type AnchorProps = React.JSX.IntrinsicElements["a"];

// optionally use a custom type guard
function isPropsForAnchorElement(
props: ButtonProps | AnchorProps
): props is AnchorProps {
return "href" in props;
}

function Clickable(props: ButtonProps | AnchorProps) {
if (isPropsForAnchorElement(props)) {
return <a {...props} />;
} else {
return <button {...props} />;
}
}

They don't even need to be completely different props, as long as they have at least one difference in properties:

type LinkProps = Omit<React.JSX.IntrinsicElements["a"], "href"> & {
to?: string;
};

function RouterLink(props: LinkProps | AnchorProps) {
if ("href" in props) {
return <a {...props} />;
} else {
return <Link {...props} />;
}
}
Approach: Generic Components

Here is an example solution, see the further discussion for other solutions. thanks to @jpavon

interface LinkProps {}
type AnchorProps = React.AnchorHTMLAttributes<HTMLAnchorElement>;
type RouterLinkProps = Omit<NavLinkProps, "href">;

const Link = <T extends {}>(
props: LinkProps & T extends RouterLinkProps ? RouterLinkProps : AnchorProps
) => {
if ((props as RouterLinkProps).to) {
return <NavLink {...(props as RouterLinkProps)} />;
} else {
return <a {...(props as AnchorProps)} />;
}
};

<Link<RouterLinkProps> to="/">My link</Link>; // ok
<Link<AnchorProps> href="/">My link</Link>; // ok
<Link<RouterLinkProps> to="/" href="/">
My link
</Link>; // error
Approach: Composition

If you want to conditionally render a component, sometimes is better to use React's composition model to have simpler components and better to understand typings:

type AnchorProps = React.AnchorHTMLAttributes<HTMLAnchorElement>;
type RouterLinkProps = Omit<AnchorProps, "href">;

interface ButtonProps {
as: React.ComponentClass | "a";
children?: React.ReactNode;
}

const Button: React.FunctionComponent<ButtonProps> = (props) => {
const { as: Component, children, ...rest } = props;
return (
<Component className="button" {...rest}>
{children}
</Component>
);
};

const AnchorButton: React.FunctionComponent<AnchorProps> = (props) => (
<Button as="a" {...props} />
);

const LinkButton: React.FunctionComponent<RouterLinkProps> = (props) => (
<Button as={NavLink} {...props} />
);

<LinkButton to="/login">Login</LinkButton>;
<AnchorButton href="/login">Login</AnchorButton>;
<AnchorButton href="/login" to="/test">
Login
</AnchorButton>; // Error: Property 'to' does not exist on type...

You may also want to use Discriminated Unions, please check out Expressive React Component APIs with Discriminated Unions.

Here is a brief intuition for Discriminated Union Types:

type UserTextEvent = {
type: "TextEvent";
value: string;
target: HTMLInputElement;
};
type UserMouseEvent = {
type: "MouseEvent";
value: [number, number];
target: HTMLElement;
};
type UserEvent = UserTextEvent | UserMouseEvent;
function handle(event: UserEvent) {
if (event.type === "TextEvent") {
event.value; // string
event.target; // HTMLInputElement
return;
}
event.value; // [number, number]
event.target; // HTMLElement
}
Take care: TypeScript does not narrow the type of a Discriminated Union on the basis of typeof checks. The type guard has to be on the value of a key and not it's type.
type UserTextEvent = { value: string; target: HTMLInputElement };
type UserMouseEvent = { value: [number, number]; target: HTMLElement };
type UserEvent = UserTextEvent | UserMouseEvent;
function handle(event: UserEvent) {
if (typeof event.value === "string") {
event.value; // string
event.target; // HTMLInputElement | HTMLElement (!!!!)
return;
}
event.value; // [number, number]
event.target; // HTMLInputElement | HTMLElement (!!!!)
}

The above example does not work as we are not checking the value of event.value but only it's type. Read more about it microsoft/TypeScript#30506 (comment)

Discriminated Unions in TypeScript can also work with hook dependencies in React. The type matched is automatically updated when the corresponding union member based on which a hook depends, changes. Expand more to see an example usecase.

import { useMemo } from "react";

interface SingleElement {
isArray: true;
value: string[];
}
interface MultiElement {
isArray: false;
value: string;
}
type Props = SingleElement | MultiElement;

function Sequence(p: Props) {
return useMemo(
() => (
<div>
value(s):
{p.isArray && p.value.join(",")}
{!p.isArray && p.value}
</div>
),
[p.isArray, p.value] // TypeScript automatically matches the corresponding value type based on dependency change
);
}

function App() {
return (
<div>
<Sequence isArray={false} value={"foo"} />
<Sequence isArray={true} value={["foo", "bar", "baz"]} />
</div>
);
}
See this in TS Playground

In the above example, based on the isArray union member, the type of the value hook dependency changes.

To streamline this you may also combine this with the concept of User-Defined Type Guards:

function isString(a: unknown): a is string {
return typeof a === "string";
}

Read more about User-Defined Type Guards in the Handbook.

Narrowing using extends

See this quick guide: https://twitter.com/mpocock1/status/1500813765973053440?s=20&t=ImUA-NnZc4iUuPDx-XiMTA

Props: One or the other but not both

Since TypeScript is a structural type system, minimum property requirements are described rather than exact, which makes it a bit more difficult to disallow certain props compared to what you might think. never can be used to allow either one or another prop, but not both:

type Props1 = { foo: string; bar?: never };
type Props2 = { bar: string; foo?: never };

const OneOrTheOther = (props: Props1 | Props2) => {
if ("foo" in props && typeof props.foo === "string") {
// `props.bar` is of type `undefined`
return <>{props.foo}</>;
}
// `props.foo` is of type `undefined`
return <>{props.bar}</>;
};
const Component = () => (
<>
<OneOrTheOther /> {/* error */}
<OneOrTheOther foo="" /> {/* ok */}
<OneOrTheOther bar="" /> {/* ok */}
<OneOrTheOther foo="" bar="" /> {/* error */}
</>
);

View in the TypeScript Playground.

A better alternative might be to use a discriminant prop like this:

type Props1 = { type: "foo"; foo: string };
type Props2 = { type: "bar"; bar: string };

const OneOrTheOther = (props: Props1 | Props2) => {
if (props.type === "foo") {
// `props.bar` does not exist
return <>{props.foo}</>;
}
// `props.foo` does not exist
return <>{props.bar}</>;
};
const Component = () => (
<>
<OneOrTheOther type="foo" /> {/* error */}
<OneOrTheOther type="foo" foo="" /> {/* ok */}
<OneOrTheOther type="foo" bar="" /> {/* error */}
<OneOrTheOther type="foo" foo="" bar="" /> {/* error */}
<OneOrTheOther type="bar" /> {/* error */}
<OneOrTheOther type="bar" foo="" /> {/* error */}
<OneOrTheOther type="bar" bar="" /> {/* ok */}
<OneOrTheOther type="bar" foo="" bar="" /> {/* error */}
</>
);

View in the TypeScript Playground.

Props: Pass nothing or all

Passing no props is equivalent to passing an empty object. However, the type for an empty object is not {}, which you might think. Make sure you understand what empty interface, {} and Object means. Record<string, never> is probably the closest you can get to an empty object type, and is recommended by typescript-eslint. Here's an example of allowing "nothing or all":

type Nothing = Record<string, never>;

interface All {
a: string;
b: string;
}

const NothingOrAll = (props: Nothing | All) => {
if ("a" in props) {
return <>{props.b}</>;
}
return <>Nothing</>;
};

const Component = () => (
<>
<NothingOrAll /> {/* ok */}
<NothingOrAll a="" /> {/* error */}
<NothingOrAll b="" /> {/* error */}
<NothingOrAll a="" b="" /> {/* ok */}
</>
);

While this works, representing and empty object with Record<string, never> is not officially recommended. It might be better approaching this in another way, to avoid trying to type "an exactly empty object". One way is grouping the required props in an optional object:

interface Props {
obj?: {
a: string;
b: string;
};
}

const NothingOrAll = (props: Props) => {
if (props.obj) {
return <>{props.obj.a}</>;
}
return <>Nothing</>;
};

const Component = () => (
<>
<NothingOrAll /> {/* ok */}
<NothingOrAll obj={{ a: "" }} /> {/* error */}
<NothingOrAll obj={{ b: "" }} /> {/* error */}
<NothingOrAll obj={{ a: "", b: "" }} /> {/* ok */}
</>
);

Another way is to make both props optional and then check that either none or all props are passed at runtime.

Props: Pass One ONLY IF the Other Is Passed

Say you want a Text component that gets truncated if truncate prop is passed but expands to show the full text when expanded prop is passed (e.g. when the user clicks the text).

You want to allow expanded to be passed only if truncate is also passed, because there is no use for expanded if the text is not truncated.

Usage example:

const App = () => (
<>
{/* these all typecheck */}
<Text>not truncated</Text>
<Text truncate>truncated</Text>
<Text truncate expanded>
truncate-able but expanded
</Text>
{/* TS error: Property 'truncate' is missing in type '{ children: string; expanded: true; }' but required in type '{ truncate: true; expanded?: boolean | undefined; }'. */}
<Text expanded>truncate-able but expanded</Text>
</>
);

You can implement this by function overloads:

import { ReactNode } from "react";

interface CommonProps {
children?: ReactNode;
miscProps?: any;
}

type NoTruncateProps = CommonProps & { truncate?: false };

type TruncateProps = CommonProps & { truncate: true; expanded?: boolean };

// Function overloads to accept both prop types NoTruncateProps & TruncateProps
function Text(props: NoTruncateProps): React.JSX.Element;
function Text(props: TruncateProps): React.JSX.Element;
function Text(props: CommonProps & { truncate?: boolean; expanded?: boolean }) {
const { children, truncate, expanded, ...otherProps } = props;
const classNames = truncate ? ".truncate" : "";
return (
<div className={classNames} aria-expanded={!!expanded} {...otherProps}>
{children}
</div>
);
}

Props: Omit prop from a type

Note: Omit was added as a first class utility in TS 3.5! 🎉

Sometimes when intersecting types, we want to define our own version of a prop. For example, I want my component to have a label, but the type I am intersecting with also has a label prop. Here's how to extract that out:

export interface Props {
label: React.ReactNode; // this will conflict with the InputElement's label
}

// this comes inbuilt with TS 3.5
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

// usage
export const Checkbox = (
props: Props & Omit<React.HTMLProps<HTMLInputElement>, "label">
) => {
const { label } = props;
return (
<div className="Checkbox">
<label className="Checkbox-label">
<input type="checkbox" {...props} />
</label>
<span>{label}</span>
</div>
);
};

When your component defines multiple props, chances of those conflicts increase. However you can explicitly state that all your fields should be removed from the underlying component using the keyof operator:

export interface Props {
label: React.ReactNode; // conflicts with the InputElement's label
onChange: (text: string) => void; // conflicts with InputElement's onChange
}

export const Textbox = (
props: Props & Omit<React.HTMLProps<HTMLInputElement>, keyof Props>
) => {
// implement Textbox component ...
};

As you can see from the Omit example above, you can write significant logic in your types as well. type-zoo is a nice toolkit of operators you may wish to check out (includes Omit), as well as utility-types (especially for those migrating from Flow).

Props: Extracting Prop Types of a Component

Sometimes you want the prop types of a component, but it isn't exported.

A simple solution is to use React.ComponentProps:

// a Modal component defined elsewhere
const defaultProps: React.ComponentProps<typeof Modal> = {
title: "Hello World",
visible: true,
onClick: jest.fn(),
};

There are advanced edge cases if you want to extract the prop types of a component taking into account internal props, propTypes, and defaultProps - check our issue here for helper utilities that resolve these.

Props: Render Props

Advice: Where possible, you should try to use Hooks instead of Render Props. We include this merely for completeness.

Sometimes you will want to write a function that can take a React element or a string or something else as a prop. The best Type to use for such a situation is ReactNode which fits anywhere a normal, well, React Node would fit:

import { ReactNode } from "react";

interface Props {
label?: ReactNode;
children?: ReactNode;
}

const Card = ({ children, label }: Props) => {
return (
<div>
{label && <div>{label}</div>}
{children}
</div>
);
};

If you are using a function-as-a-child render prop:

import { ReactNode } from "react";

interface Props {
children: (foo: string) => ReactNode;
}

Something to add? File an issue.

Handling Exceptions

You can provide good information when bad things happen.

class InvalidDateFormatError extends RangeError {}
class DateIsInFutureError extends RangeError {}

/**
* // optional docblock
* @throws {InvalidDateFormatError} The user entered date incorrectly
* @throws {DateIsInFutureError} The user entered date in future
*
*/
function parse(date: string) {
if (!isValid(date))
throw new InvalidDateFormatError("not a valid date format");
if (isInFuture(date)) throw new DateIsInFutureError("date is in the future");
// ...
}

try {
// call parse(date) somewhere
} catch (e) {
if (e instanceof InvalidDateFormatError) {
console.error("invalid date format", e);
} else if (e instanceof DateIsInFutureError) {
console.warn("date is in future", e);
} else {
throw e;
}
}

View in TypeScript Playground

Simply throwing an exception is fine, however it would be nice to make TypeScript remind the consumer of your code to handle your exception. We can do that just by returning instead of throwing:

function parse(
date: string
): Date | InvalidDateFormatError | DateIsInFutureError {
if (!isValid(date))
return new InvalidDateFormatError("not a valid date format");
if (isInFuture(date)) return new DateIsInFutureError("date is in the future");
// ...
}

// now consumer *has* to handle the errors
let result = parse("mydate");
if (result instanceof InvalidDateFormatError) {
console.error("invalid date format", result.message);
} else if (result instanceof DateIsInFutureError) {
console.warn("date is in future", result.message);
} else {
/// use result safely
}

// alternately you can just handle all errors
if (result instanceof Error) {
console.error("error", result);
} else {
/// use result safely
}

You can also describe exceptions with special-purpose data types (don't say monads...) like the Try, Option (or Maybe), and Either data types:

interface Option<T> {
flatMap<U>(f: (value: T) => None): None;
flatMap<U>(f: (value: T) => Option<U>): FormikOption<U>;
getOrElse(value: T): T;
}
class Some<T> implements Option<T> {
constructor(private value: T) {}
flatMap<U>(f: (value: T) => None): None;
flatMap<U>(f: (value: T) => Some<U>): Some<U>;
flatMap<U>(f: (value: T) => Option<U>): Option<U> {
return f(this.value);
}
getOrElse(): T {
return this.value;
}
}
class None implements Option<never> {
flatMap<U>(): None {
return this;
}
getOrElse<U>(value: U): U {
return value;
}
}

// now you can use it like:
let result = Option(6) // Some<number>
.flatMap((n) => Option(n * 3)) // Some<number>
.flatMap((n = new None())) // None
.getOrElse(7);

// or:
let result = ask() // Option<string>
.flatMap(parse) // Option<Date>
.flatMap((d) => new Some(d.toISOString())) // Option<string>
.getOrElse("error parsing string");