A short note about TypeScript and his capabilities.
What is TypeScript?
TypeScript is a super set of JavaScript.
Why TypeScript:
TypeScript reduce number of bugs by using types which help you catch bugs at the compile-time instead of having them occurring at runtime;
TypeScript allow you to use new JS today;
TypeScript Types
TypeScript uses types to analyse your code for errors and to understand what values are associated with variables.
Via TypeScript you can explicitly specify types for identifiers such variables, functions, objects, etc. It’s called — type annotations. Just use the : type
syntax
// e.g.
let someNumber: number;
let someString: string;
let someBoolean: boolean = true;
someNumber = 1;
someString = 1; // Compile error
If you forget to explicitly specify type then TypeScript will infer the type you. It’s called — type inference. Let’s take a look for example:
let someNumber = 0;
// It is equivalent to the line below
let someNumber: number = 0;
When do I need use type inference or type annotations?
It’s recommended to use type annotations when:
When you create a variable and assign it a value later;
When you need a variable that can’t be inferred;
When a function returns the
any
type and you need to specify the value;
In all other cases you should use type inference as much as possible.
There are two categories of types
Primitive types:
string — represents text data;
// string examples
let someString1: string = 'Hello World'; // single quotes
let someString2: string = "Hello World"l // double quotes
let someString3 = `Hello World`; // multi-line string with backtick
let someString4 = `${someString1}`; // string interpolation
number — represents numeric values;
// decimal numbers
let someNumber1: number;
let someNumber2 = 5.55;
let x: number = 10,
y: number = 20;// binary numbers
let someBinaryNumber1 = 0b99;
let someBinaryNumber2: number = 0b100;// big int
let someBigIntNumber = 9007199254740991n;// hexadecimal numbers
let someHexDecimalNumber = 0XA;
boolean — has
true
orfalse
values
let someBoolean1 = true;
let someBoolean2: boolean;
someBoolean2 = true;
null — has only one value:
null
undefined — has only one value:
undefined
. Also it is default value of an uninitialised variablesymbol — represents a unique const
Object types:
object — represents all values that are not in primitive types
let person: object;person = {
name: 'Stas',
age: 30
};
We can explicitly specify properties of the person
object via syntax below
let person: {
name: string,
age: number
};
person = {
name: 'Stas',
age: 30
};
// or we can do it in this waylet
person: {
name: string,
age: number
} = {
name: 'Stas',
age: 30
};
If you will try to access a property that doesn’t exist on the person
object, you’ll get an error.
array — is an ordered list of data
let someArray: number[]; // to add element we can use next syntax
someArray.push(1); // or
someArray[0] = 1;
We can declare array with initial items and TypeScript will infers the fruits
array as an array of strings.
let fruits = ['banana', 'mango'];
It is equivalent to the following:
let fruits: string[];
fruits = ['banana', 'mango'];
Of course you can not add any another type to declared type of array. TS compiler will throw error.
fruits.push(100); // error
enum — list of named constants.
enum Week {
Monday, // 0
Tuesday, // 1
Wednesday, // ...
Thursday,
Friday,
Saturday,
Sunday
}
// you can change start number explicitly
enum Week {
Monday = 1,
...
}
It’s recommended to use enums when you have a small set of fixed values that are closely related and known at compile time.
functions — are the building blocks of readable, maintainable, and reusable code
function functionName(par1: type, par2: type, ...): returnType {
//some code
}
You can also declare a function type which has parameters and return type
(par: type, par,...) => returnType
// example
let stringConcat: (str1: string, str2: string) => string;
stringConcat = function(str1: string, str2: string) {
return str1 + str2;
}
// or you can do it like this
let stringConcat: (str1: string, str2: string) => string =
function(s1: string, s2: string) {
return s1 + s2;
}
}
Functions can have optional parameters
// use '?' after parameter name
function functionName(par1: type, par2?: type): returnType {
//some code
}
Or default parameters
// use 'par:type = value' syntax
function functionName(par1: type, par2: type = 'Hello'): returnType {
//some code
}
You can also overload function
function add(a: number, b: number): number;
function add(a: string, b: string): string;
function add(a: any, b: any): any { return a + b; }
class— JS doesn’t have a class, but TS allow you to create the class(like in C#, Java, C++) based on prototype inheritance.
class Person {
name;
age;
constructor(name, age) {
this.name = name;
this.age = age;
} getPersonInfo() {
return `${name} ${age}`;
}
}
let person = new Person('Stas', 30);
console.log(person.getPersonInfo());
Or you can use type annotations for the properties and methods in class
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
getPersonInfo(): string {
return `${name} ${age}`;
}
}
let person = new Person('Stas', 30);
console.log(person.getPersonInfo());
TS class has three access modifiers to properties and methods:
private
modifier — allows access to property within the same class.protected
modifier — allows access to property within the same class and subclasses.public
modifier — allows access to property from any location.
In class you can use readonly
modifier that allows you to mark the properties immutable. With readonly
you can assign a value only in property declaration or constructor.
class Person {
readonly name: string;
constructor(name: string) {
this.name = name;
}
}
If you want to inherit one class from another class, use extends
keyword and if parent class has a constructor that initializes the some properties(name, age) you need to initialize these properties in the constructor of the Employee class by calling its parent class’ constructor via super()
.
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
getPersonInfo(): string {
return `${name} ${age}`;
}
}
class Employee extends Person {
jobTitle: string; constructor(
name: string,
age: number,
jobTitle: string) {
super(name, age);
this.jobTitle = jobTitle;
}
describe() {
return super.getPersonInfo() + this.jobTitle;
}
}
let employee = new Employee('Stas', 30, 'Developer');
console.log(employee.describe());
Class can has static properties and methods which shared by all instances of a class. Use static
keyword to declare static property or method
class Person {
static counter: number;
constructor() {
Person.counter++;
}
static getPersonCount() {
return Person.counter;
}
}
let person1 = new Person();
let person2 = new Person();
console.log(Person.getPersonCount()); // 2
Another good TypeScript feature — abstract class, an abstract class has at least one abstract method and to use it you need inherit it and provide the implementation for the abstract methods.
Of course you can not create instance of abstract class
abstract class Person {
constructor(name: string, age: number) {}
abstract getInfo(): string;
}
class Manager extends Employee {
constructor(name: string, age: number) {
super(name, age);
}
getInfo(): string {
return this.name + ' ' + this.age;
}
}
interface— define contracts in your code and provide explicit names for type checking. Interface may contain readonly or optional properties and you can use interface as class type that make a contract between unrelated classes or as function types.
interface Person {
name: string,
age: number
}
function getInfo(person: Person) {
return person.name + ' ' + person.age;
}
// optional properties
interface Person {
name: string,
age: number,
jobTitle?: string
}
function getInfo(person: Person) {
if(person.jobTitle) {
return person.name + ' ' + person.age + '-' person.jobTitle;
}
return person.name + ' ' + person.age;
}
//readonly properties
interface Person {
readonly name: string,
age: number
}
let person: Person;
person = {
name: 'Stas',
age: 30
}
person.name = 'David'; // error
person.age = 31; // ok
Via interface you can describe function types
interface StringFormat {
(str: string, isLower: boolean): string
}
let format: StringFormat;
format = function(str: string, isLower: boolean) {
return isLower
? str.toLocaleLowerCase()
: str.toLocaleUpperCase()
}
console.log(format('Hello World', true)); // hello world
Finally, class can implemented interface via implements
interface Json {
toJson(): string
}
class Employee implements Person {
name: string; constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
toJson() {
return JSON.stringify(this);
}
}
let employee = new Employee('Stas', 30);
console.log(employee.toJson()); // {"name":"Stas","age":"30"}
Last thing with interfaces — it can extend one or multiple existing interfaces.
interface A {
a(): void
} interface B extends A {
b(): void
}
// B interface has two methods: a() and b()
Specific types
any — use when you don’t know type at the time of writing the program (some values may come from API) or when you migrate from JS to TS.
void — use when function doesn’t return any value.
function(message: string): void {
console.log(message);
}
never — type that contains no value, it is mean you cannot assign any value to a variable with a
never
type.never
use when you have a function that always throws an error. For example:
function someFunction(message: string): never {
throw new Error(message);
}
Or you can use never
type if you have an indefinite loop
let infinityLoop = function createInfinityLoop() {
while(true) {
console.log('.');
}
}
union — allows you to store a value of one or several types in a variable
let someUnionValue: string | number;
someUnionValue = 1; // ok
someUnionValue = 'Hello'; // ok
someUnionValue = false; // error
aliases — allows you to define new names for existing types
type newAliasesType = string | number;
let someVariable: newAliasesType;
literal — allows you to define a type that accepts only specific string values
let statusLiteral: 'approve' | 'reject' | 'pending';
let myStatus: statusLiteral;
myStatus = 'approve'; // ok
myStatus = 'hello'; // error
intersection — creates a new type by combining multiple existing types and new type will have all features of the existing types. Order doesn’t matter
type typeAB = typeA & typeB; // definition
// example
type hybridVariable = number & string;
let someVariable: hybridVariable = '3'; // ok
someVariable = 3; // ok
someVariable = false // error
Note, that union
type and intersection
are not the same.
Type Guard and Casting
use
typeof
if you need to check variable type
let a: number;
if (typeof a === 'number') { ... }
2. use instanceof
to check instance of class
class Person1 {...}
class Person2 {...}type Person = Person1 | Person2;
function showInfo(person: Person) {
if (person instanceof Person1) {...}
if (person instanceof Person2) {...}
}
3. use in
if you need to check existence of a property on an object
class Person {
name: string;
...
}
function showInfo(person: Person) {
if('name' in person) {...}
}
TS allow you convert a variable from one type to another. Use the as
keyword or <>
operator for type castings.
let a: typeA;
let b = a as typeB;// or let a: typeA;
let b = <typeB>a;
Generics
Generics allow you to write the reusable and generalized form of functions, classes, and interfaces.
Generics benefits:
Getting rid of type casting
Implementing generics algorithms
Leverage type checks at the compile time.
function getRandomElement<T>(items: T[]): T {
let randomIndex = Math.floor(Math.random() * items.length);
return items[randomIndex];
}
let numbers = [1, 5, 7, 4, 2, 9];
let randomNumberEle = getRandomElement<number>(numbers);
console.log(randomNumberEle);
let strings = ['a', 'b', 'c'];
let randomStringEle = getRandomElement<string>(strings);
console.log(randomStringEle);
You can use generics with multiple types
function merge<U, V>(obj1: U, obj2: V) {
return {
...obj1,
...obj2
};
}
And denote the constraint, you use the extends
keyword
function merge<U extends object, V extends object>(obj1: U, obj2: V) {...}
Inspired by — https://www.typescripttutorial.net