Typescript is a superset of Javascript equipped with static type. Typescript is becoming vastly popular web development language for different Javascript based frameworks and runtimes like React, Angular, Node.js to name a few. In this Article we are going to explore different data types that typescript offers.
Primitive type
There are five primitive types available in typescript
1. Number
To define any type of numeric value whether it’s a floating point number, integer, hexadecimal, binary, or octal “number” is the go-to type in Typescript. Let’s see some examples of how to declare numeric variables in Typescript
let m: number = 20;
let n: number = 34.5;
let p: number = 0xf00d; //Hexadecimal number
let q: number = 0b1010; // Binary number
let r: number = 0o744; // Octal number
2. String
To define any type of string value for a variable, it must be declared as a “string” in Typescript. let’s see some examples of strings
let animal: string = 'Lion';
let education: string = "M.Tech"
3. Boolean
Boolean variables in Typescript are declared as “boolean”. Let’s see examples of how variables are declared as boolean in Typescript
let isActive: boolean = true
let isLoggedin: boolen = false
4. Null
in Typescript null is both a datatype and value . Where as in Javascript null is a value but its type is object. It represents nothingness or empty value. Let’s see how we declare a variable as null in Typescript
let nl: null = null
Null is a subtype of other types , which means null is assignable to number, string etc. But when using strictNullChecks
flag as true in tsconfig.json file then null is only assignable to unknown
, any
and their respective types. it helps to avoid lots of potential errors in your code
5. Undefined
In Typescript undefined is a type as well as value just like null. Like null it also represents nothingness. Lets see an example of undefined
let und: undefined = undefined
Like null, undefined is also a subtype of other types. which means null is assignable to a number, string, etc. But when using the strictNullChecks
flag as true in the tsconfig.json file then undefined is only assignable to unknow, any and void.
Reference Types
Reference types doesn’t have a fixed size and dynamic in nature. There are three types of reference types in Typescript
1. Array
Array in Typescript can hold values of same type. The data types of values can be ‘string’, ‘number’ , objects etc. You can declare an array in typescript in two different ways. Let’s see some examples of how we can declare arrays of different kind in typescript
let numarr: number[] = [11,12,13,14,15]
let numberarr: Array<number> = [20,30,40,50]
let strArr: string[] = ['Welcome','Thank You', 'Bye']
let nameArr: Array<string> =['John','Jay','Joy','Jean']
2. Object
One of the most common types of data that we encounter in Javscript is of Object type. To define an object type variable we simple list the properties with their types
function printLatLang({lat: number,lang: number}): string{
return `The Latitude is ${lat} and Longitude is ${lang}`;
}
3. Tuples
At their core, tuples are similar to arrays. However, while arrays typically have elements of a singular type (e.g., an array of numbers or strings), tuples allow for a combination of types. Each position in a tuple has a designated type, and the order matters.
let person: [string, number];
person = ["John", 30]; // This is valid
person = [30, "Jay"]; // This would raise a type error
Other Available Types in Typescript
1. Any
TypeScript also has a special type, “any"
, that you can use whenever you don’t want a particular value to cause typechecking errors.
let notSure: any = 4;
notSure = "maybe a string";
The any
type is useful when you don’t want to write out a long type just to convince TypeScript that a particular line of code is okay. Generally if you don’t declare a type for a variable in Typescript , the complier infer it as any type. If in tsconfig.json we declare noImplicitAny property as true then Typescript will throw error for any implicit any
2. Void
When there is no type involved void is used. It is most commonly used for function return types when a function doesn’t return any value.
function warnUser(): void {
console.log("This is a warning message");
}
3. Enum
A helpful addition to the standard set of datatypes from JavaScript is the enum
. An enum is a way of giving more friendly names to sets of numeric values.
enum OrderStatus {Pending, Dispatched, Shipped, Delivered, Cancelled}
let o: OrderStatus = OrderStatus.Shipped;
By default, enums begin numbering their members starting at 0
. But you can manually set the values of the enum
enum OrderStatus={
Pending = 1
Dispatched = 2
Shipped = 4
Delivered = 3
Cancelled = 7
}
let o: OrderStatus = OrderStatus.Shipped;
4. Unknown
From Typescript version 3.0 and above a special type has been introduced which is ‘unknown’ type. When you don’t know exactly what will be the type of the variable or a return type of a function ‘unknown’ can be used. Though ‘unknown’ and ‘any’ can hold any values but unknown is proffered over any because it provides safer typing.
let x: unknown = 0;
x = '1';
x= false;
5. Union Type
When a particular variable can consume multiple types of values in Typescript we can combine those types while defining the type for the variable
let value: number | string;
value = 123; // ok
value = '123'; // ok
6. Literal Type
In TypeScript, a literal type represents an exact value that a variable can hold. It narrows down the type to a much more specific set of allowable values. Literal types are often combined with union types to represent a value that can be one of several predefined values.
String Literal Types
String literal types allow you to specify the exact string values a string can take.
type Direction = "North" | "South" | "East" | "West";
let move: Direction;
move = "North"; // OK
move = "Northeast"; // Error: Type '"Northeast"' is not assignable to type 'Direction'
Numeric Literal Types
Similar to string literal types, but for numbers.
type DiceValue = 1 | 2 | 3 | 4 | 5 | 6;
let roll: DiceValue;
roll = 4; // OK
roll = 7; // Error: Type '7' is not assignable to type 'DiceValue'
Boolean Literal Types
Boolean types in TypeScript already behave like literal types where they can only be assigned true
or false
. But for completeness’ sake:
type TrueOnly = true;
let isTrue: TrueOnly;
isTrue = true; // OK
isTrue = false; // Error: Type 'false' is not assignable to type 'true'
Template Literal Types (introduced in TypeScript 4.1)
With TypeScript 4.1 and later, you can create new string types by manipulating string literal types using template literal patterns:
type World = "world";
type Greeting = `Hello ${World}`;
const greet: Greeting = "Hello world"; // OK
Along with these types some of the concepts related to types we must know in order to get the whole essence of Typescript
Type Guards
In TypeScript, ensuring a variable conforms to a specific type at runtime becomes essential, especially since JavaScript lacks static typing. Here, the concept of “Type Guards” comes into play. Type guards are expressions that perform runtime type checking, allowing TypeScript to narrow down the type within a specific block.
Common Methods for Type Guarding:
typeof
: It’s one of the basic type guards and checks whether a variable is a certain basic type (string
, number
, boolean
, object
, or function
).
function padLeft(value: string | number, padding: string | number) {
if (typeof padding === "number") {
return Array(padding + 1).join(" ") + value;
}
if (typeof padding === "string") {
return padding + value;
}
}
instanceof
: This guard is used to check if an object is an instance of a class or a constructor.
class Bird {
fly() {
console.log("Bird flies");
}
}
class Fish {
swim() {
console.log("Fish swims");
}
}
function move(animal: Bird | Fish) {
if (animal instanceof Bird) {
animal.fly();
} else {
animal.swim();
}
}
Here, the instanceof
type guard ensures the appropriate method (fly
or swim
) is called based on the passed animal’s actual type.
User-defined Type Guards: When built-in guards aren’t sufficient, you can create your own type guard by defining a function whose return type is a type predicate.
function isFish(animal: Bird | Fish): animal is Fish {
return (animal as Fish).swim !== undefined;
}
function move(animal: Bird | Fish) {
if (isFish(animal)) {
animal.swim();
} else {
animal.fly();
}
}
The animal is Fish
is a type predicate. If isFish
returns true
, TypeScript knows animal
must be of type Fish
within that block.
Type Aliases
Type aliases are a powerful feature in TypeScript’s toolkit, allowing developers to create more readable, maintainable, and organized code. By providing a way to label and encapsulate complex type structures, type aliases streamline code and improve its clarity.
Defining Type Aliases
To create a type alias, you use the type
keyword, followed by your desired name for the type, an equals sign, and then the type definition.
type StringOrNumber = string | number;
let str: StringOrNumber = 'Magic'
let num: StringOrNumber = 321
Here, StringOrNumber
can be used wherever you want a type that can be either a string
or a number
.
One unique feature of type aliases is that they can refer to themselves, allowing the creation of recursive types.
type TreeNode = {
value: string;
children: TreeNode[];
};
Interfaces
An interface declaration is another way to name an object type:
interface Person {
firstName: string;
lastName: string;
age: number
}
function printPerson(pr: Person): void{
console.log(`My name is ${firstName} ${lastName} and I am ${age} years old`);
}
let pr1 = {
firstName: 'John',
lastName: 'Doe',
age: 30
}
printPerson(pr1)
Difference between Interface and Type Aliases
Feature/Aspect | Interfaces | Type Aliases |
---|---|---|
Definition | Used to define the structure of an object or other custom types. | Can represent any valid type, not just the structure of objects. |
Declaration Merging | Supports declaration merging. Multiple declarations with the same name are merged. interface User { name: string; } interface User { age: number; } let user: User = { name: “Alice”, age: 30 }; // Works fine | Does not support declaration merging. Duplication results in an error. |
Extensibility | Can be extended using extends . Can be implemented by classes using implements . | Cannot be extended or implemented in the same way. Uses intersection types (& ) to combine types. |
Use Cases | Primarily for object shapes, classes, and other OOP constructs. | Versatile; can represent primitives, unions, intersections, tuples, recursive types, etc. |
Descriptive Errors | Typically clearer error messages when used in function signatures or expected types. | Might have more verbose error messages for complex types. |
Compatibility | Present from the initial versions of TypeScript. | Introduced later; advanced features may not be available in older TypeScript versions. |
Literal Types | Cannot be used to directly define literal types. | Can directly define literal types (e.g., `type Direction = “left” |
Recursive References | Can reference themselves. | Can also reference themselves, often used in more complex constructs. |
Generics
Generics are a feature in TypeScript that allows you to write reusable, type-safe code without committing to a single data type. This capability facilitates the creation of flexible and reusable components without compromising type safety. Generics are common in languages like Java and C#, and TypeScript brings this powerful feature to the JavaScript ecosystem.
Why Use Generics?
Imagine writing a function to return the last item in an array. Without generics, you could return any
, but this would sacrifice type information. With generics, you can return the exact type of item in the array.
Basic Usage:
Here’s a basic example of a generic function:
function identity<T>(arg: T): T {
return arg;
}
In this function, T
is a type variable. It stands in for any type, and the consumer of the function gets to choose what type that is.
Usage:
let output1 = identity<string>("myString");
let output2 = identity<number>(100);
Generic Types:
You can also define generic types. Using the identity
function from before:
let myIdentity: <T>(arg: T) => T = identity;
Generic Interfaces:
Interfaces can also be generic. Here’s how you might rewrite the above using an interface:
interface GenericIdentityFn<T> {
(arg: T): T;
}
let myIdentity: GenericIdentityFn<number> = identity;
Generic Classes:
Classes can have generic properties and methods:
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
In this Article we have tried to cover most of the aspects of TypeScript’s type system, but there are many nuanced features and patterns that developers can utilize. Checking the official TypeScript documentation is highly recommended for a comprehensive understanding.