What is TypeScript and Why Do We Use It?
TypeScript is a superset of JavaScript developed and maintained by Microsoft. It adds static type definitions to JavaScript, providing a way to ensure type safety and enhance the development process with additional features. Here’s a breakdown of what TypeScript is and why it’s used, tailored for an interview setting:
What is TypeScript?
TypeScript is a programming language that builds on JavaScript by adding static types. It allows developers to write JavaScript code with optional type annotations. The TypeScript code is then compiled (transpiled) to plain JavaScript, which can be executed in any environment that runs JavaScript, including browsers, Node.js, and more.
Why TypeScript?
As JavaScript projects grow in size, they become difficult to maintain. There are a few reasons for this. First, JavaScript was never designed to build large-scale applications. Its original purpose was to provide small scripting functionality for a web page. Until recently, it didn’t provide tools and constructs for structuring large projects, such as classes, modules, and interfaces. Also, JavaScript is dynamically typed. It doesn’t support features such as IntelliSense.
function written in ts
function add(a: number, b: number): number {
const sum = a + b;
return sum;
}
function written in normal way in js
function add(a, b) {
const sum = a + b;
return sum;
}
What are the primitive types in TypeScript?
TypeScript has three primitive types that are frequently used: string, number, and boolean. These correspond to the similarly named types in JavaScript.
- string: represents text values such as “javascript”, “typescript”, etc.
- number: represents numeric values like 1, 2, 32, 43, etc.
- boolean: represents a variable that can have either a ‘true’ or ‘false’ value
Explain how the arrays work in TypeScript.
We use arrays to store values of the same type. Arrays are ordered and indexed collections of values. The indexing starts at 0, i.e., the first element has index 0, the second has index 1, and so on.
Here is the syntax to declare and initialize an array in TypeScript.
This ensures type safety by allowing you to specify the types of elements that the array can contain.
Defining Arrays
let numbers: number[] = [1, 2, 3, 4, 5];
let strings: string[] = ["apple", "banana", "cherry"];
let numbers: Array<number> = [1, 2, 3, 4, 5];
let strings: Array<string> = ["apple", "banana", "cherry"];
working With Array
let names: string[] = ["Alice", "Bob", "Charlie"];
// names.push(42); // Error: Argument of type '42' is not assignable to parameter of type 'string'
// Correct usage
names.push("Dave");
console.log(names); // ["Alice", "Bob", "Charlie", "Dave"]
Tuple Types
TypeScript also supports tuples, which are arrays with a fixed number of elements of specific types
let tuple: [string, number];
tuple = ["hello", 42]; // Valid
// tuple = [42, "hello"]; // Invalid: Type 'number' is not assignable to type 'string'
// Accessing tuple elements
console.log(tuple[0]); // "hello"
console.log(tuple[1]); // 42
What is any type, and when to use it?
There are times when you want to store a value in a variable but don’t know the type of that variable in advance. For example, the value is coming from an API call or the user input. The ‘any’ type allows you to assign a value of any type to the variable of type any.
TypeScript assumes a variable is of type any when you don’t explicitly provide the type, and the compiler cannot infer the type from the surrounding context.
What is void in TypeScript?
In TypeScript, the void type is used to indicate that a function does not return a value. It is similar to the concept of void in other programming languages like Java and C#. When a function is declared with a return type of void, it means that the function doesn't return anything.
When to Use the void Type?
You should use the void type when you define a function that performs some operations but does not return a value. This is common for functions that modify the state, log information, perform actions, etc., without needing to return any result.
Here is a simple example where void is used to specify that a function doesn't return a value:
function logMessage(message: string): void {
console.log(message);
}
logMessage("Hello, TypeScript!"); // Logs: Hello, TypeScript!
In this example:
- The
logMessagefunction takes astringparameter and logs it to the console. - The return type of the function is
void, indicating that it does not return any value.
In TypeScript, the unknown type represents a value whose type is not known at compile time. It is similar to any in that it can hold values of any type, but with an important distinction: while any allows any operations on it, unknown requires type checking or type assertion before performing operations on it. This helps maintain type safety even when dealing with values of unknown types.
When to Use the unknown Type?
You should use the unknown type when you're working with values whose types are not known at compile time, such as when:
- Receiving Data from Dynamic Sources: When you're dealing with data from external sources like user input, APIs, or libraries where the type is uncertain.
- Interoperating with Dynamic Languages: When interacting with JavaScript libraries or APIs that may have dynamic types.
- Type Safety: When you want to maintain type safety while still allowing flexibility in the type of values.
Provide the syntax of a function with the type annotations.?
Functions are blocks of code to perform a specific code. Functions can optionally take one or more arguments, process them, and optionally return a value.
Here’s the TypeScript syntax to create and call a function.
function greet(name: string): string {
return `Hello, ${name}`;
}
let greeting = greet("Anders");
console.log(greeting); // "Hello, Anders"
Question: What is an object in JavaScript?
Answer:
In JavaScript, an object is a data structure that allows you to store collections of key-value pairs. Each key, also known as a property, is a string (or Symbol), and each value can be any data type, including other objects and functions.
In TypeScript, an Object type refers to any value with properties. It can be defined by simply listing the properties and their types. For example,
let pt: { x: number; y: number } = {
x: 10,
y: 20
};
How to specify optional properties in TypeScript?
An object type can have zero or more optional properties by adding a ‘?’ after the property name.
let pt: { x: number; y: number; z?: number } = {
x: 10,
y: 20
};
console.log(pt);
In the example above, because the property ‘z’ is marked as optional, the compiler won’t complain if we don’t provide it during the initialization
Explain the purpose of the never type in TypeScript.?
As the name suggests, the never type represents the type of values that never occur. For example, a function that never returns a value or that always throws an exception can mark its return type as never.
function error(message: string): never {
throw new Error(message);
}
You might wonder why we need a ‘never’ type when we already have ‘void’. Though both types look similar, they represent two very different concepts.
A function that doesn't return a value implicitly returns the value undefined in JavaScript. Hence, even though we are saying it’s not returning anything, it’s returning ‘undefined’. We usually ignore the return value in these cases. Such a function is inferred to have a void return type in TypeScript.
What is Enum?
In TypeScript, an enum (short for "enumeration") is a way to define a set of named constants, making it easier to work with a collection of related values. Enums are useful when you have a group of related values that you want to work with in a type-safe and readable way.
Rest Parameter Syntax
function add(...values: number[]) {
let sum = 0;
values.forEach(val => sum += val);
return sum;
}
const sum = add(5, 10, 15, 20);
console.log(sum); // 50
Explain the TypeScript class syntax.?
TypeScript fully supports classes. The TypeScript syntax for class declaration is similar to that of JavaScript, with the added type support for the member declarations.
Here is a simple class that defines an Employee type
class Employee {
name: string;
salary: number;
constructor(name: string, salary: number) {
this.name = name;
this.salary = salary;
}
promote() : void {
this.salary += 10000;
}
}
// Create a new employee
let john = new Employee("John", 60000);
console.log(john.salary); // 60000
john.promote();
console.log(john.salary); // 70000
What is the purpose of the tsconfig.json file?
A tsconfig.json file in a directory marks that directory as the root of a TypeScript project. It provides the compiler options to compile the project.
What is meant by type inference?
TypeScript can infer the type of a variable when you don’t provide an explicit type. This is known as type inference. This is usually done when the variables or parameters are initialized during the declaration.
For example, TypeScript knows that the variable foo is a string, even though we don’t mention string as a type.
let foo = "this is a string";
console.log(typeof foo); // "string"
What is meant by contextual typing?
Contextual typing in TypeScript is when the type of a variable or function is inferred based on where it is used. This helps provide type safety and better editor support without needing explicit type annotations.
What is the purpose of noImplicitAny?
The noImplicitAny flag in TypeScript ensures that every variable has an explicit type. If TypeScript cannot infer a type, it will generate an error instead of assuming the type is any
Explain the various ways to control member visibility in TypeScript.?
TypeScript provides three keywords to control the visibility of class members, such as properties or methods.
- public: You can access a public member anywhere outside the class. All class members are public by default.
- protected: A protected member is visible only to the subclasses of the class containing that member. Outside code that doesn’t extend the container class can’t access a protected member.
- private: A private member is only visible inside the class. No outside code can access the private members of a class.
Does TypeScript support static classes? If not, why?
TypeScript doesn’t support static classes, unlike the popular object-oriented programming languages like C# and Java.
These languages need static classes because all code, i.e., data and functions, need to be inside a class and cannot exist independently. Static classes provide a way to allow these functions without associating them with any objects.
In TypeScript, you can create any data and functions as simple objects without creating a containing class. Hence TypeScript doesn’t need static classes. A singleton class is just a simple object in TypeScript.
Abstract Classes in TypeScript?
Abstract classes in TypeScript are classes that cannot be instantiated directly. They are designed to be extended by other classes and can contain both implementation and abstract methods (methods without a body).
Summary
- Abstract classes are used to define common behavior and structure that derived classes must follow.
- They cannot be instantiated directly and are intended to be extended.
- They are useful for shared base functionality, partial implementation of interfaces, and enforcing a structure for derived classes.
13. What are union types in TypeScript?
A union type is a special construct in TypeScript that indicates that a value can be one of several types. A vertical bar (|) separates these types.
Consider the following example where the variable value belongs to a union type consisting of strings and numbers. The value is initialized to string “Foo”. Because it can only be a string or a number, we can change it to a number later, and the TypeScript compiler doesn’t complain.
let value: string | number = "Foo";
value = 10; // Okay
What are intersection types?
Intersection types let you combine the members of two or more types by using the ‘&’ operator. This allows you to combine existing types to get a single type with all the features you need.
The following example creates a new type Supervisor that has the members of types Employee and Manager.
interface Employee {
work: () => string;
}
interface Manager {
manage: () => string;
}
type Supervisor = Employee & Manager;
// john can both work and manage
let john: Supervisor
Type Assertions in TypeScript
Type assertions in TypeScript are a way to tell the compiler to treat a value as a specific type. This can be useful when you know more about the type of a value than TypeScript does, allowing you to override its inferred type.
Syntax
There are two ways to use type assertions:
1 angle bracket syntax
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
2.as syntax (recommended in JSX and newer code):
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
Summary
- Type assertions tell the TypeScript compiler to treat a value as a specific type.
- Syntax: Use either
<Type>orvalue as Type. - Use cases: Overriding inferred types, working with DOM elements, and interfacing with third-party libraries.
How to make object properties immutable in TypeScript? (hint: readonly)?
You can mark object properties as immutable by using the readonly keyword before the property name. For example:
interface Coordinate {
readonly x: number;
readonly y: number;
}
let c: Coordinate = { x: 5, y: 15 };
c.x = 20; // Cannot assign to 'x' because it is a read-only property.
Type Declaration Files in TypeScript?
A type declaration file in TypeScript, with the extension .d.ts, provides type information about JavaScript code, allowing TypeScript to understand the types of variables, functions, and other entities in the code.
// myLibrary.d.ts
declare module "myLibrary" {
export function greet(name: string): string;
export const version: string;
}
// use of it
import { greet, version } from "myLibrary";
console.log(greet("Alice")); // Type-checked
console.log(version); // Type-checked
Summary
- Type Declaration Files (
.d.ts): Provide type information for JavaScript code. - Benefits: Enhance type safety, improve autocompletion, and serve as documentation.
- Example: Defines types for functions and constants in libraries.
Creating Custom Declarations: Write your own .d.ts files for your JavaScript code or when using third-party libraries without TypeScript support.
Explain the purpose of the ‘in’ operator.?
The in operator is used to find if a property is in the specified object. It returns true if the property belongs to the object. Otherwise, it returns false.
const car = { make: 'Hyundai', model: 'Elantra', year: 2017 };
console.log('model' in car); // true
console.log('test' in car); // false
String literal types in TypeScript allow variables or parameters to accept only specific string values, enhancing type safety, readability, and editor support. They are particularly useful for defining restricted sets of allowable values in functions, object properties, and union types.
type Direction = "up" | "down" | "left" | "right";
function move(direction: Direction) {
console.log(`Moving ${direction}`);
}
move("up"); // Valid
move("left"); // Valid
move("forward"); // Error: Argument of type '"forward"' is not assignable to parameter of type 'Direction'.
In this example:
Directionis a string literal type that can only be"up","down","left", or"right".- The
movefunction takes a parameterdirectionof typeDirection, ensuring only the specified string values can be passed.
Template Literal Types in TypeScript?
Template literal types in TypeScript extend the concept of string literal types by allowing you to create complex string patterns using template literals. This feature can be used to enforce and infer specific string formats, enhancing type safety and precision in type definitions.
type Greeting = `Hello, ${string}!`;
let greet1: Greeting = "Hello, world!"; // Valid
let greet2: Greeting = "Hi, world!"; // Error: Type '"Hi, world!"' is not assignable to type 'Greeting'.
Inheritance in TypeScript
Inheritance is a fundamental concept in object-oriented programming that allows one class to extend another, inheriting its properties and methods. TypeScript, being an object-oriented language, supports inheritance, enabling you to create hierarchical class structures and reuse code efficiently.
Key Concepts
- Base Class (Super Class):
- The class whose properties and methods are inherited by another class.
- Derived Class (Sub Class):
- The class that inherits properties and methods from the base class.
extendsKeyword:- Used to create a class that inherits from another class.
Syntax
The extends keyword is used to establish an inheritance relationship between classes.
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
makeSound(): void {
console.log("Some generic animal sound");
}
}
class Dog extends Animal {
constructor(name: string) {
super(name); // Call the constructor of the base class
}
makeSound(): void {
console.log("Woof! Woof!");
}
}
const myDog = new Dog("Buddy");
myDog.makeSound(); // Output: Woof! Woof!
console.log(myDog.name); // Output: Buddy
Generics in Typescript
Generics in TypeScript provide a way to create reusable and flexible components that can work with a variety of data types while maintaining type safety. They allow you to define functions, classes, and interfaces with placeholders for types, which are then specified when the function, class, or interface is used.
Why Use Generics?
- Reusability: Write a single function or class that works with different data types.
- Type Safety: Ensure that your code is type-checked, reducing the risk of runtime errors.
- Flexibility: Work with a variety of data types without sacrificing type information.
Basic Syntax of Generics
Generics are specified using angle brackets (< >) with a type variable. The most common type variable used is T, but you can use any valid identifier.
Example: Generic Functions
Here’s a simple example of a generic function that returns the argument it receives:
export default App;
function identity<T>(arg: T): T {
return arg;
}
// Usage
let output1 = identity<string>("Hello, Generics!");
let output2 = identity<number>(42);
console.log(output1); // Output: Hello, Generics!
console.log(output2); // Output: 42
In this example:
- The function
identitytakes a type parameterT. - The parameter
argand the return type are both of typeT. - When calling the function, we specify the type (
stringandnumberin this case).
Key Advantages of TypeScript Over JavaScript
- Static Typing:
- Catches errors at compile time, reducing runtime bugs.
- Enhanced IDE Support:
- Superior IntelliSense, autocompletion, and type checking improve developer productivity.
- Improved Readability and Maintainability:
- Type annotations make the codebase more understandable and easier to maintain.
- Early Error Detection:
- Compile-time checking helps detect errors early in the development cycle.
- Better Refactoring Tools:
- Enables powerful and safer refactoring, making large-scale changes more manageable.
- Support for Modern JavaScript Features:
- Uses the latest JavaScript features and ensures compatibility with older environments through compilation.
- Strict Null Checks:
- Prevents common errors related to null and undefined values.
- Improved Documentation:
- Type annotations act as inline documentation, making the code self-explanatory.
- Interoperability:
- Seamlessly integrates with existing JavaScript code and popular libraries/frameworks.
- Strong Community and Ecosystem:
- Extensive support and resources from a growing community.
Decorators Type in TypeScript?
In TypeScript, decorators are a special kind of declaration that can be attached to classes, methods, properties, and parameters. They provide a way to add metadata or modify the behavior of these elements at design time. Decorators are an experimental feature in TypeScript, but they are commonly used in frameworks like Angular to enhance functionality and code readability.
Types of Decorators
- Class Decorators
- Method Decorators
- Property Decorators
- Parameter Decorators
Enabling Decorators
To use decorators in TypeScript, you need to enable the experimental decorator support in your tsconfig.json file:
{
"compilerOptions": {
"experimentalDecorators": true
}
}
//example
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return `Hello, ${this.greeting}`;
}
}
What is maps file in ts?
A .map file, often referred to as a source map, is a file that maps the transformed or minified code back to its original source code. This is particularly useful in TypeScript and other transpiled languages, as it allows developers to debug the original source code even though the browser is running the compiled JavaScript code.
