TypeScript is a natural extension of JavaScript that’s used in many projects in place of JavaScript.
However, not everyone knows how it actually works.
In this article, we’ll look at the use of implicit any
types, unions types, and type assertions.
Using Implicitly Defined Any Types
any
types can be implicitly defined if we allow it in the compiler options.
If we set noImplicitAny
to false
, then we can have implicit any
types.
This makes it easier to selectively TypeScript in an existing JavaScript project.
It also simplifies working with 3rd-party JavaScript packages.
For instance, we can write:
const getTax = (price) => {
return (price * 0.2).toFixed(2);
};
Then we removed all the type annotations.
Disabling Implicit Any Types
Even though we can disable the noImplicitAny
option, it’s probably not a good idea with new projects.
We set noImplicitAny
to true
so that it’ll check for any implicit any
types in parameters and variables
For instance, if we set it to true
, then we’ll know that we’re writing any code that has an implicit any
type since the compiler will warn us and won’t build the code.
Using Type Unions
TypeScript allows for flexibility in our data type restrictions.
One way to make our data types more flexible is the use of union types.
We can separate each type we want to join together with |
.
Union types mean that we can assign something that can be any one of the types listed.
For instance, we can write:
const getTax = (price: number, format: boolean): string | number => {
if (format) {
return (price * 0.2).toFixed(2);
}
return price * 0.2;
};
In the code above, we have getTax
, which has the return string | number
, which means it can return either a string or a number.
In the body, we return a string if format
is true
and a number otherwise.
The return type is restricted to either of those types.
Type Assertions
Type assertions tell the TypeScript compiler to treat a value as a specific type.
It’s also called type narrowing.
We can narrow a union with type assertions.
To narrow types, we can use the as
operator.
For instance, given that we have the getTax
function:
const getTax = (price: number, format: boolean): string | number => {
if (format) {
return (price * 0.2).toFixed(2);
}
return price * 0.2;
};
We can add type assertions by writing:
const getTax = (price: number, format: boolean): string | number => {
if (format) {
return (price * 0.2).toFixed(2) as string;
}
return (price * 0.2) as number;
};
In case people aren’t aware that toFixed
returns a string, we can use as
to make that clear.
Also, we can write:
const tax: string = getTax(100, true) as string;
to narrow the type of value returned by getTax
to a string.
Likewise, we can do the same with number:
const tax: number = getTax(100, false) as number;
Asserting to an Unexpected Type
We can’t assert a type to something other than the expected types.
In the getTax
function, we should only return a string or number.
So if we try to assert it to anything else like:
const tax: boolean = getTax(100, false) as boolean;
We’ll get an error from the TypeScript compiler.
Alternative Type Assertion Syntax
We can use brackets to add type assertions instead of the as
operator.
For instance, we can write:
const tax: number = <number>getTax(100, false);
instead of :
const tax: number = getTax(100, false) as number;
They’re the same.
Type Guard
The typeof
keyword can be used to test a specific type without needing type assertion.
For instance, we can write:
const getTax = (price: number, format: boolean): string | number => {
if (typeof price !== "number") {
return 0;
}
if (format) {
return (price * 0.2).toFixed(2) as string;
}
return (price * 0.2) as number;
};
We have:
if (typeof price !== "number") {
return 0;
}
to check if price
is a number. If it’s not, we return 0. Otherwise, we proceed with the rest of the code.
Conclusion
We can use union types to let us assign a variable or return something with different types.
Also, we can narrow the types with the as
keyword or brackets to make the type of something clear.