When we hear about OOP, what is the first thing that comes to mind? Classes!
Basic structure
Classes in TypeScript look pretty the same as in JavaScript only with specifying types.
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
greet() {
console.log(`Welcome, ${this.name}!`);
}
}
const p1 = new Person("Mary");
p1.greet(); // Welcome, Mary!
Access modifiers
We can restrict or open access to specific properties or methods by using keywords:
Private modifier
The private modifier makes a property available only within the class.
class Person {
private name: string;
constructor(name: string) {
this.name = name;
}
greet() {
console.log(`Welcome, ${this.name}!`);
}
}
const p1 = new Person("Mary");
console.log(p1.name); // Property 'name' is private and only accessible within class 'Person'
But how would we read it ouside the class? Here comes getter:
class Person {
private name: string;
constructor(name: string) {
this.name = name;
}
greet() {
console.log(`Welcome, ${this.name}!`);
}
getName() {
return this.name;
}
}
const p1 = new Person("Mary");
console.log(p1.getName()); // Mary
We can also use setter to change the private property (we can also set some conditions):
class Person {
private name: string;
constructor(name: string) {
this.name = name;
}
greet() {
console.log(`Welcome, ${this.name}!`);
}
setName(name: string) {
if (name.length < 5) return;
this.name = name;
}
}
Public modifier
Actually, this value is there by default.
class Person {
public name: string;
constructor(name: string) {
this.name = name;
}
greet() {
console.log(`Welcome, ${this.name}!`);
}
}
const p1 = new Person("Mary");
console.log(p1.name); // Mary
Protected modifier
The protected modifier makes a property available only within the class and its subclasses:
class Person {
protected name: string;
constructor(name: string) {
this.name = name;
}
}
class User extends Person {
greet() {
console.log(`Hello there, ${this.name}`);
}
}
const p1 = new User("Mary");
console.log(p1.greet());
Readonly modifier
The readonly modifier prevents the property from being modified outside of the constructor:
class Person {
readonly name: string = "No name";
constructor(otherName: string) {
this.name = otherName;
}
changeName(otherName: string) {
this.name = otherName; // Cannot assign to 'name' because it is a read-only property
}
}
const p1 = new Person("Mary");
p1.name = "Ann"; // Cannot assign to 'name' because it is a read-only property
We can also use it for interfaces:
interface User {
readonly password: string;
name: string;
}
let user: User = {
password: 'password',
name: 'John Smith'
}
user.name = 'Mary Smith';
user.password = 'newPassword'; //Cannot assign to 'password' because it is a read-only property.
Abstract class
This is a restricted class (we can't create instances from it), from which we can create subclasses. It's usually used to define mandatory methods.
abstract class Dog {
abstract bark(duration: number): void;
walk(duration: number) {
console.log("Walking");
this.bark(duration);
}
}
class Husky extends Dog {
bark(duration: number) {
console.log("Wooooooo");
}
}
class Chihuahua extends Dog {
bark(duration: number) {
console.log("wof wof wof");
}
}
let d1 = new Husky();
d1.walk(2);
Here, subclasses must implement their own
barkmethod, since it's defined with keyworkabstract. They inherited the methodwalk.
Classes and interfaces
Classes can implement an interface, that allows to treat instances from different classes (that implement the same interface) as the same object hiding complexity, that we don't care about it at this moment:
interface MakeSound {
makeSound(): void;
}
class Python implements MakeSound {
length: number;
constructor(length: number) {
this.length = length;
}
makeSound() {
console.log('Ssssss!');
}
}
class Puma implements MakeSound {
makeSound() {
console.log('Roar!');
}
}
const python = new Python(10);
const puma = new Puma();
function animalSpeak(animal: MakeSound) {
animal.makeSound();
}
animalSpeak(python);
animalSpeak(puma);
function
animalSpeakis interested only in the ability of object to makeSound, it doesn't care if the object is python or puma.
Static method and field
Like in JavaScript, we can define static methods and fields for a class. They cannot be accessed on instances, but on the class itself. We can use it to create some shared values or maybe count instances that exist:
class Person {
static instanceCount: number = 0;
name: string;
constructor(name: string) {
Person.instanceCount++;
this.name = name;
}
}
const p1 = new Person('Mary');
const p2 = new Person('Tom');
console.log(Person.instanceCount); // 2
We also can define static method:
class Person {
static instanceCount: number = 0;
name: string;
constructor(name: string) {
Person.instanceCount++;
this.name = name;
}
static clearCount() {
this.instanceCount = 0;
}
}
const p1 = new Person('Mary');
const p2 = new Person('Tom');
Person.clearCount();
console.log(Person.instanceCount); // 0
Static methods can only access variables that associate with the class.
Top comments (0)