Can anyone explain why anyone needs angle brackets when they can use colon, which is much more readable?

Using Angle brackets example:

Table.jsx

import React, { FunctionComponent } from 'react';

export interface Props {
  filtered: string[][]
};

const Table: FunctionComponent<Props> = ({ filtered }) => {
  return (
    <table />
  )
};

export default Table;

Using props example:

Table.tsx

import React from 'react';

export interface Props {
  filtered: string[][]
};

const Table = ({ filtered }: Props) => {
  return (
    <table />
  )
};

export default Table;

Why use angle brackets when colon does the job? What is the real benefit of using angle brackets, and why would I choose it over just using colon?

Thanks


Solution 1:

Edit: "Why use a generic type?"

A)

One reason is to be able to use the provided type in multiple ways. For example, in the official type definition from React:

interface FunctionComponent<P = {}> {
    (props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null;
    propTypes?: WeakValidationMap<P>;
    contextTypes?: ValidationMap<any>;
    defaultProps?: Partial<P>;
    displayName?: string;
}

Because it is defined using a generic, the FunctionComponent interface "knows" the following things about implementations of the interface:

  1. The type of the first function argument
  2. The type of the propTypes static property
  3. The type of the defaultProps static property

B)

Another reason is that the alternative gets messy fairly quickly. Take the following as a simplified version of the FunctionComponent interface, implemented without generics:

import React from 'react';

export interface FunctionComponentProps {
  children: React.ReactNode;
}

export interface FunctionComponent {
    (props: FunctionComponentProps, context?: any): React.ReactElement<any, any> | null;
}

We'd run into problems when we want to use that type:

export interface MyComponentProps extends FunctionComponentProps {
  children: boolean; // invalid - can't change interface prop type
  className: string;
}

export interface MyComponent extends FunctionComponent {
  // invalid - same problem, cannot redeclare function signature
  (props: MyComponentProps, context?: any): React.ReactElement<any, any> | null;
}

// inferred types of children and classNames is any because of the re-declarations
export const Button: MyComponent = ({ children, className }) => {
  return <button className={className}>{children ? 'YES' : 'NO'}</button>;
};

It "works" if your custom types do not extend the provided type, but you'll end up with an awful lot of useless boilerplate code if you re-invent the wheel for every component.

export interface MyComponentProps {
  children: boolean;
  className: string;
}

export interface MyComponent {
  (props: MyComponentProps, context?: any): React.ReactElement<any, any> | null;
}

export const Button: MyComponent = ({ children, className }) => {
  return <button className={className}>{children ? 'YES' : 'NO'}</button>;
};

Original Answer

The angle bracket syntax provides more information to the type checking system, so the benefits come in the form of better type checking.

For example:

Table.tsx

import React, { FunctionComponent } from "react";

export interface Props {
  filtered: string[][];
}

const Table: FunctionComponent<Props> = ({ filtered }) => {
  console.log("filtered = ", filtered);
  // Type check fails because a ReactNode is not returned
  //return <table />;
};

Table.defaultProps = {
  // Type check fails because null is not a valid value for filtered
  filtered: null
};

export default Table;

Table_bad.tsx

import React from "react";

export interface Props {
  filtered: string[][];
}

const Table = ({ filtered }: Props) => {
  console.log("filtered = ", filtered);
  // Typescript does not know enough to complain that the render function does not return a ReactNode
  //return <table />;
};

Table.defaultProps = {
  // Typescript does not know that defaultProps should implement Props
  filtered: null
};

export default Table;

Demo

https://codesandbox.io/s/so-61508203-56xum?file=/src/components/Table_bad.tsx