Unit5 - Subjective Questions
INT219 • Practice Questions with Detailed Answers
What is TypeScript and how does it relate to JavaScript?
TypeScript is an open-source programming language developed and maintained by Microsoft. It is a strict syntactical superset of JavaScript.
Key Relationships:
- Superset: Any valid JavaScript code is also valid TypeScript code. TypeScript adds optional static typing to the language.
- Transpilation: Browsers cannot execute TypeScript directly. It must be compiled (transpiled) into plain JavaScript using the TypeScript compiler () or Babel.
- Evolution: TypeScript introduces features from future JavaScript specifications (ECMAScript) before they are officially supported in all browsers.
Explain the key benefits of using TypeScript in front-end development.
The key benefits of using TypeScript include:
- Static Typing: It catches type-related errors at compile-time rather than runtime, significantly reducing bugs.
- Enhanced IDE Support: Provides superior IntelliSense, code completion, and navigation features (like 'Go to Definition') in editors like VS Code.
- Readability and Maintainability: Explicit types serve as documentation, making the code easier to understand and maintain for large teams.
- Refactoring: It makes refactoring large codebases safer and easier, as the compiler flags broken dependencies immediately.
- Modern JavaScript: It allows developers to use modern ES6+ features while maintaining compatibility with older browsers via transpilation.
Describe the concept of Type Annotations in TypeScript with examples.
Type Annotations are a way to explicitly describe the type of a variable, function parameter, or function return value. They are used by the TypeScript compiler to enforce type checking.
Syntax:
The syntax uses a colon : followed by the type.
Examples:
typescript
// Variable annotation
let username: string = "JohnDoe";
let age: number = 25;
// Function parameter and return type annotation
function add(a: number, b: number): number {
return a + b;
}
If a value does not match the annotated type, the compiler throws an error.
Differentiate between the any, unknown, and void types in TypeScript.
-
any:- Disables type checking for the variable.
- Allows any operation on the value (properties, methods).
- Use case: Migrating JS code to TS or when the type is truly dynamic.
-
unknown:- Similar to
anyin that it accepts any value. - Difference: It is safer because you cannot perform operations on an
unknownvariable without first checking (narrowing) its type.
- Similar to
-
void:- Represents the absence of a value.
- Use case: Commonly used as the return type for functions that do not return a value.
Example:
typescript
let x: any = 10; x.foo(); // No Error (but crashes at runtime)
let y: unknown = 10; y.foo(); // Compile Error
function log(): void { console.log("Hi"); } // Returns nothing
What is a Tuple in TypeScript and how is it different from an Array?
Array:
An array in TypeScript typically holds a collection of elements of the same type (though they can be unions), and it has a variable length.
typescript
let scores: number[] = [10, 20, 30];
Tuple:
A Tuple is a special type of array that contains a fixed number of elements where the type of each element is known and need not be the same.
typescript
// A tuple representing a string and a number
let user: [string, number];
user = ["Alice", 30]; // Valid
user = [30, "Alice"]; // Error: Type order mismatch
Difference: Tuples enforce specific types at specific index positions and a fixed initial length, whereas Arrays generally enforce a single type for all elements and dynamic length.
Explain the concept of Interfaces in TypeScript. How do optional and readonly properties work within an interface?
Interfaces define the structure (contract) of an object. They specify the names of properties and the types of values those properties must hold, without implementing the logic.
Optional Properties (?):
Properties that are not required for the object to be valid. Marked with a ?.
Readonly Properties (readonly):
Properties that can only be assigned when the object is created. They cannot be changed afterwards.
Example:
typescript
interface Car {
readonly vin: string; // Cannot change after creation
model: string;
year?: number; // Optional
}
let myCar: Car = { vin: "123", model: "Tesla" };
// myCar.vin = "456"; // Error: readonly
Compare and contrast Interfaces and Type Aliases.
Both Interfaces and Type Aliases are used to define shapes and custom types, but they have distinct differences:
| Feature | Interface | Type Alias |
|---|---|---|
| Definition | Defines the shape of an object. | Defines a name for any type (object, primitive, union, etc.). |
| Syntax | interface User { name: string; } |
type User = { name: string; }; |
| Extensibility | Supports Declaration Merging (defining the same interface twice merges them). | Does not support declaration merging. |
| Extension | Uses extends keyword. |
Uses Intersection (&) to extend. |
| Use Case | Preferred for defining object shapes and API contracts. | Preferred for Unions, Intersections, Tuples, and Primitives. |
Recommendation: Use interface by default for objects; use type when you need specific features like Unions.
Describe Union Types in TypeScript. Provide a scenario where they are useful.
Union Types allow a variable to hold values of multiple distinct types. They are defined using the pipe symbol ().
Syntax:
typescript
let identifier: TypeA | TypeB;
Scenario:
Imagine a function parameter that accepts padding. The padding could be a specific number (pixels) or a string (like "20px" or "auto").
Example:
typescript
function setPadding(padding: number | string) {
if (typeof padding === "number") {
return ${padding}px;
}
return padding;
}
This allows flexibility in the API while still maintaining type safety (you can't pass a boolean, for instance).
What are Intersection Types? How are they represented syntactically?
Intersection Types combine multiple types into one. The resulting type has all the features (properties and methods) of the combined types. This is often used to mixins or to combine interface definitions.
Syntax:
Uses the ampersand symbol ().
Example:
typescript
interface Draggable {
drag: () => void;
}
interface Resizable {
resize: () => void;
}
// UIElement has BOTH drag and resize methods
type UIElement = Draggable & Resizable;
let box: UIElement = {
drag: () => {},
resize: () => {}
};
Explain the concept of Generics in TypeScript. Why are they considered a crucial feature for reusable code?
Generics allow you to write code components (functions, classes, interfaces) that can work with a variety of types rather than a single one. It works like a variable for types, often denoted as .
Why they are crucial:
- Reusability: You can write a function once and use it with strings, numbers, or custom objects without rewriting code.
- Type Safety: unlike using
any, Generics preserve the type information. If you put a string in, the compiler knows a string comes out.
Example:
typescript
function identity<T>(arg: T): T {
return arg;
}
let output1 = identity<string>("myString");
let output2 = identity<number>(100);
Write a TypeScript function using Generics to create a generic class Box that can store a value of any type.
Here is a Generic Class implementation:
typescript
// Generic Class Definition
class Box<T> {
private _contents: T;
constructor(value: T) {
this._contents = value;
}
getContents(): T {
return this._contents;
}
setContents(value: T): void {
this._contents = value;
}
}
// Usage
const numberBox = new Box<number>(123);
console.log(numberBox.getContents()); // Output: 123
const stringBox = new Box<string>("Hello World");
console.log(stringBox.getContents()); // Output: Hello World
In this example, captures the type passed during instantiation, ensuring numberBox only accepts numbers and stringBox only accepts strings.
Explain Generic Constraints using the extends keyword.
Sometimes, when writing a generic, we need to ensure that the type parameter () possesses certain properties. Generic Constraints allow us to restrict the types that can be used with a generic by using the extends keyword.
Example:
We want a function that logs the .length of an argument. Not all types have a .length property (e.g., numbers don't). We must constrain .
typescript
interface Lengthwise {
length: number;
}
// T must implement the Lengthwise interface
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // Now valid because T is constrained
return arg;
}
loggingIdentity("Hello"); // Valid (string has length)
loggingIdentity([1, 2]); // Valid (array has length)
// loggingIdentity(3); // Error: number doesn't have length
What is Type Inference in TypeScript? Give examples of when it occurs.
Type Inference is the ability of the TypeScript compiler to automatically deduce the type of a variable or expression when no explicit type annotation is provided.
When it occurs:
-
Variable Initialization:
typescript
let x = 3; // TypeScript infers x is 'number'
// x = "hello"; // Error -
Function Return Types:
typescript
function add(a: number, b: number) {
return a + b; // Infers return type is 'number'
} -
Best Common Type:
typescript
let arr = [0, 1, null]; // Infers (number | null)[]
Type inference reduces verbosity while maintaining type safety.
Explain Type Narrowing and describe how typeof and instanceof are used as Type Guards.
Type Narrowing is the process of refining a less specific type (like string | number or unknown) to a more specific type within a conditional block.
Type Guards:
-
typeof: Used for primitive types (string, number, boolean).
typescript
function printId(id: string | number) {
if (typeof id === "string") {
console.log(id.toUpperCase()); // TS knows id is string here
} else {
console.log(id); // TS knows id is number here
}
} -
instanceof: Used for class instances.
typescript
class Dog { bark() {} }
class Cat { meow() {} }function sound(animal: Dog | Cat) {
if (animal instanceof Dog) {
animal.bark(); // TS knows animal is Dog
}
}
What are Discriminated Unions? How do they aid in type narrowing?
Discriminated Unions (also known as Tagged Unions) are a pattern in TypeScript involving a union of interfaces that all share a common singleton property (the "discriminant" or "tag").
How they aid narrowing:
TypeScript can check the value of the shared property to infer which specific interface inside the union is currently being used.
Example:
typescript
interface Circle { kind: "circle"; radius: number; }
interface Square { kind: "square"; side: number; }
type Shape = Circle | Square;
function getArea(s: Shape) {
switch (s.kind) { // 'kind' is the discriminant
case "circle":
// TS knows 's' is Circle here
return Math.PI * s.radius 2;
case "square":
// TS knows 's' is Square here
return s.side 2;
}
}
Describe the TypeScript Compilation Workflow.
Since browsers and Node.js (by default) cannot execute TypeScript, the workflow involves a transformation process:
- Write Code: Developer writes code in
.tsfiles using TypeScript features (types, interfaces, etc.). - Configuration: The project typically contains a
tsconfig.jsonfile defining compilation rules (target JS version, module system, strictness). - Transpilation: The TypeScript Compiler (
tsc) reads the configuration and source files.- It performs static type checking. If errors exist, compilation may stop or warn.
- It strips out all type information (interfaces, annotations).
- Output: The compiler generates standard JavaScript (
.js) files. - Execution: The resulting JavaScript is executed in the browser or server environment.
What is the role of tsconfig.json? List and explain three common compiler options.
tsconfig.json is the root configuration file for a TypeScript project. It specifies the root files and the compiler options required to compile the project.
Common Options:
target: Specifies the ECMAScript target version for the output JavaScript (e.g.,"ES5","ES6","ES2020"). Lower targets produce more polyfills/verbose code for compatibility.module: Specifies the module code generation (e.g.,"CommonJS"for Node.js,"ESNext"for modern frontend bundlers).strict: A shorthand that enables a wide range of type-checking behavior (likenoImplicitAny,strictNullChecks) to ensure the highest level of type safety.
Explain the significance of the strictNullChecks flag in TypeScript configuration.
By default in older TS versions, null and undefined are assignable to every type (e.g., you could assign null to a number variable). This often leads to runtime errors like "Cannot read property of null".
strictNullChecks: true changes this behavior:
nullandundefinedbecome distinct types.- If you want a variable to accept null, you must explicitly define it using a union type:
let name: string | null;. - You cannot access properties of a variable that might be null without checking first (narrowing).
This flag significantly improves the robustness of the code by forcing developers to handle null cases explicitly.
How do Enum types work in TypeScript? Provide an example of a numeric enum.
Enums allow a developer to define a set of named constants. TypeScript provides both numeric and string-based enums.
Numeric Enum Example:
By default, enums are zero-indexed, auto-incrementing numbers.
typescript
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right // 3
}
let move: Direction = Direction.Up;
You can also set a starting value:
typescript
enum Status {
Active = 1,
Inactive // 2
}
Enums make code more readable (using names instead of "magic numbers") and provide reverse mappings (getting the name from the value) in numeric enums.
Explain the concept of Literal Types and how they relate to Union types.
Literal Types allow you to define a type that is not just "string" or "number", but a specific string or number value.
Relation to Unions:
On their own, literal types aren't very useful (e.g., let x: "hello" = "hello" can only ever be "hello"). However, they are powerful when combined with Union Types to create a set of allowed values.
Example:
typescript
// String Literal Union
type Alignment = "left" | "right" | "center";
function alignText(align: Alignment) {
// ...
}
alignText("left"); // Valid
// alignText("top"); // Error: Argument of type '"top"' is not assignable
This enforces that the variable can only hold one of the exact specified values.