Typescript is a strict syntactical superset of JavaScript developed by Microsoft and adds optional static typing to the language. TypeScript is used for building large-scale, maintainable JavaScript applications.
Unlike Javascript, Typescript can identify errors or mistakes at the early stage of the development process (at compile time). It can save time by reducing efforts to change code when issues arise after running the applications.
Today, we will discuss advanced features of Typescript like decorators, generics, enums, and some others, which can be useful in your application to make it more scalable.
Your daily habits shape your future. Try out justly and make your future more advanced.
Decorators are a way to add additional functionality to classes
, properties
, methods
, and accessors
. It executes code before an actual class instance has been created.
They are a powerful feature that allows for a more expressive and elegant way to write code.
We can enable support for decorators in javascript by command line or from tsconfig.json as below:
$tsc --target ES5 --experimentalDecorators
{
"compilerOptions": {
"target": "ES5",
"experimentalDecorators": true
}
}
// Define the decorators
function logger(target: any) {
console.log(`Creating an instance of ${target.name}`);
}
function logMethod(target: any, key: string, descriptor: PropertyDescriptor) {
console.log(`Calling ${key} method`);
}
function logProperty(target: any, key: string) {
console.log(`Accessing ${key} property`);
}
// Use the decorator on a class and its properties
@logger
class TestClass {
@logMethod
testMethod() {
console.log("testMethod called");
}
@logProperty
testProp:string;
}
// It will print "Creating an instance of TestClass"
const testClass = new TestClass();
// It will print "Calling testMethod method"
testClass.testMethod();
// It will print "Accessing testProp property"
console.log(testClass.testProp);
In the above code, We have defined decorators (logger
, logMethod
, logProperty
) and used them before the class, method, and property definitions. They will execute before the actual code will be executed.
Typescript is a typed language. However, it is not necessary to define types on the variable declaration. Typescript can infer the type based on its declaration.
let a = 1; // let a: number = 1
The type of the a
variable is inferred to be number
. This kind of inference takes place when initializing variables and members, setting parameter default values, and determining function return types.
If the object is initialized with multiple types, TypeScript looks for the most common type to infer the type of the object.
let a = [0, 1, null]; // let a: (number | null)[]
The type of a variable is inferred from its best common type, that is number | null
Learn more about type inference here.
For building large and scalable software systems, we sometimes have to reuse the components.
Generics allow you to create reusable components that can work with multiple types of data.
We can use any
for allowing multiple types to function. But we lose the information about the type when the function returns in that case.
function identity(arg: any): any {
return arg;
}
Below is an example of creating generics in typescript,
function identity<T>(arg: T): T {
return arg;
}
Here T
is a type of variable. In the context of the above code, we can say that If T
is a string, then arg
type and function’s return type
is also string.
The below snippet shows how a generic function can be called,
let message = identity<string>("Hello world!!"); // message = Hello world!!
Same as the other languages, Typescript also provides an Enum facility. Enums allow us to define a set of named constants. Typescript has both numeric
and string
type Enums.
auto-incremented
, means if the first value is defined, it will automatically assign its following value to other enums.constant-initialized
with a string literal, or with another string enum member.// numeric
// Up = 1, Down = 2, Left = 3, Right = 4
enum Direction { Up, Down, Left, Right }
// or
// Up = 3, Down = 4 , Left = 5, Right = 6
enum Direction {
Up = 3,
Down,
Left,
Right,
}
// string
// Up = "UP", Down = "DOWN" , Left = "LEFT", Right = "RIGHT"
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",
}
Namespaces provide a way to structure the codebase logically, making it easier for others to understand and use. Namespaces are similar to modules in other programming languages and are used to prevent naming collisions between different parts of the code.
The basic syntax for creating a namespace is as below,
namespace Calculations {
export function sum(x: number, y: number) {
return x + y;
}
export function product(x: number, y: number) {
return x * y;
}
}
console.log(Calculations.sum(1,2)) // 3
console.log(Calculations.product(4,5)) // 20
Namespaces can also be split across multiple files. You can use the “/// <reference path=’’/>”
to refer to another file that contains the same namespace.
// calculations.ts
namespace Calculations {
export function sum(x: number, y: number) {
return x + y;
}
}
// main.ts
/// <reference path="calculations.ts" />
console.log(Calculations.sum(1,2)) // 3
We can also use one namespace inside another.
namespace Calculations {
export function sum(x: number, y: number) {
return x + y;
}
export namespace Circle {
export function circumference(d: number) {
return 2 * Math.PI * d;
}
}
}
console.log(Calculations.Circle.circumference(10)) // 62.83185307179586
A tuple is an ordered, fixed-size collection of elements, where each element can have a different type.
Tuples are similar to arrays, but with a key difference: the types of the elements and the number of elements in a tuple are known at compile time.
Tuples can be useful in situations where you need to pass a fixed number of elements with different types to a function or return multiple values from a function.
let person: [string, number, boolean] = ['Sem', 25, true];
If we assign different values to the tuple elements, it will throw an error like below,
// define our tuple
let person: [string, number, boolean];
// throws an error as initialized with different types
person = [true, 'Sem', 25];
Tuple destructuring is used to unpack the elements of a tuple into separate variables.
let [name, age, isStudent] = person;
console.log(name); // 'Sem'
console.log(age); // 25
console.log(isStudent); // true
There may be a situation when we have to make a decision about type of output
based on its input type
.
In that case, the most advanced feature of typescript conditional types will be useful. It allows us to express a type that depends on a condition.
type UnwrapArray<T> = T extends (infer U)[] ? U : never;
Above is a conditional type that returns the type of the input (U
) if it’s an array, otherwise, it returns “never”.
You can also use the “extends” keyword to check if a type is a subtype of another type.
type IsString<T> = T extends string ? true : false;
let a: IsString<string> = true;
let b: IsString<number> = false;
TypeScript offers several advanced types that can be used to express more complex type relationships and constraints.
type A = { a: number };
type B = { b: string };
type AB = A & B;
let ab: AB = { a: 1, b: "hello" };
let value: number | string = "hello";
value = 20;
type Name = string;
let name: Name = "John";
// returns "id" | "name" | "description"
type Keys = keyof { id: 1, name: 2, description: 3 };
let obj = { a: 1, b: "hello" };
let propExist: 'a' in obj // true
You can explore more advanced types from the Advanced typescript types cheat sheet.
TypeScript is an object-oriented programming language. It provides many of the features of traditional object-oriented languages such as classes
, interfaces
, inheritance
, and polymorphism
.
You can explore and use those features to make your software application more scalable and well-defined.
We’re Grateful to have you with us on this journey!
Suggestions and feedback are more than welcome!
Please reach us at Canopas Twitter handle @canopas_eng with your content or feedback. Your input enriches our content and fuels our motivation to create more valuable and informative articles for you.
That’s it for today, Keep exploring for the best.
Let's Work Together
Not sure where to start? We also offer code and architecture reviews, strategic planning, and more.