What is an interface?#
// 1. Define object structure
interface Car {
brand: string;
speed: number;
drive(): void;
}
let myCar: Car = {
brand: "Toyota",
speed: 120,
drive() {
console.log("Driving at speed: " + this.speed);
}
};
// 2. Specify interface as read-only and optional
interface Point {
readonly x: number; // read-only
readonly y: number; // read-only
z?: number; // optional
}
let p1: Point = { x: 10, y: 20 };
What is the difference between type and interface?#
What is the difference between unknown and any?#
let value: unknown;
value = 42;
// Type checking is required before calling the `toFixed` method
if (typeof value === "number") {
value.toFixed(); // Correct
}
value = "Hello";
// Type checking is required before calling the `toUpperCase` method
if (typeof value === "string") {
value.toUpperCase(); // Correct
}
What is the usage of the never type?#
// Throwing an exception
function throwError(message: string): never {
throw new Error(message);
}
// Infinite loop
function infiniteLoop(): never {
while (true) {
// Infinite loop, never returns
}
}
// Exhaustive check
type Shape = 'circle' | 'square';
function getArea(shape: Shape): number {
switch (shape) {
case 'circle':
return Math.PI * 1 ** 2;
case 'square':
return 1 * 1;
default:
const _exhaustiveCheck: never = shape; // Here, `shape` will never be any other value
throw new Error(`Unexpected shape: ${shape}`);
}
}
What is the difference between never and void?#
function logMessage(message: string): void {
console.log(message); // This function returns `undefined`
}
function throwError(message: string): never {
throw new Error(message); // This function will never return
}
Explain union types, intersection types, type aliases, and type guards.#
Union Types#
- Union types allow a variable to be one of several types. Defined using the | (pipe) symbol.
- Scenario: Used to represent that a value can be one of several different types, such as function parameters that can accept multiple types of data.
let value: string | number;
function formatInput(input: string | number): string {
if (typeof input === "string") {
return input.toUpperCase();
} else {
return input.toFixed(2);
}
}
Intersection Types#
- Intersection types combine multiple types into one type, which has all the features of the combined types. Defined using & (ampersand).
- Scenario: Used to combine the properties of multiple types to create an object type that has all properties.
interface Person {
name: string;
}
interface Employee {
id: number;
}
let employee: Person & Employee = {
name: "Alice",
id: 123
};
function printEmployee(employee: Person & Employee) {
console.log(`Name: ${employee.name}, ID: ${employee.id}`);
}
Type Aliases#
- Type aliases create a new name (alias) for a type. Defined using the type keyword, applicable to basic types, union types, intersection types, etc.
- Scenario: Used to simplify complex types and improve code readability. It can create simple aliases for complex types like objects, functions, union types, etc.
type ID = string | number;
let userId: ID = "abc123";
type User = {
name: string;
age: number;
};
function greet(user: User) {
console.log(`Hello, ${user.name}`);
}
Type Guards#
Type guards are a technique in TypeScript used to determine the actual type of a value and perform further processing based on that type. They are very useful when using union types to ensure that the correct operations are executed in different type branches.
- typeof Type Guard: Used to check basic types (string, number, boolean, etc.)
function printId(id: string | number) {
if (typeof id === "string") {
console.log(id.toUpperCase());
} else {
console.log(id.toFixed(2));
}
}
- instanceof Type Guard: Used to check if an object is an instance of a certain class.
class Dog {
bark() {
console.log("Woof!");
}
}
class Cat {
meow() {
console.log("Meow!");
}
}
function makeSound(animal: Dog | Cat) {
if (animal instanceof Dog) {
animal.bark();
} else {
animal.meow();
}
}
- is Keyword: You can create custom type guards by returning a value of type is from a function.
function isString(value: any): value is string {
return typeof value === "string";
}
function printValue(value: string | number) {
if (isString(value)) {
console.log(value.toUpperCase());
} else {
console.log(value.toFixed(2));
}
}
- in Keyword: Used to check if an object has a certain property, especially when checking object types in union types.
interface Bird {
fly(): void;
}
interface Fish {
swim(): void;
}
function move(animal: Bird | Fish) {
if ("fly" in animal) {
animal.fly();
} else {
animal.swim();
}
}
What is the purpose of the typeof keyword?#
// 1. Reuse existing variable types
let person = {
name: "Alice",
age: 25,
};
type PersonType = typeof person; // { name: string; age: number; }
// 2. Function return types
function getPerson() {
return { name: "Alice", age: 25 };
}
type PersonType = typeof getPerson(); // { name: string; age: number; }
// 3. Runtime type checking in JavaScript
function printValue(value: number | string) {
if (typeof value === "string") {
console.log("This is a string: ", value);
} else {
console.log("This is a number: ", value);
}
}
What is the purpose of the keyof keyword?#
// 1. Get keys of an object type
type Person = {
name: string;
age: number;
location: string;
};
type PersonKeys = keyof Person; // 'name' | 'age' | 'location'
// 2. Type constraints (key must be a key of obj)
function getValue<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const person = { name: "Alice", age: 25, location: "NYC" };
const name = getValue(person, "name");
const age = getValue(person, "age");
How to define an enum? What are its usage scenarios?#
enum ResultEnum {
SUCCESS = 200,
ERROR = 500,
NOTLOGGED = 401,
OVERDUE = 402,
TIMEOUT = 5000,
}
enum LogLevel {
Info,
Warn,
Error
}
How to union the keys of an enum type?#
enum Direction {
Up,
Down,
Left,
Right
}
type DirectionType = keyof typeof Direction; // Up | Down | Left | Right
What are generics?#
// Simple generic function
function echo<T>(arg: T): T {
return arg;
}
echo<number>(42); // Passing number type
// Generic interface
interface Box<T> {
content: T;
}
// Default generic type
function createArray<T = string>(length: number, value: T): T[] {
return Array(length).fill(value);
}
How to constrain generic types?#
Specify that generic parameters must meet certain conditions, such as having certain properties or methods.
interface Lengthwise {
length: number;
}
function logLength<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
logLength("Hello"); // String has length property, valid
// logLength(123); // This will throw an error because number type does not have length property
What is the purpose of as? When to use it?#
let someValue: any = "This is a string";
// We know someValue is of string type
let strLength: number = (someValue as string).length;
let colors = ["red", "green", "blue"] as const;
// `colors` will have the type readonly ["red", "green", "blue"]
What is a tuple? How does it differ from a TypeScript array?#
let tuple: [string, number];
tuple = ["Hello", 42]; // Valid
// tuple = [42, "Hello"]; // Invalid, wrong order
function getUserInfo(): [string, number] {
return ["Alice", 25];
}
let entry: [string, number] = ["score", 100];
console.log(entry[0]); // Outputs "score"
// Tuple with optional elements
type FlexibleTuple = [string, number?, boolean?];
let tuple1: FlexibleTuple = ["Hello"];
What is function overloading? What is its purpose?#
// Overload signatures (without implementation)
function format(input: number): string;
function format(input: Date): string;
// Implementation signature (contains logic)
function format(input: any): string {
if (typeof input === "number") {
return input.toFixed(2); // Format number
} else if (input instanceof Date) {
return input.toISOString(); // Format date
}
}
// Different number and type of parameters
function greet(name: string): string;
function greet(firstName: string, lastName: string): string;
function greet(firstName: string, lastName?: string): string {
if (lastName) {
return `Hello, ${firstName} ${lastName}!`;
} else {
return `Hello, ${firstName}!`;
}
}
How to implement conditional types?#
Conditional types are typically written in the form T extends U ? X : Y, where T extends U indicates a condition check; if the condition is true, type X is returned; otherwise, type Y is returned. This is similar to the ternary operator in JavaScript: condition ? trueValue : falseValue.
T extends U ? X : Y
// • T extends U: Indicates the condition, checking if type T can be assigned to type U.
// • X: The type returned when T satisfies the condition (i.e., T extends U is true).
// • Y: The type returned when T does not satisfy the condition (i.e., T extends U is false).
// Example:
type IsString<T> = T extends string ? "Yes" : "No";
type A = IsString<string>; // "Yes"
type B = IsString<number>; // "No"
What are some commonly used built-in utility types?#
Partial<T>#
Makes all properties of type T optional.
const Person = {
name: string;
age: number;
}
type PartialPerson = Partial<Person>;
// PartialPerson is equivalent to { name?: string; age?: number; }
Required<T>#
Makes all properties of type T required.
interface Person {
name?: string;
age?: number;
}
type RequiredPerson = Required<Person>;
// RequiredPerson is equivalent to { name: string; age: number; }
Readonly<T>#
Makes all properties of type T readonly, preventing modification.
interface Person {
name: string;
age: number;
}
type ReadonlyPerson = Readonly<Person>;
// ReadonlyPerson is equivalent to { readonly name: string; readonly age: number; }
Pick<T, K>#
Creates a new type by selecting a subset of properties from type T. K is a union type of the properties to be selected.
interface Person {
name: string;
age: number;
address: string;
}
type PersonNameAndAge = Pick<Person, "name" | "age">;
// PersonNameAndAge is equivalent to { name: string; age: number; }
Omit<T, K>#
Constructs a new type by excluding properties specified by K from type T.
interface Person {
name: string;
age: number;
address: string;
}
type PersonWithoutAddress = Omit<Person, "address">;
// PersonWithoutAddress is equivalent to { name: string; age: number; }
Record<K,T>#
Constructs a type where keys K can be strings, numbers, etc., and values T are the types of all properties.
type Page = "home" | "about" | "contact";
type PageInfo = { title: string };
const pages: Record<Page, PageInfo> = {
home: { title: "Home Page" },
about: { title: "About Us" },
contact: { title: "Contact Us" },
};
Exclude<T,U>#
Excludes all subtypes of type U from union type T.
type A = "a" | "b" | "c";
type B = "a";
type Result = Exclude<A, B>; // "b" | "c"
Extract<T,U>#
Extracts all subtypes from union type T that can be assigned to type U.
type A = "a" | "b" | "c";
type B = "a" | "b";
type Result = Extract<A, B>; // "a" | "b"
NonNullable<T>#
Excludes null and undefined from type T.
type A = string | null | undefined;
type NonNullableA = NonNullable<A>; // string
ReturnType<T>#
Gets the return type of function type T.
function getUser() {
return { name: "Alice", age: 25 };
}
type UserType = ReturnType<typeof getUser>;
// UserType is equivalent to { name: string; age: number; }
InstanceType<T>#
Gets the instance type of constructor function type T.
class Person {
name: string = "Alice";
age: number = 25;
}
type PersonInstance = InstanceType<typeof Person>;
// PersonInstance is equivalent to Person
Parameters<T>#
Gets the parameter types of function type T, returning a tuple type.
function greet(name: string, age: number): string {
return `Hello ${name}, you are ${age} years old.`;
}
type GreetParams = Parameters<typeof greet>;
// GreetParams is equivalent to [string, number]
ConstructorParameters<T>#
Gets the parameter types of constructor function type T, returning a tuple type.
class Person {
constructor(public name: string, public age: number) {}
}
type PersonConstructorParams = ConstructorParameters<typeof Person>;
// PersonConstructorParams is equivalent to [string, number]
ThisType<T>#
Used to specify the type of the context object this, usually used in conjunction with the noImplicitThis option.
type ObjectDescriptor<D, M> = {
data?: D;
methods?: M & ThisType<D & M>; // this in M will be inferred as D & M type
};
let obj: ObjectDescriptor<{ x: number }, { foo(): void }> = {
data: { x: 10 },
methods: {
foo() {
console.log(this.x); // Inferred as number
}
}
};
Awaited<T>#
Gets the resolved type of a Promise.
type P = Promise<string>;
type Result = Awaited<P>; // string
What are declare and declare global?#
What is the difference between .d.ts and .ts files?#
How to type annotate component template instance references?#
<template>
<MyComponent ref="myComponentRef" message="Hello, World!" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import MyComponent from './MyComponent.vue';
const myComponentRef = ref<InstanceType<typeof MyComponent> | null>(null)
</script>
Explanation of typescript.json configuration!#
https://typescript.p6p.net/typescript-tutorial/tsconfig.json.html#compileoptions
{
// Compilation options
"compilerOptions": {
// Specify ECMAScript target version (e.g., ES5, ES6, ESNext).
"target": "ES6",
// Specify the module system to use
"module": "ESNext",
// Enable all strict type-checking options.
"strict": true,
// Enable interoperability support for ES modules.
"esModuleInterop": true,
// Generate .map files for debugging.
"sourceMap": true,
// Specify the compilation method for JSX code (e.g., react, react-jsx, preserve).
"jsx": "preserve",
// Specify the library files to use (e.g., DOM, ES6, ESNext).
"lib": ["ES6", "DOM"],
// Skip type checking of declaration files.
"skipLibCheck": true,
// Prohibit implicit any types without type annotations.
"noImplicitAny": true,
// Generate declaration files; enabling this will automatically generate declaration files.
"declaration": true,
// Specify the directory to store generated declaration files.
"declarationDir": "./file",
// Only generate declaration files without generating js files.
"emitDeclarationOnly": true,
// Allow export= to be imported via import from
"esModuleInterop": true,
// Resolve non-relative module base URL; default is the current directory.
"baseUrl": "./",
// Path mapping relative to baseUrl
"paths": {
"@/*": ["./src/*"]
},
// ...
},
// Included files or directories
"include": [],
// Excluded files or directories
"exclude": [],
// Specify a concrete list of files to compile, suitable for small projects.
"files": [],
// Used to support project references, suitable for large projects.
"references": []
// Inherit other configurations
"extends": ""
}