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:
- The type of the first function argument
- The type of the
propTypes
static property - 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