Basic Types vs Interfaces vs Type Aliases
Understanding the differences between Basic Types, Interfaces, and Type Aliases in TypeScript with practical examples.
Hello! TypeScript is a language that allows multiple custom types to be created for later use. There are 3 main types we must know when writing any code. Those would be Basic Types, Interfaces, and Type Aliases. In this article, we will go through each one, with a few basic examples, and why you might want to use each one.
Basic Types
First, we begin with Basic Types. These are the generic types that TypeScript comes built in with that we use as the basic building blocks. We use these everyday, and they are all very recognizable. The most common types are: string , number, and boolean , with string representing a string value like “apples”, a number representing lets say 100, and boolean which represents either true or false. We also have access to arrays, which can be signified in TypeScript with string[]; meaning a array of strings or number[]; meaning an array of numbers. You can append any sort of type on there, so it doesn’t just have to be a basic type, we can also use complex interfaces!
const num: number = 0; // This will always be a number!
const str: string = 'foobar'; // This will always be a string!
const isValid: boolean = false; // this will always be either true or false!
const catNameArr: string[] = ['Johnny', 'Ms. Muffins', 'Dandy']; // This will always be an array of strings!
As shown above, you can denote the type by adding : typename to the end of the variable name - this will let the compiler know that you are wanting to cast that as the type described! TypeScript is also pretty smart, it can also infer the type based on the declaration, so you don’t always need to explicitly type everything! You should always try to type anything that does not have a type though!
”Any” Type
TypeScript also includes a special type, named any that will let you bypass type checking errors. This is usually bad practice, I do not recommend including any in your code, but be my guest! Sometimes it is useful if you don’t want to spend time convincing the compiler that the piece of code you wrote is type compliant, but it is still best to try and avoid this as much as possible.
const complexThing: any = 'cat'; // The compiler will read this as any even though we assigned it as a string!
Type Inference
TypeScript can also be pretty smart, and automatically pick up on types if the value is already defined! In this case, you won’t have to include the type and it will pick up what the type is!
const count = 5; // inferred as a number!
Object Types
Sometimes, we want to define our objects as well. TypeScript has us covered here! We can define Object types like so:
const specialObj: { isEnabled: boolean; damage?: number } = { isEnabled: true, damage: 1 };
From above, we can see that the variable specialObj is being assigned an object with the keys isEnabled with the value boolean and damage key with the value number. This is now telling us that the object specifically wants these 2 fields with these two types. If you also noticed, damage has a little ? right next to it ; that tells the compiler that this property is optional!
Interfaces
Having all of these types is nice, but what if we want to create something more? What if we have multiple values that are complex objects, that are all similarly typed? That is where interfaces come into play! Interfaces are a way we can define complex types to use anywhere in our application! We typically want to use these when defining the structure or “shape” of objects or data types. That way, if we expect some sort of complex object, we can reference back to the interface and know what we are expecting back from that API!
interface Enemy { // We want our enemies to follow this structure, good to use interface!
health: number;
name: string;
damage: number;
isUnique?: boolean; // Optional properties here!
}
interface Slime extends Enemy { // Here we extend Enemy to make a "Slime" with a special attack!
specialAttack: () => void;
}
const slimeEnemy: Slime = { health: 10, name: "Slime", damage: 1, specialAttack: () => void; }
Type Aliases
Next, we have type aliases! This lets you create what is essentially a new type! We can go pretty crazy when it comes to type, giving it all sorts of properties. We are also able to use unions with types, giving the ability to potentially have differing values. It lets us generate more complex structures that we can use in order to type a lot more things in our applications and make our lives easier!
type Balance = string | number; // Sometimes maybe our API returns a string or a number for the balance, we can account for that here!
type Sword = {
damage: number;
name: string;
};
const coolSword: Sword = { damage: 10, name: 'Cool Sword' };
When to use Type vs Interfaces?
It is important here to also mention that most people tend to use these interchangeably. Most of the time, they look very similar, and seemingly do the same thing. To keep it simple, there are rules to follow when choosing to create a type or interface. If you are needing to define the shape of an object or class that might be extended or implemented, you would use an interface. If you need to use something like a union or intersection, you would use a type!
type AccountBalance = string | number; // Here a union is required, so we would define this as a type
interface BankAccount {
// Here we are defining a structure we want a bank account to follow, so a interface is good here!
balance: number;
id: string;
transactions: Transactions[];
}