title | date | description | tags | ||
---|---|---|---|---|---|
React with TypeScript v3.7 |
2019-12-29 |
How some of the new features of TypeScript v3.7 work with React. |
|
With version 3.7, TypeScript has released some powerful new features. These dramatically improve React dev experience, so I thought I'd touch on a few of them, especially now that I've been using them in production code for a bit.
Essentially, optional chaining allows you to attempt to access nested properties that may or may not exist. Specifically, if you run into a null or undefined, it will prevent you from accessing any deeper, rather than throwing an error. You may have seen errors like these before:
Uncaught TypeError: Cannot read property 'c' of undefined
Uncaught TypeError: a.b.c is not a function
And to counter that perhaps you did something like this:
if (a && a.b && a.b.c) {
c();
}
Well now you can do this and it has the same effect:
a?.b?.c?.();
One example of how this effects React development would be with Redux stores. Let's say that the store has a user
, which is either null if no one is signed in, or an object describing the user. Keep in mind this isn't Redux specific -- this could be a context or even just local state.
interface User {
id: string;
firstName: string;
lastName: string;
address?: {
street?: string;
city?: string;
state?: string;
country?: string;
postal?: string;
};
}
If we want to display the country as a title, we can do so like this:
const title: string | undefined = useSelector(
(s: State) => s.user?.address?.country,
);
Nullish coalescing allows you to fallback on a value whenever something is undefined or null. For example, if you have a value that could be undefined or null, and you want a default for it, you can use the ??
operator like this:
function getTitle(title?: string): string {
return title ?? 'Default title';
}
One way this can be used is as a fallback for optional chaining. Since optional chaining will "exit early" if it encounters a null or undefined, you can add the ??
operator to the end to allow for fallbacks:
const title: string = useSelector(
(s: State) => s.user?.address?.country ?? 'Canada',
);
One more advanced case is when you want to create a component that is optionally controlled. A good example of this is the base HTML input
. If you provide the value
prop, then typing in the input
won't change anything unless you also update the value
. However, if you do not provide a value
, the input
contains its own internal state, and typing will update the value.
Let's do something similar. We will create a button which, when clicked, will update the count shown inside of it. It takes a prop count
and a callback onClick
. If count
is given, it will be a controlled component, but if not it will fallback to it's own internal state.
interface CountButtonProps {
count?: number;
onClick?: (newCount: number) => void;
}
const CountButton = (props: CountButtonProps) => {
const [stateCount, setStateCount] = useState(0);
const count = props.count ?? stateCount;
const onClick = () => {
setStateCount(count + 1);
props.onClick?.(count + 1);
};
return <button onClick={onClick}></button>;
};
As you can see, the inner state will only be used as a fallback when props.count is undefined. This is important because previously, you may have been tempted to do the following:
const count = props.count || stateCount;
However, if props.count
was 0
, this would have still defaulted to stateCount
, giving us incorrect behavior (since 0 is falsy). The ??
operator is much more effective whenever you're dealing with numbers which could be 0
, or strings that could be ''
.
That just about covers it. Obviously the 3.7 release featured a bunch of useful stuff, but optional chaining and nullish coalescing were two features that have the largest impact on React development. I hope that these new features help with your React development as well.