A Tour of Utility Types in React

keyof

type ObjectLiteralType = {
  first: 1;
  second: 2;
};

// Inferred Type: "first" | "second"
type Result = keyof ObjectLiteralType;

Getting The Type Of A Single Key In An Object

Use the index operator.

type Obj = {
  0: "a";
  1: "b";
  prop0: "c";
  prop1: "d";
};

// Inferred Type: "c"
type Result0 = Obj["prop0"];

// Inferred Type: "a" | "b"
type Result1 = Obj[0 | 1];

// Inferred Type: "c" | "d"
type Result2 = Obj["prop0" | "prop1"];

What About Getting The Values?

It's not as clean, but it'll work.

type Obj = {
  a: "A";
  b: "B";
  c: number;
};

// Inferred Type: number | "A" | "B"
type Values = Obj[keyof Obj];

Unions

type A = "a" | "b" | "c";
type B = "b" | "c" | "d";

// Inferred Type: "a" | "b" | "c" | "d"
type Union = A | B;

Unions With Objects

type ObjectTypeA = {
  firstProp: number;
  sharedProp: string;
};

type ObjectTypeB = {
  secondProp: boolean;
  sharedProp: string;
};

type Union = ObjectTypeA | ObjectTypeB;

You have to check for anything that is not shared between both.

Intersections

This is useful when trying to combine the props that you're going to use for a React component, just sayin'.

type A = "a" | "b" | "c";
type B = "b" | "c" | "d";

// Inferred Type: "b" | "c"
type Intersection = A & B;

Conditionals

Ternaries only.

type Wrap<T> = T extends { length: number } ? [T] : T;

Conditionals: Example

There isn't much of a good reason for this one to exist, but it helps explain the syntax a bit, so here we are.

type IsAssignableTo<A, B> = A extends B ? true : false;

// Type `123` is assignable to type `number`
// Inferred Type: true
type Result1 = IsAssignableTo<123, number>;

// Type `number` is not assignable to type `123`
// Inferred Type: false
type Result2 = IsAssignableTo<number, 123>;

Exclude

Takes stuff out of a union. It's built into TypeScript, but here is also what it would look like if you wanted to implement it yourself.

type Exclude<T, U> = T extends U ? never : T;

// Inferred Type: 1 | 3
type Result0 = Exclude<1 | 2 | 3, 2>;

// Inferred Type: "a" | "b"
type Result1 = Exclude<1 | "a" | 2 | "b", number>;

// Inferred Type: "a" | 2
type Result2 = Exclude<1 | "a" | 2 | "b", 1 | "b" | "c">;

Extract

The opposite of Exclude.

type Extract<T, U> = T extends U ? T : never;

// Inferred Type: 1 | 2
type Result1 = Extract<1 | "a" | 2 | "b", number>;

// Inferred Type: 1 | "b"
type Result2 = Extract<1 | "a" | 2 | "b", 1 | "b" | "c">;

Objects

type ObjectWithAKey = { a: string };

You can also define a type for keys as well.

type ObjectWithStringKeys = { [key: string]: number };

You can iterate over a union if you want.

// Inferred Type: { a: number; b: number; c: number; }
type Result = {
  [K in "a" | "b" | "c"]: number;
};

type Mask = {
  [K in keyof ObjectLiteralType]: boolean;
};

Pick

type ObjectLiteralType = {
  john: 1;
  paul: 2;
  george: 3;
  ringo: 4;
};

// Inferred Type: { george: 2; ringo: 4; }
type Result = Pick<ObjectLiteralType, "george" | "ringo">;

Omit

Literally the opposite of Pick

type ObjectLiteralType = {
  john: 1;
  paul: 2;
  george: 3;
  ringo: 4;
};

// Inferred Type: { john: 1; paul: 2; }
type Result = Omit<ObjectLiteralType, "george" | "ringo">;

String Manipulation Utilities

type UppercaseWes = Uppercase<"wes">;
type LowercaseWes = Lowercase<"Wes">;
type CapitalizeWes = Capitalize<"wes">;
type UncapitalizeWes = Uncapitalize<"Wes">;

React.HTMLProps<HTMLXXXElement>

A type representing Props of specified HTML element. Useful for extending HTML Elements.

const Input = (props: <Props & React.HTMLProps<HTMLInputElement>) => {
  // …
}

<Input about={...} accept={...} alt={...} ... />

React.ComponentProps<typeof XXX>

We'll use this one in the very next exercise—just sayin'.

type MyComponentProps = React.ComponentProps<typeof MyComponent>;

Generic List Component

import * as React from "react";

export interface GenericListProps<T> {
  items: T[];
  itemRenderer: (item: T) => JSX.Element;
}

export class GenericList<T> extends React.Component<GenericListProps<T>, {}> {
  render() {
    const { items, itemRenderer } = this.props;

    return <div>{items.map(itemRenderer)}</div>;
  }
}