OOP in TypeScript

Classes

Class is the basic unit containing all data and behaviors inside it. It is a template from which we create objects that we use at runtime.

Java is an Object Oriented Programming (OOP) and classes are everything in it. You can’t write even the most basic Java program without using classes. JavaScript, as we learned in the last chapter, is a functional programming language.

TypeScript supports Classes that are very similar to Java classes and it supports many common patterns such as implementing interfaces, inheritance, and static methods. However, unlike Java, classes are optional in TypeScript.

TypeScript - Classes are Optional

To illustrate this point, let’s write a simple Hello World program in both Java and TypeScript.

First in Java.

public class HelloWorld {
  public static void main(String[] args) {
    System.out.println("Hello, World!");
  }
}

Now in TypeScript:

console.log("Hello, World!");

Notice that in Java, we had a write extra code just to print “Hello, World!”. This is because all Java code must be contained within a class. The main(...) method, which is the entry point for a Java program, must also be defined within a class. Therefore, in order to output “Hello, World!” in Java, we need to define a class and a main() method within that class.

In contrast, we don’t need classes in TypeScript. Hence, we can simply use the console.log() function to output “Hello, World!” in a TypeScript program, without the need to define a class.

However, that is not to indicate classes are any less important in TypeScript. Many problems are modeled best using the OOP paradigm and best represented using classes and objects in both Java and TypeScript.

Types

In Java, all objects have a type. The type is a class that we can easily determine at run-time or compile time because Java objects are related to their type when we declare them. There’s one-to-one correspondence between runtime types and objects and their compile-time declarations. A method in Java that excepts a particular type e.g. someMethod(MyClass o) will only accepts argument that are of type MyClass (unless there’s a inheritance relationship or common interface.)

In TypeScript, we think of a type as a set of values that share something in common. A value can belong to many sets at the same time.

Free Question and Answers with Detailed Explanations

What is Duck Typing?

For example, in Java, we cannot pass around a value that is either a String or Integer, because there isn’t a single type that represents this sort of value (unless you create one using hierarchy but it’s not natural.) In TypeScript, this is natural since every type is just a set. How do you describe a value that’s either a string or a number? Simple: string | number.

If you’d like to learn more about how types are represented in TypeScript compared to Java, this resource is highly recommend.

Differences Between TypeScript and Java Classes

There are a few key conceptual and syntactical differences between classes in Java and TypeScript:

  1. TypeScript has a more flexible class model than Java. In TypeScript, you can use the class, abstract class, interface, and type keywords to define different types of classes and class-like constructs. In Java, there is only one type of class, and you can use the abstract and final keywords to modify the behavior of a class.

  2. TypeScript has a more advanced type system than Java, which allows you to specify the types of class properties and method parameters more precisely. In Java, you can specify the type of a class property or method parameter using type annotations, but the type system is not as powerful as it is in TypeScript.

  3. TypeScript supports access modifiers like public, private, and protected, which can be used to control the visibility and accessibility of class members. In Java, we can use the public, private, protected, and default to specify the access level of a class member.

  4. TypeScript has a readonly modifier that can be used to mark class properties as read-only. Whereas in Java, you can use the final keyword to mark a class property as read-only, but there is no equivalent to the readonly modifier.

  5. In TypeScript, an object can belong to multiple classes at the same time. This is not possible in Java.

  6. In TypeScript, you can use Mixins to add functionality to classes without using inheritance of interfaces.

Free Question and Answers with Detailed Explanations

What are Mixins in TypeScript?

Constructors

In both TypeScript and Java, you can use constructors to initialize an object when it is created.

In Java, you can define a constructor for a class like this:

public class MyClass {
  private String name;

  public MyClass(String name) {
    this.name = name;
  }
}

In TypeScript, you can define the same constructor and class like this:

class MyClass {
  constructor(public name: string) {
    // constructor implementation goes here
  }
}

Here we can see that the constructor is is a special function that is called when an object of the MyClass class is created. The name parameter is a public class property (variable) that can be accessed from outside the class.

Constructor definition is optional in both languages because the language generates a default constructor for you.

It is also possible to define multiple constructors in both TypeScript and Java. Overloading allows you to define multiple constructors for a class where each constructor has different arguments. This allows you to create objects and initialize their properties differently based on the constructor used to create that object.

super() calls

Just like Java, if your class extends your a base class, the super keyword refers to parent class.

Free Question and Answers with Detailed Explanations

What is super keyword in Java?

Here’s an example:

// Parent class
class Animal {
  protected name: string;

  constructor(name: string) {
    this.name = name;
  }

  move(distanceInMeters: number) {
    console.log(`${this.name} moved ${distanceInMeters}m.`);
  }
}

class Snake extends Animal {
  constructor(name: string) {
    super(name); //call the constructor of parent class i.e. Animal
  }

  move(distanceInMeters) {
    console.log("Slithering...");
    super.move(distanceInMeters);
  }
}

let s = new Snake("Kaa the Python");
s.move(5); // Output: "Kaa the Python moved 5m." 

Access Modifiers: public, private, protected methods

In both TypeScript and Java, you can use access modifiers like public, private, and protected to control the visibility and accessibility of your class members (fields, properties, and methods). Unlike Java, in TypeScript, if you don’t specify an access modifier, the member will be public by default.

  • public: public members are visible and accessible from anywhere, both within and outside the class. This is the default visibility if no access modifier is specified.
  • private: Members marked as private are only visible within the class where they are declared. They cannot be accessed from outside the class.
  • protected: Members marked as protected are visible and accessible within the class where they are declared, as well as in any subclasses. They cannot be accessed from outside the class or any of the subclasses. A constructor can also be marked protected. This means that the class cannot be instantiated outside of its containing class, but can be extended or inherited from.
Free Question and Answers with Detailed Explanations

Can we override a protected method in Java?

class Animal {
  protected name: string;
  public age: number;
  private location: string;

  constructor(name: string, age: number, location: string) {
    this.name = name;
    this.age = age;
    this.location = location;
  }

  protected move(distanceInMeters: number) {
    console.log(`${this.name} who's ${this.age} years old moved ${distanceInMeters}m in ${this.location}`);
  }
}

class Snake extends Animal {
  constructor(name: string, age: number, location: string) {
    super(name, age, location);
  }

  public increaseAge(age: number) {
   super.age = age // age is protected so we can access in the subclass
  }

  public crawl() {
    this.move(5);
  }
}

let kaa = new Snake("Kaa the Python", 3, "Africa");
kaa.increaseAge(10)

console.log(kaa.age); // Output: 10 (age is public can be assessed outside the class)
console.log(kaa.location); // Error: location is private and can only be accessed within class 'Animal'
console.log(kaa.name); // Error: name is protected and can only be accessed within class 'Animal' and its subclasses.


kaa.crawl(); // Output: "Kaa the Python who's 10 years old moved 5m in Africa" 

readonly Modifier

You can make properties readonly by using the readonly keyword. Readonly properties must be initialized at the time of declaration or in the constructor.

class Octopus {
  readonly name: string;
  readonly numberOfLegs: number = 8;
 
  constructor(theName: string) {
    this.name = theName;
  }
}
 
let dad = new Octopus("Man with the 8 strong legs");
dad.name = "Man with the 3-piece suit"; // ERROR Cannot assign to 'name' because it is a read-only property.

Source

Importing Classes

In TypeScript, we use the import keyword to import classes, functions, and other values from another module. For example:

import { MyClass } from './my-module';

This will import the MyClass class from the my-module module.

You can also import the entire module and access the exports using the module’s name:

import * as myModule from './my-module';  
let myClass = new myModule.MyClass();

In Java, you can use the import keyword to import classes from another package. For example:

import com.example.MyClass;

This will import the MyClass class from the com.example package. We can also use the * wildcard to import all of the classes in a package:

import com.example.*;

One key difference between TypeScript and Java is that TypeScript uses a module system, whereas Java uses a package system. In TypeScript, modules are used to organize and reuse code, whereas in Java, packages are used to group related classes and define a namespace for the classes.

This is not to be confused with Java Modules introduced in Java 9 which is a way to package related Java packages and associated resources with a descriptor file as a modular JAR file.

Module vs Packages

In TypeScript, a module is a way to organize and reuse code. A module is a collection of related variables, functions, and classes that are encapsulated together and can be imported and used by other parts of your code.

Modules in TypeScript are similar to the concept of libraries or packages in other programming languages. They allow you to divide your code into smaller, more manageable pieces and reuse those pieces in multiple places.

Modules in TypeScript are defined using the export keyword. For example, you can define a class in a module like this:

export class MyClass {
  // class implementation goes here
}

You can then import that class into another module or file using the import keyword:

import { MyClass } from './my-module';

let instance = new MyClass();

Here are key differences between Modules and Packages:

  1. Java packages are used to group related classes and define a namespace for those classes. In TypeScript, modules are used to organize and reuse code, and do not necessarily define a namespace.

  2. Both packages and modules help resolve naming conflicts, but Java packages are hierarchical unlike TypeScript

package com.example;

public class MyClass {
}
package com.example.subpackage; // Subpackage

public class MyClass  {
}
  1. Packages are mandatory in Java and all Java programs are organized as sets of packages. Modules are optional in TypeScript.

  2. In Java, packages are defined using the package keyword are all classes in the same package can use public and protected methods without importing. In TypeScript, modules are defined using the export and import keywords, and must be explicitly imported to be used anywhere.

Inheritance

In both TypeScript and Java, inheritance allows you to create a subclass that inherits properties and methods from the superclass and can also have its own properties and methods. For example in Java, we can define the Animal class is the superclass, and the Dog class is the subclass. The Dog class extends the Animal class and inherits its move() method. It also has its own bark() method.

public class Animal {
  // Properties and methods go here
  public void move() {
    System.out.println("The animal is moving");
  }
}

public class Dog extends Animal {
  // Properties and methods go here
  public void bark() {
    System.out.println("Woof woof!");
  }
}

Dog dog = new Dog();
dog.move(); // Output: "The animal is moving"
dog.bark(); // Output: "Woof woof!"

Here’s the same class hierarchy and inheritance in TypeScript:

class Animal {
  // Properties and methods go here
  move(): void {
    console.log("The animal is moving");
  }
}

class Dog extends Animal {
  // Properties and methods go here
  bark(): void {
    console.log("Woof woof!");
  }
}

let dog = new Dog();
dog.move(); // Output: "The animal is moving"
dog.bark(); // Output: "Woof woof!"

Note: TypeScript has support for mixins, which allow you to combine the properties and methods of multiple classes into a single class, while Java does not have this feature.

Interfaces

An interface defines a contract that specifies the behavior that a class or object must have, but it does not provide any implementation details. It defines a contract that classes and objects must adhere to. Both TypeScript and Java have support for interfaces, but they have some differences in how they are implemented and used.

Here’s how interfaces are defined and used in Java:

public interface Shape {
  // Properties and methods go here
  double area();
}

public class Square implements Shape {
  // Properties and methods go here
  private double sideLength;
  public double area() {
    return sideLength * sideLength;
  }
}

Shape square = new Square();
square.sideLength = 10;
System.out.println(square.area()); // Output: 100

In this example, the Shape interface defines an area() method that must be implemented by any class that implements the interface. The Square class implements the Shape interface and provides an implementation for the area() method.

In TypeScript, an interface is defined using the interface keyword, and it can contain properties and methods that a class or object must implement. An interface can also have optional properties and methods, which are marked with a ? at the end of the property or method name.

interface Shape {
  // Properties and methods go here
  area(): number;
}

class Square implements Shape {
  // Properties and methods go here
  sideLength: number;
  area(): number {
    return this.sideLength * this.sideLength;
  }
}

let square = new Square();
square.sideLength = 10;
console.log(square.area()); // Output: 100

Licenses and Attributions


Speak Your Mind

-->