Let's go fix another file. Details.tsx. Before we get into the file, make a new file called APIResponsesTypes.ts. In there put

export type Animal = "dog" | "cat" | "bird" | "reptile" | "rabbit";

export interface Pet {
  id: number;
  name: string;
  animal: Animal;
  description: string;
  breed: string;
  images: string[];
  city: string;
  state: string;
}

export interface PetAPIResponse {
  numberOfResults: number;
  startIndex: number;
  endIndex: number;
  hasNext: boolean;
  pets: Pet[];
}
  • This allows us to resuse these response types anywhere we reference the API responses and have an enforceable shape that TypeScript knows what to do with.
  • We made Animal a type instead of an interface. This confuses people a lot and the sum of the answer is it frequently doesn't matter which you choose. The general advice is "use interfaces unless you need type aliases". Here we wanted to have a type alias that just allows a few different strings, something an interface can't do but a type can.

Onto Details. Rename it Details.tsx

// imports
import { PetAPIResponse, Animal } from "./APIResponsesTypes";

class Details extends Component<{ params: { id?: string }}> { … }

// replace state
state = {
  loading: true,
  showModal: false,
  animal: "" as Animal,
  breed: "",
  city: "",
  state: "",
  description: "",
  name: "",
  images: [] as string[],
};

// inside componentDidMount
const json = (await res.json()) as PetAPIResponse;

// add href to toggleModal
adopt = () => (window.location.href = "http://bit.ly/pet-adopt");

// error boundary
const WrappedDetails = () => {
  const params = useParams<{ id: string }>();
  return (
    <ErrorBoundary>
      <Details params={params} />
    </ErrorBoundary>
  );
};
  • We need to tell TypeScript what props each component expects. Now when you import that component elsewhere, TS will make sure the consumer passes all the right props in.
  • We have to give all state a default setting. This prevents errors on the initial render and it gives TypeScript the ability to infer all your types.
  • It can't tell what type photos is so we tell it's an array of strings from the pet library.
  • We had to add .href to the end of location. Technically that API expect a Location object but it just works with a string. With TS we need to be a bit more adherent to the spec so we'll do it the correct by setting .href.
  • TS still won't be happy because our other pages haven't been typed yet. We're getting there.
  • Technically the id param can come back as undefined. We have to provide for that with the ? in the params.

🏁 Click here to see the state of the project up until now: typescript-2