Module 6: Explore Advance Types of Typescript
6-1 Type Assertion
Type assertion in TypeScript is a way to tell the compiler "trust me, I know what I'm doing" about the type of a value. You can use angle-bracket (<Type>) or as syntax.
Example - Type Assertion using as
let someValue: unknown = "Hello World!";
let strLength: number = (someValue as string).length;
console.log(strLength); // 12
Example - Type Assertion using angle-bracket
let anotherValue: any = 123.456;
let intValue: number = <number>anotherValue;
console.log(intValue); // 123.456
Note: Type assertion doesn't actually change the type at runtime, it just tells the TypeScript compiler to assume a certain type.
6-2 Type Interface
An interface in TypeScript is like a blueprint for objects. It tells you what properties and methods an object should have, so TypeScript can check if your object matches the blueprint.
interface IUser {
name: string;
age: number;
}
interface IUserWithRole extends IUser {
role: "admin" | "user";
}
const user1: IUserWithRole = { name: "Robin", age: 30, role: "admin" };
interface IFriends {
[index: number]: string;
}
const friends: IFriends = ["A", "B", "C"];
Note: Interface only work on
Objecttype in jsarray,object,function
6-3 Generics in TypeScript
Generics allow you to write reusable and flexible code that works with any type.
type GenericArray<T> = Array<T>;
const userList: GenericArray<{ name: string; age: number }> = [
{ name: "Rakib Rahman", age: 42 },
];
const runList: GenericArray<number> = [12, 43, 66, 22];
6-4 Generics with interface
interface Developer<T, X = null> {
name: string;
salary: number;
device: {
brand: string;
model: string;
};
smartWatch: T;
bike?: X;
}
interface NonBrandWatch {
heartRate: string;
stopwatch: boolean;
}
interface BrandWatch {
heartRate: string;
callSupport: boolean;
calculator: boolean;
NC: boolean;
}
const dev1: Developer<NonBrandWatch, { brand: "HERO"; cc: "200cc" }> = {
name: "Mr. Dev1",
salary: 200,
device: {
brand: "Asus",
model: "S510",
},
smartWatch: {
heartRate: "80bpm",
stopwatch: true,
},
bike: {},
};
const dev2: Developer<BrandWatch> = {
name: "Mr. Dev2",
salary: 100,
device: {
brand: "hp",
model: "X34",
},
smartWatch: {
heartRate: "75bpm",
callSupport: true,
calculator: true,
NC: false,
},
bike = null,
};
6-5 Generics with function
// Generic Function
const createArrayWithGeneric = <T>(value: T): T[] => {
return [value];
};
const numberArray = createArrayWithGeneric(123); // number[]
const stringArray = createArrayWithGeneric("hello"); // string[]
const booleanArray = createArrayWithGeneric<boolean>(true); // boolean[]
// Generic Tuple
const createArrayTupleWithGeneric = <X, Y>(param1: X, param2: Y) => {
[param1, param2];
};
const tuple1 = createArrayTupleWithGeneric("Laptop", false);
6-6 Constraint in typescript
// Constraints make sure generics only accept certain types. Set rules for generics using extends
const addStudent = <T extends { id: number; name: string }>(studentInfo: T) => {
return { Course: "Programming", ...studentInfo };
};
const s1 = { id: 123, name: "Dev1", useLinux: true };
const result = addStudent(s1);
6-7 keyof constraint with generics
type popularVehicle = {
car: sting;
bike: string;
cng: string;
};
type myFav = keyof popularVehicle;
const myVehicle: myFav = "car";
type userType = {
id: number;
name: string;
address: {
home: string;
roadNo: number;
};
};
const user1: userType = {
id: 123,
name: "Hasan",
address: {
home: "Dhaka",
roadNo: 1,
},
};
const getPropertyFromObj = (obj: userType, key: keyof userType) => {
return obj[key];
};
// generic
const getPropertyFromObj = <T>(obj: T, key: keyof T) => {
return obj[key];
};
const result = getPropertyFromObj(user1, "name");
6-8 Explore Enum
// Enum lets you create a list of named values in TypeScript.
enum Direction {
Up,
Down,
Left,
Right,
}
const move = (dir: Direction) => {
console.log(`Moving ${Direction[dir]}`);
};
move(Direction.Up); // Output: Moving Up
Note: To use enum in node. we have to use
--experimental-transform-typesflag
In TypeScript enum are not recommended because they generate runtime code, break with bundlers, behave inconsistently, and are easily replaced by safer union types or as const objects.
6-9 Use as const instead of enum
// You can use "as const" with objects to get enum-like behavior in TypeScript
const Directions = {
Up: "Up",
Down: "Down",
Left: "Left",
Right: "Right",
} as const;
type Direction = (typeof Directions)[keyof typeof Directions];
const move = (dir: Direction) => {
console.log(`Moving ${dir}`);
};
move(Directions.Up); // Output: Moving Up
6-10 Explore conditional type
type A = null;
type B = undefined;
type C = A extends null ? true : false;
type vehicle = {
bike: string;
car: string;
ship: string;
};
type checkVehicle<T> = T extends keyof vehicle ? true : false;
type hasBike = checkVehicle<"cycle">;
6-11 Explore mapped types
type Person = {
name: string;
age: number;
email: string;
};
// Make all properties of Person optional
type PartialPerson = {
[Key in keyof Person]?: Person[Key];
};
// Make all properties of Person readonly
type ReadonlyPerson = {
readonly [Key in keyof Person]: Person[Key];
};
// Example usage
const partial: PartialPerson = { name: "Jibon" }; // age and email are optional
const readonlyPerson: ReadonlyPerson = {
name: "Jahid",
age: 30,
email: "jahid@example.com",
};
type GenericReadonlyPerson<T> = {
readonly [Key in keyof T]: T[Key];
};
const p1: GenericReadonlyPerson<{ name: string; age: number }> = {
name: "Hasan",
age: 20,
};
6-12 Explore Utility types
type PersonType = {
id: number;
name: string;
age?: number;
};
// Partial<T> make optional
type PartialVehicle = Partial<PersonType>;
const partialVehicle: PartialVehicle = { id: 123 };
// Required<T>
type RequiredPerson = Required<Person>;
const person1: RequiredPerson = {
id: 123
name: "Hasib",
age: 25,
};
// Readonly<T>
type ReadonlyVehicle = Readonly<{
bike: string;
car: string;
ship: string;
}>;
const readonlyVehicle: ReadonlyVehicle = {
bike: "Hero",
car: "BMW",
ship: "Titanic",
};
// readonlyVehicle.bike = "Yamaha"; // Error: Cannot assign to 'bike' because it is a read-only property
// Record<K, T>
type CountryCode = "BD" | "US" | "UK";
type CountryCallingCodes = Record<CountryCode, string>;
const callingCodes: CountryCallingCodes = {
BD: "+880",
US: "+1",
UK: "+44",
};
// Pick<T, K>
type FullPerson = {
name: string;
age: number;
email: string;
};
type PersonNameAndEmail = Pick<FullPerson, "name" | "email">;
const nameAndEmail: PersonNameAndEmail = {
name: "Rafi",
email: "rafi@example.com",
};
// Omit<T, K>
type PersonWithoutEmail = Omit<FullPerson, "email">;
const personWithoutEmail: PersonWithoutEmail = {
name: "Sumaiya",
age: 22,
};
// Exclude<T, U>
type Letters = "A" | "B" | "C";
type ExcludedLetters = Exclude<Letters, "A">; // "B" | "C"
// Extract<T, U>
type Numbers = 1 | 2 | 3;
type ExtractedNumbers = Extract<Numbers, 2 | 3 | 4>; // 2 | 3
// NonNullable<T>
type NullableType = string | null | undefined;
type NonNullableType = NonNullable<NullableType>; // string
// ReturnType<T>
function getUser() {
return { name: "Karim", age: 28 };
}
type UserType = ReturnType<typeof getUser>;
// Parameters<T>
function sum(a: number, b: number): number {
return a + b;
}
type SumParameters = Parameters<typeof sum>; // [number, number]