Skip to main content

Other TypeScript theory

Is TypeScript a compiled or interpreted language? What are the differences?​

TypeScript (TS) is a compiled programming language. It's designed as a superset of JavaScript (JS), meaning any valid JS code is also valid TS code. The primary difference with TypeScript lies in the addition of static types and other features that enhance the development process and code maintenance. Before TypeScript code can be executed in a browser or on a server, it must be compiled into JavaScript, as JS is the language directly executed by browsers and Node.js runtimes. This compilation process transforms TS code into equivalent JS code, optimizing it for execution and removing types to make it understandable to the runtime environment.

Compiled programming languages, like TypeScript, require a compilation stage where the source code is transformed into machine code or into code in another programming language (in the case of TypeScript, into JavaScript) that can be executed by the machine. This is different from interpreted languages, such as pure JavaScript, where code is executed directly by the runtime environment line-by-line without a preliminary compilation stage. The main advantage of compiled languages is that they allow many errors to be detected at the compilation stage before the program is executed, and they can be optimized for performance enhancement. At the same time, interpreted languages provide more flexibility and simplify the development process by allowing code to be quickly executed without prior compilation.

Does TypeScript support all OOP principles?​

Yes, TypeScript supports all the fundamental principles of object-oriented programming (OOP). These principles include encapsulation, inheritance, abstraction, and polymorphism. TypeScript extends JavaScript by adding static typing capabilities and classes, making it more suitable for developing complex applications using OOP principles.

Encapsulation allows hiding the implementation details of a class and providing a public interface for interacting with it. In TypeScript, this is achieved using access modifiers private, protected, and public, which define which properties and methods of the class are accessible from the outside.

Inheritance enables the creation of new classes based on existing ones, inheriting their properties and methods. TypeScript supports inheritance through the extends keyword, allowing classes to inherit functionality from base classes.

Abstraction allows the creation of classes that define a template for a group of subclasses, leaving the implementation of some methods to their specific implementations. In TypeScript, abstract classes and methods can be created using the abstract keyword.

Polymorphism is the ability of objects with the same interface to provide different implementations. In TypeScript, polymorphism is implemented through interfaces and abstract classes, allowing objects of different classes to work together through a common interface.

Thus, TypeScript provides full support for OOP, offering developers powerful tools for creating structured and scalable applications.

What is the difference between type and interface in TypeScript?​

In TypeScript, type and interface are two means for declaring types, but they have different uses and capabilities.

Interface:

  • Interfaces are intended for declaring the shape of objects, describing their structure.
  • They can be extended by other interfaces using the extends keyword, allowing the creation of complex types through inheritance.
  • Interfaces can be implemented by classes, ensuring that a class adheres to a certain object structure.
  • TypeScript allows for multiple interfaces with the same name to be declared in the same scope, and they will automatically merge into one.

Type:

  • Type aliases can be used to create new names for any type of data, including primitives, unions, and intersection types.
  • type cannot be extended or implemented, making it less flexible compared to interfaces in the context of OOP.
  • Type aliases can be used to create complex types using unions, intersections, and tuples, sometimes making them more convenient for working with complex data structures.
  • It's not possible to declare multiple type aliases with the same name in the same scope.

The primary difference between type and interface lies in their purpose and ways of use:
interface is better suited for declaring object shapes that can be extended or implemented, while type offers greater flexibility for creating complex or unique types of data.

Why do we use TypeScript in projects?​

TypeScript is used in projects for several reasons that make it particularly attractive for developing large-scale applications and improving code quality:

  • Static Typing: TypeScript provides static typing, which helps catch errors at compile time, before the code is executed. This improves code reliability and simplifies debugging.
  • Easier Refactoring: Static typing allows development tools to offer more powerful refactoring and code completion features, speeding up the development process and reducing the likelihood of errors when modifying code.
  • Improved Compatibility with JavaScript: TypeScript is a superset of JavaScript, meaning any valid JavaScript code is also valid TypeScript code. This allows for easy integration of TypeScript into existing projects.
  • OOP Approach: TypeScript supports object-oriented programming with classes and interfaces, making it ideal for developing complex applications.
  • Better Support for Large Projects: Features such as modules, namespaces, and decorators make TypeScript particularly suitable for the development of large-scale applications.
  • Broad Ecosystem and Community: A large and active community means a wealth of available tools, libraries, and frameworks, as well as good support and educational resources.

Using TypeScript can significantly enhance code quality, simplify maintenance, and facilitate the development of more complex and reliable applications.

What is a Type Guard and what types exist in TypeScript?​

In TypeScript, a Type Guard is an expression that performs a runtime type check, allowing TypeScript to understand what type of data is being used in a certain block of code. This ensures type safety when working with variables of unknown type. There are several ways to implement type guards in TypeScript:

  • Using the typeof operator: Allows checking the primitive type of a variable (e.g., string, number, boolean). This is the simplest form of type guarding.
if (typeof variable === "string") {
console.log(variable.toUpperCase()); // TypeScript understands that variable is a string
}
  • Using the instanceof operator: Used to check whether an object is an instance of a specific class.
class MyClass {}

let myInstance = new MyClass();

if (myInstance instanceof MyClass) {
// TypeScript understands that myInstance is an instance of MyClass
}
  • Using User-Defined Type Guards: Custom type guards are implemented using functions that return a type predicate. This allows for more complex type checks.
function isString(test: any): test is string {
return typeof test === "string";
}

if (isString(variable)) {
console.log(variable.toUpperCase()); // TypeScript understands that variable is a string
}
  • Using the in operator: Checks for the presence of a property in an object, which can be used as a type guard when dealing with different interfaces.
interface A {
a: number;
}

interface B {
b: string;
}

function doSomething(x: A | B) {
if ("a" in x) {
// TypeScript understands that x is of type A
} else {
// TypeScript understands that x is of type B
}
}
  • Using Type Assertions: Serves to assert that a variable is of a certain type. This is not so much a type guard method as a way to inform TypeScript about the type of a variable.
function assertIsString(val: any): asserts val is string {
if (typeof val !== "string") {
throw new Error("Not a string!");
}
}

Type Guards in TypeScript help improve the safety and predictability of the code, allowing developers to explicitly specify data types and ensuring their correct use.

What is a Generic in TypeScript?​

Generics in programming are a powerful tool that allows the creation of components (such as functions and data types) capable of working with any type of data. This is achieved by using type parameters that are specified when the component is invoked, which provides flexibility and code reusability without losing type information.

In TypeScript, generics allow the creation of components capable of working with various data types while still maintaining strict type safety. For example, you can create a function that works with an array of any type of elements, preserving information about the type of elements in the array. This makes the code more secure and easily scalable, as there is no need to write separate logic for each data type.

An example of using generics in TypeScript:

function identity<T>(arg: T): T {
return arg;
}

let output1 = identity<string>("myString");
let output2 = identity<number>(10);

In this example, <T> is a type parameter that allows the identity function to take an argument of any type T and return a value of the same type T. Thus, the function becomes universal and can be used with different types of data while still maintaining strict type safety.

What are Mapped Types and where are they used?​

Mapped Types in TypeScript is a way to create new types based on old ones by transforming each property of the existing type. They allow for efficiently "mapping" through existing types to create new, more flexible or restricted versions, making them a powerful tool for working with data types.

Mapped Types are often used to create types that need to be closely related to other types but with some modifications. For example, you can create a type that makes all properties of an existing type optional or read-only.

Examples of Mapped Types:

  • Creating a type where all properties of the source type become optional:
type Partial<T> = {
[P in keyof T]?: T[P];
};
  • Creating a type where all properties of the source type become read-only:
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};

These capabilities make Mapped Types ideal for working with complex data structures where flexibility and the ability to modify the properties of types are required. They are also widely used in the development of libraries and frameworks, where there is a need to provide users with the ability to extend and modify standard types.