Skip to main content

Top TypeScript theory

What is TypeScript and what are its main advantages compared to JavaScript?​

TypeScript is a superset of JavaScript that adds static typing and other features to enhance development and code maintenance. It compiles to plain JavaScript, meaning it can run on any environment that supports JavaScript, including browsers and Node.js. The primary advantages of TypeScript over JavaScript include:

  • Static Typing: TypeScript introduces static types, allowing developers to define the types of variables, function parameters, and return values. This reduces runtime errors by catching type-related bugs at compile-time.
let message: string = "Hello, TypeScript!";
message = 42; // Error: Type 'number' is not assignable to type 'string'
  • Enhanced IDE Support: TypeScript provides better tooling support with autocompletion, type-checking, and refactoring capabilities in IDEs, improving productivity and code quality.

  • Early Error Detection: TypeScript's type system helps identify errors before code execution, reducing bugs in production.

  • Improved Code Readability and Maintenance: Explicit types and interfaces make the code more readable and easier to understand, especially in large codebases.

  • Support for Modern JavaScript Features: TypeScript supports the latest ECMAScript features and allows developers to use them before they are widely supported in browsers.

  • Object-Oriented Programming Features: TypeScript supports features like classes, interfaces, inheritance, and access modifiers, making it more suitable for large-scale applications.

What are types in TypeScript, and what are some examples of basic data types?​

Types in TypeScript provide a way to define the shape and structure of data in your code, enhancing code reliability and enabling static type checking. TypeScript includes several basic types, some of which include:

Number: Represents numeric values, both integers and floating-point numbers.​

let age: number = 25;

String: Used for textual data, enclosed in single or double quotes.​

let name: string = "John";

Boolean: Represents logical values: true or false.​

let isActive: boolean = true;

Array: Represents a collection of elements of the same type.​

let numbers: number[] = [1, 2, 3];

Tuple: A fixed-size array where each element can have a different type.​

let tuple: [string, number] = ["age", 30];

Enum: A way to define a set of named constants.​

enum Color {
Red,
Green,
Blue,
}
let color: Color = Color.Green;

Any: A type that can hold any value, effectively opting out of type checking.​

let anything: any = "Hello";
anything = 42; // valid

Void: Typically used for functions that do not return a value.​

function logMessage(message: string): void {
console.log(message);
}

Null and Undefined: Used to represent the absence of a value.​

let n: null = null;
let u: undefined = undefined;

Object: A type that represents non-primitive values, useful for functions that accept objects as parameters.​

let user: { name: string; age: number } = { name: "Alice", age: 25 };

Union: Combines multiple types into one, allowing a value to be one of several types.​

let value: string | number;
value = "Hello";
value = 10;

What are interfaces in TypeScript and how are they used? Provide examples.​

Interfaces in TypeScript are used to define the structure of an object, ensuring that the object adheres to a specific shape. Interfaces can include properties, methods, and their types, but do not provide actual implementations. They help in defining contracts within the code.

Example:

interface Person {
name: string;
age: number;
greet(): void;
}

const employee: Person = {
name: "John Doe",
age: 30,
greet() {
console.log(`Hello, my name is ${this.name}`);
},
};

employee.greet(); // Output: Hello, my name is John Doe

Usage:

  • Implementing Interfaces in Classes: Interfaces can be used to enforce the structure of a class.
class Employee implements Person {
constructor(
public name: string,
public age: number,
) {}

greet() {
console.log(`Hello, my name is ${this.name}`);
}
}

const emp = new Employee("Jane Doe", 25);
emp.greet(); // Output: Hello, my name is Jane Doe
  • Extending Interfaces: Interfaces can extend other interfaces to inherit their properties and methods.
interface Employee extends Person {
employeeId: number;
}

const developer: Employee = {
name: "Alice",
age: 28,
employeeId: 101,
greet() {
console.log(
`Hello, my name is ${this.name} and my ID is ${this.employeeId}`,
);
},
};

How to declare and use type aliases in TypeScript? Provide examples.​

Type aliases in TypeScript allow you to create new names for existing types, which can be useful for simplifying complex type definitions or making code more readable. Type aliases can be used for primitive types, object types, union types, and more.

Example:

type ID = string | number;

let userId: ID = 123;
userId = "abc123"; // Valid because ID can be a string or number

Usage:

  • Object Type Aliases: Define a type alias for an object structure.
type Point = {
x: number;
y: number;
};

const point: Point = { x: 10, y: 20 };
  • Union Type Aliases: Create a type alias that can represent multiple types.
type Status = "success" | "failure" | "pending";

let currentStatus: Status = "success";
  • Function Type Aliases: Define a type alias for a function signature.
type Greet = (name: string) => string;

const sayHello: Greet = (name) => `Hello, ${name}`;

What are generics in TypeScript and how are they used? Provide examples.​

Generics in TypeScript allow you to create reusable components that work with various types, without specifying the exact type in advance. Generics provide a way to make functions, classes, and interfaces more flexible while maintaining type safety.

Example:

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

let num = identity<number>(42); // num: number
let str = identity<string>("Hello"); // str: string

Usage:

  • Generic Functions: Functions that can operate on different types.
function getFirstElement<T>(arr: T[]): T {
return arr[0];
}

const firstNum = getFirstElement([1, 2, 3]); // firstNum: number
const firstStr = getFirstElement(["a", "b", "c"]); // firstStr: string
  • Generic Classes: Classes that can handle various types.
class Box<T> {
private _content: T;

constructor(content: T) {
this._content = content;
}

getContent(): T {
return this._content;
}
}

const numberBox = new Box<number>(123);
const stringBox = new Box<string>("Hello World");
  • Generic Constraints: Restrict the types that can be used with generics.
interface Lengthwise {
length: number;
}

function logLength<T extends Lengthwise>(item: T): void {
console.log(item.length);
}

logLength("Hello"); // Works because string has a length property
logLength([1, 2, 3]); // Works because array has a length property

Types of Properties in TypeScript​

In TypeScript, there are several types of properties that you can define in an interface or a class:

Required Properties​

These properties must be present in the object. They do not have a question mark (?) in the interface.

interface User {
name: string; // Required property
}

Optional Properties​

These properties may or may not be present in the object. They are marked with a question mark (?).

interface User {
age?: number; // Optional property
}

Readonly Properties​

These properties cannot be modified after their initial assignment. They are marked with the readonly keyword.

interface User {
readonly id: number; // Readonly property
}

Index Signature Properties​

These properties do not have a specific name but have a known type for the keys and values. They are defined using the syntax [key: string]: valueType.

interface StringDictionary {
[key: string]: string; // Index signature property
}

Method Properties​

These properties are functions and are defined as methods within the interface or class.

interface User {
greet(): void; // Method property
}

How does function typing work in TypeScript? Provide examples of declaring and using functions with types.​

In TypeScript, function typing is essential for defining the input and output types of a function, ensuring type safety. Here's how it works:

Basic Function Typing​

function add(x: number, y: number): number {
return x + y;
}

In this example, x and y are typed as number, and the function's return type is also number.

Optional and Default Parameters​

function greet(name: string, greeting?: string): string {
return greeting ? `${greeting}, ${name}` : `Hello, ${name}`;
}

The greeting parameter is optional (?), and if not provided, the function will default to saying "Hello".

Rest Parameters​

function sum(...numbers: number[]): number {
return numbers.reduce((acc, num) => acc + num, 0);
}

The ...numbers parameter allows the function to accept an arbitrary number of number arguments.

Function Types​

type MathOperation = (a: number, b: number) => number;

const multiply: MathOperation = (a, b) => a * b;

Here, MathOperation is a type that describes any function taking two number arguments and returning a number.

What are utility types in TypeScript? Provide examples (e.g., Partial, Pick, Omit).​

Utility types in TypeScript are predefined types that help manipulate existing types. Some common utility types include:

Partial: Makes all properties of a type optional.​

interface User {
name: string;
age: number;
}

const updateUser: Partial<User> = { age: 30 };

In this example, Partial<User> allows the updateUser object to have only some of the User properties

Pick: Creates a new type by selecting a subset of properties from an existing type.​

interface User {
name: string;
age: number;
email: string;
}

type UserContact = Pick<User, "name" | "email">;

const contact: UserContact = { name: "Alice", email: "alice@example.com" };

Omit: Creates a new type by excluding specific properties from an existing type.​

type UserWithoutEmail = Omit<User, "email">;

const userWithoutEmail: UserWithoutEmail = { name: "Alice", age: 25 };

Here, Omit<User, 'email'> creates a type without the email property.

Readonly: Makes all properties of a type read-only, preventing reassignment.​

type ReadonlyUser = Readonly<User>;

const readonlyUser: ReadonlyUser = { name: "Alice", age: 25 };
// readonlyUser.age = 26; // Error: Cannot assign to 'age' because it is a read-only property.

These utility types simplify complex type manipulations and make your TypeScript code more robust and maintainable.

What is type assertion in TypeScript, and how is it used? Provide examples.​

Basic Syntax​

let someValue: unknown = "Hello TypeScript";

let strLength: number = (someValue as string).length;

Here, the as string assertion tells TypeScript to treat someValue as a string, allowing access to string-specific properties like length.

Alternative Angle-Bracket Syntax​

let strLength: number = (<string>someValue).length;

This syntax is equivalent to as but is not recommended in JSX files due to syntax conflicts.

Using Type Assertion with DOM Elements​

const inputElement = document.querySelector(
'input[type="text"]',
) as HTMLInputElement;

inputElement.value = "Hello World";

Here, as HTMLInputElement is used to assert that the inputElement is of the specific type HTMLInputElement, allowing access to the value property.

Type assertions are powerful but should be used cautiously, as they can override TypeScript's type-checking and potentially lead to runtime errors if used incorrectly.

How do union types and intersection types work in TypeScript? Provide examples.​

Union types and intersection types in TypeScript allow combining multiple types in different ways.

Union Types​

let id: number | string;
id = 101; // Valid
id = "E101"; // Also valid

Here, id can be either a number or a string.

Intersection Types​

interface Person {
name: string;
}

interface Employee {
employeeId: number;
}

type EmployeeDetails = Person & Employee;

const employee: EmployeeDetails = { name: "Alice", employeeId: 101 };

In this example, EmployeeDetails must include all properties from both Person and Employee.

What are decorators in TypeScript and how do they work? Provide examples of using decorators.​

Decorators in TypeScript are special declarations that can be attached to classes, methods, properties, or parameters to modify their behavior. They are a form of metaprogramming, allowing you to extend or change the functionality of the code in a declarative way. Decorators are typically used in frameworks like Angular for dependency injection, routing, and more.

Example:

function logClass(constructor: Function) {
console.log(`Class ${constructor.name} has been instantiated`);
}

@logClass
class MyClass {
constructor() {
console.log("MyClass instance created");
}
}

// Output:
// Class MyClass has been instantiated
// MyClass instance created

Usage:

  • Class Decorators: Modify or annotate a class.
function sealed(target: Function) {
Object.seal(target);
Object.seal(target.prototype);
}

@sealed
class SealedClass {
// The class is sealed, meaning no properties or methods can be added
}
  • Method Decorators: Modify or observe the behavior of class methods.
function logMethod(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor,
) {
const originalMethod = descriptor.value;

descriptor.value = function (...args: any[]) {
console.log(`Method ${propertyKey} called with args: ${args}`);
return originalMethod.apply(this, args);
};
}

class Calculator {
@logMethod
add(a: number, b: number): number {
return a + b;
}
}

const calc = new Calculator();
calc.add(2, 3); // Output: Method add called with args: 2,3
  • Property Decorators: Modify or observe the behavior of class properties.
function readonly(target: any, propertyKey: string) {
Object.defineProperty(target, propertyKey, {
writable: false,
});
}

class Person {
@readonly
name: string = "John Doe";
}

const person = new Person();
person.name = "Jane Doe"; // Error: Cannot assign to 'name' because it is a read-only property
  • Parameter Decorators: Modify or observe parameters of class methods.
function logParameter(
target: any,
propertyKey: string,
parameterIndex: number,
) {
console.log(
`Parameter at index ${parameterIndex} in method ${propertyKey} is being logged`,
);
}

class Greeter {
greet(@logParameter message: string) {
console.log(message);
}
}

These examples demonstrate the powerful features of TypeScript that enhance JavaScript with strong typing, generics, decorators, and more. This makes TypeScript an excellent choice for building scalable and maintainable web applications.