Advanced Types

In this section we will discuss some advanced concepts and techniques that would helps to improve your TypeScript code.

Type Guard

TypeScript is aware of your if check and able to narrow down the type. Consider the following example:

ts
const findEvenNumberAndDivideIt = (numbers: number[]): number => {
const evenNumber = numbers.find((num) => num % 2 === 0);
return evenNumber / 2; // Hey, this could be undefined!
};
ts
const findEvenNumberAndDivideIt = (numbers: number[]): number => {
const evenNumber = numbers.find((num) => num % 2 === 0);
return evenNumber / 2; // Hey, this could be undefined!
};

The type error of TypeScript is valid. We can fix it by doing a null check:

ts
const findEvenNumberAndDivideIt = (numbers: number[]): number => {
const evenNumber = numbers.find((num) => num % 2 === 0);
if (evenNumber) {
return evenNumber / 2;
}
return 0;
};
ts
const findEvenNumberAndDivideIt = (numbers: number[]): number => {
const evenNumber = numbers.find((num) => num % 2 === 0);
if (evenNumber) {
return evenNumber / 2;
}
return 0;
};

TypeScript is smart enough to know that once you do a null check, the value will no longer be undefined.

Custom Type Guard

Many times you want to abstract away common type check, like isDefined function:

ts
const isDefined = (value: any) => typeof value !== 'undefined';
ts
const isDefined = (value: any) => typeof value !== 'undefined';

Now if we apply to the findEvenNumberAndDivideIt example:

ts
const isDefined = (value: any) => typeof value !== 'undefined';
const findEvenNumberAndDivideIt = (numbers: number[]): number => {
const evenNumber = numbers.find((num) => num % 2 === 0);
if (isDefined(evenNumber)) {
// This could be undefined??? 🤦‍♂️🤦‍♂️
return evenNumber / 2;
}
return 0;
};
ts
const isDefined = (value: any) => typeof value !== 'undefined';
const findEvenNumberAndDivideIt = (numbers: number[]): number => {
const evenNumber = numbers.find((num) => num % 2 === 0);
if (isDefined(evenNumber)) {
// This could be undefined??? 🤦‍♂️🤦‍♂️
return evenNumber / 2;
}
return 0;
};

This is because TypeScript will not go into the implementation of isDefined to figure our the implementation. For TypeScript it is just a function that returns a boolean value.

We can restore the previous typechecking ability by making the isDefined function as custom type guard:

ts
const isDefined = <T,>(value: T | undefined): value is T => typeof value !== 'undefined';
const findEvenNumberAndDivideIt = (numbers: number[]): number => {
const evenNumber = numbers.find((num) => num % 2 === 0);
if (isDefined(evenNumber)) {
// Oh my lord TypeScript is happy again 🙏🙏
return evenNumber / 2;
}
return 0;
};
ts
const isDefined = <T,>(value: T | undefined): value is T => typeof value !== 'undefined';
const findEvenNumberAndDivideIt = (numbers: number[]): number => {
const evenNumber = numbers.find((num) => num % 2 === 0);
if (isDefined(evenNumber)) {
// Oh my lord TypeScript is happy again 🙏🙏
return evenNumber / 2;
}
return 0;
};

Do It: Creating Type Guard

Refactor all the functions in typecheck.ts so that they becomes typeguard.

Type Utilities

Omit

<Form />

<Input />

Pick

  • Opposite of Omit
ts
type ThirdPartyProps = {
color: 'default' | 'success' | 'danger' | 'info' | 'warning';
size?: 'large' | 'small';
children: string;
};
type MyProps = Pick<ThirdPartyProps, 'color' | 'size'>;
ts
type ThirdPartyProps = {
color: 'default' | 'success' | 'danger' | 'info' | 'warning';
size?: 'large' | 'small';
children: string;
};
type MyProps = Pick<ThirdPartyProps, 'color' | 'size'>;