{"id":19763253,"url":"https://github.com/sougata-github/typescript","last_synced_at":"2026-02-03T16:02:20.095Z","repository":{"id":224680217,"uuid":"763899702","full_name":"sougata-github/TypeScript","owner":"sougata-github","description":"Practice files for TS.","archived":false,"fork":false,"pushed_at":"2024-08-06T06:20:06.000Z","size":210,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-22T22:37:02.654Z","etag":null,"topics":["javascript","typescript"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sougata-github.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-02-27T05:41:24.000Z","updated_at":"2024-08-06T06:20:10.000Z","dependencies_parsed_at":"2024-03-06T09:26:44.718Z","dependency_job_id":"201d0f60-b7fb-4216-8961-ec3490ee55c1","html_url":"https://github.com/sougata-github/TypeScript","commit_stats":null,"previous_names":["sougata-github/typescript"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/sougata-github/TypeScript","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sougata-github%2FTypeScript","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sougata-github%2FTypeScript/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sougata-github%2FTypeScript/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sougata-github%2FTypeScript/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sougata-github","download_url":"https://codeload.github.com/sougata-github/TypeScript/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sougata-github%2FTypeScript/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29048694,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-03T15:43:47.601Z","status":"ssl_error","status_checked_at":"2026-02-03T15:43:46.709Z","response_time":96,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript","typescript"],"created_at":"2024-11-12T04:08:36.804Z","updated_at":"2026-02-03T16:02:20.074Z","avatar_url":"https://github.com/sougata-github.png","language":"JavaScript","readme":"# TypeScript\n\nTypeScript is a superset of JavaScript, providing type safety and static type checking.\n\n## Statically Typed vs Dynamically Typed Languages\n\nIn statically typed languages, the data type of a variable is known at compile time, while in dynamically typed languages, the data type is assigned during runtime by the interpreter, depending on its value.\n\n## Usage of TypeScript\n\nTypeScript is primarily a development tool, where the project still runs JavaScript.\n\n## Syntax\n\n```typescript\nlet variableName: type = value;\n```\n\n## Running TypeScript Programs\n\nTo run a `.ts` program, follow these steps:\n\n```bash\ntsc fileName.ts\nnode fileName.js\n```\n\nThe first step involves transpiling, where the TypeScript compiler (`tsc`) transpiles the TypeScript code to JavaScript. Transpiling is the process of converting the source code from one programming language to another. It is often used in web development, especially with languages like TypeScript and JSX, which are not directly supported by web browsers.\n\n## Primitive Types: String, number, and boolean\n\nIn TypeScript, primitive types such as string, number, and boolean are used to represent basic data types.\n\nNote: JavaScript does not have a special runtime value for TypeScript types, so there's no equivalent to \"int\" or \"float\" - everything is simply number.\n\n```typescript\nlet userId: number = 334455;\n\nconsole.log(userId);\n```\n\nHowever, it's not always necessary to explicitly specify types. In some cases, TypeScript can infer types based on the assigned values. Overusing types can make the code verbose and less readable.\n\n```typescript\nlet userId = 334455;\n\nconsole.log(userId);\n```\n\n## any\n\nIn situations where TypeScript cannot determine or is unsure about the type of a value, it marks the variable as `any`. However, using `any` is discouraged as it disables type checking. It's recommended to specify types explicitly or use the compiler flag `noImplicitAny` to flag implicit `any` types as errors.\n\n## Functions in TypeScript\n\nType annotations for functions are strong and recommended.\n\n### Syntax\n\n```typescript\nfunction myFunc(param: type): returnType {\n  // Function logic\n}\n```\n\n### Arrow Functions\n\nArrow functions provide a concise syntax for writing functions.\n\n```typescript\nconst myFunc = (param: type): returnType =\u003e {\n  // Function logic\n};\n```\n\n### Providing a Default Value\n\n```typescript\nfunction signUp(name: string, email: string, hasPaid: boolean = false) {\n  return {\n    name,\n    email,\n    hasPaid,\n  };\n}\n\nconst user = signUp(\"MyName\", \"test@gmail.com\");\n```\n\n### Return Type Value\n\n```typescript\nfunction getName(name: string): string {\n  return name;\n}\n```\n\n### never\n\nSome functions never return a value. The `never` type represents values that are never observed. In a return type, this means that the function throws an exception or terminates execution of the program.\n\n```typescript\nconst fail = (message: string): never =\u003e {\n  throw new Error(message);\n};\n\nfail(\"There was an error!\");\n```\n\n## Objects\n\n### Assigning type to an object\n\n```typescript\nlet myObj: { name: string; age: number } = {\n  name: \"John\",\n  age: 22,\n};\n\nconsole.log(myObj);\n```\n\n### Bad behaviour of objects\n\nWhen a function is called with an optional parameter, it throws an error. However, if an object with an optional property is created and assigned to a variable, calling the function with that variable does not result in an error.\n\n```typescript\nfunction createUser({ name, isPaid }: { name: string; isPaid: boolean }) {\n  console.log(name);\n  console.log(isPaid);\n}\n\n// Throws an error\ncreateUser({ name: \"AAA\", isPaid: true, email: \"test@gmail.com\" });\n\n// No error\nlet newUser = { name: \"John Doe\", isPaid: true, email: \"hello@gmail.com\" };\ncreateUser(newUser);\n```\n\n## Type Aliases\n\nType Aliase is a name for any type. This is convenient, but it's common to want to use the same type more than once and refer to it by a single name.\n\n```typescript\n// Defining a type:\ntype Point = {\n  x: number;\n  y: number;\n};\n\n// Using the type\nfunction printCoord(pt: Point) {\n  console.log(`The value of X coordinate is: ${pt.x}.`);\n  console.log(`The value of y coordinate is: ${pt.y}.`);\n}\n\nprintCoord({ x: 100, y: 100 });\n```\n\n### Combining types\n\n```typescript\ntype cardNumber = {\n  cardumber: string;\n};\n\ntype cardDate = {\n  carddate: Date;\n};\n\n// Combination of the above two types\ntype cardDetails = cardNumber \u0026 cardDate;\n```\n\n### readonly \u0026 \"?\"(optional)\n\n`readonly` is used to make a property unchangeable, the value cannot be changed after it is set. `?` marks the property as optional.\n\n```typescript\ntype User = {\n  readonly _id: string;\n  name: string;\n  email: string;\n  creditDetails?: cardDetails;\n};\n\nlet user1: User = {\n  _id: \"1\",\n  name: \"John\",\n  email: \"john@gmail.com\",\n  // Since creditDetails is optional we choose not to include it.\n};\n\nconsole.log(user1);\n\n//user1._id = \"2\"; -\u003e Not allowed since property is read only\n\nlet user2: User = {\n  _id: \"2\",\n  name: \"Doe\",\n  email: \"doe@gmail.com\",\n  creditDetails: {\n    cardumber: \"123\",\n    carddate: new Date(),\n  },\n};\n\nconsole.log(user2);\n```\n\n## Arrays\n\nTo specify the type of an array like `[1,2,3]`, you can use the syntax `number[]`, this works for any type (e.g. `string[]`).\n\n```typescript\nlet list1: number[] = [1, 2, 3];\nconsole.log(list1);\n\n// Another syntax:\nconst list2: Array\u003cNumber\u003e = [4, 5, 6];\nconsole.log(list2);\n\n// Array of objects\ntype User = {\n  name: string;\n  age: number;\n};\n\nconst allUsers: User[] = [];\n\nallUsers.push(\n  {\n    name: \"John\",\n    age: 22,\n  },\n  {\n    name: \"Doe\",\n    age: 23,\n  }\n);\nconsole.log(allUsers);\n\n// 2D arrays\nconst matrix: number[][] = [\n  [1, 2, 3, 4],\n  [5, 6, 7, 8],\n];\nconsole.log(matrix);\n```\n\n## Union Type\n\nUnion types are used when a value can be more than a single type. We use the vertical bar (`|`) to separate each type.\n\n```typescript\ntype item = \"🍔\" | \"🍕\" | \"🌭\" | \"🥪\";\n\ntype addons = \"🍟\" | \"🥤\";\n\nlet myItems: { item: item; addon: addons } = { item: \"🍔\", addon: \"🍟\" };\nconsole.log(`MyItems: ${myItems.item} + ${myItems.addon}`);\n```\n\n```typescript\nconst numbers: (string | number)[] = [1, 2, \"3\"];\nconsole.log(numbers);\n```\n\nNote: Direct manipulation on individual types is not allowed because TS treats the union type as a new data type.\n\n```typescript\nfunction getId(id: number | string) {\n  //not allowed\n  //id.toUpperCase();\n\n  if (typeof id === \"string\") {\n    return id.toLowerCase();\n  }\n\n  return id;\n}\n\nconsole.log(getId(\"1234\"));\nconsole.log(getId(1234));\n```\n\n## Tuples\n\nTuples are typed arrays of pre-defined length and types for each index. The order of data type in each index should be maintained.\n\n```typescript\nconst rgb: [number, number, number] = [255, 255, 255];\nconsole.log(rgb);\n\nconst user2: [string, number] = [\"john doe\", 22];\nconsole.log(user2);\n```\n\nArray methods are allowed on tuples that violate type safety, so a good practice would be to make a tuple \"readonly\".\n\n```typescript\n//strangely this is allowed:\nuser2.push(344);\n\nconst user3: readonly [string, number] = [\"john doe\", 22];\n// this will now throw an error:\nuser3.push(344);\n```\n\n## Enums\n\nA special class that represents a group of constants, can be string or numeric.\n\n```typescript\nenum MEMBER_ROLE {\n  ADMIN = \"admin\",\n  MODERATOR = \"moderator\",\n  GUEST = \"guest\",\n}\n\nconst memberRole = MEMBER_ROLE.GUEST;\nconsole.log(memberRole);\n```\n\nFirst value is initialised to 0 by default then 1 is added to each additional value.\n\n```typescript\nconst enum ZERO_ONE {\n  ZERO,\n  ONE,\n}\n\nconst zero = ZERO_ONE.ZERO;\nconsole.log(zero);\n\nconst one = ZERO_ONE.ONE;\nconsole.log(one);\n```\n\nWe can also set the value and have it auto increment from that.\n\n```typescript\nconst enum TWO_THREE {\n  TWO = 2,\n  THREE,\n}\n\nconst two = TWO_THREE.TWO;\nconsole.log(two);\n\nconst three = TWO_THREE.THREE;\nconsole.log(three);\n```\n\nOr we can initialise all the values.\n\n```typescript\nconst enum HUNDREDS {\n  ONE = 100,\n  TWO = 200,\n  THREE = 300,\n}\n\nconst oneHundred = HUNDREDS.ONE;\nconsole.log(oneHundred);\n```\n\n## Interfaces\n\nUsed to define shape/structure of objects. Interfaces are similar to type aliases, except they only apply to object types.\n\n```typescript\ninterface rectangle {\n  height: number;\n  width: number;\n  area?(): number;\n}\n\n//rect1 adheres to interface rectangle.\nconst rect1: rectangle = {\n  height: 1.5,\n  width: 2.5,\n};\nconsole.log(rect1);\n\nconst rect2: rectangle = {\n  height: 2,\n  width: 4,\n  area: function () {\n    return this.height * this.width;\n  },\n};\n\nconst areaOfRectangle = rect2.area ? rect2.area() : \"Area cannot be computed\";\nconsole.log(areaOfRectangle);\n```\n\nInterfaces can extend each other's definition. Extending simply means you are creating a new interface with same properties as that of the original interface, plus something new.\n\n```typescript\ninterface coloredRectangle extends rectangle {\n  color: string;\n}\n\nconst coloredRect: coloredRectangle = {\n  height: 2.5,\n  width: 4.5,\n  color: \"green\",\n};\nconsole.log(coloredRect);\n```\n\n## Interfaces vs Types\n\n### Declaration Syntax\n\nType: Types can be used to define simple types, union types, intersection types, and more.\n\n```typescript\ntype MyType = number | string;\n```\n\nInterface: Interfaces are typically used for defining object shapes.\n\n```typescript\ninterface MyInterface {\n  prop1: number;\n  prop2: string;\n}\n```\n\n### Extending/Implementing\n\nType: Types can be used to create union or intersection types, but they cannot be extended or implemented.\n\n```typescript\ntype A = { prop: number };\ntype B = { prop: string };\n\ntype C = A \u0026 B; // Intersection type\n```\n\nInterface: Interfaces can extend other interfaces and can be implemented by classes.\n\n```typescript\ninterface A {\n  prop1: number;\n}\n\ninterface B {\n  prop2: string;\n}\n\ninterface C extends A, B {} // Extending interfaces\n```\n\n### Declaration Merging\n\nType: Types don't support declaration merging.\n\n```typescript\ntype MyType = { prop1: number };\ntype MyType = { prop2: string }; // Error: Duplicate identifier 'MyType'.\n```\n\nInterface: Interfaces support declaration merging, which means you can declare an interface multiple times, and their members will be merged.\n\n```typescript\ninterface MyInterface {\n  prop1: number;\n}\n\ninterface MyInterface {\n  prop2: string;\n}\n\nconst MyObj: MyInterface = { prop1: 1, prop2: \"1\" }; // Valid\n```\n\n## Setting up TypeScript for projects (Node.js)\n\n- Create `tsconfig.json` file\n\n```bash\ntsc --init\n```\n\n`tsconfig.json` is a configuration file used by the TypeScript compiler (tsc) to specify compiler options and project settings for a TypeScript project. This file allows developers to define various settings such as compilation target, module system, output directory, and more.\n\n- Initialise Node Package Manager\n\n```bash\nnpm init -y\n```\n\n- Create `dist` and `src` folders.\n\n- Create `index.html` file and make it point to `index.js` in the `dist` folder by adding a script tag.\n\n- Create `index.ts` in the `src` folder.\n\n- Specify output directory in `tsconfig.json`\n\n```json\n\"outDir\": \"./dist\"\n```\n\nAll TypeScript files will be transpiled to JavaScript files with the same name as the TypeScript files and stored in the `dist` directory.\n\n- Add content to your `.ts` file.\n\n- Compile and run `.ts` file.\n\n```bash\ntsc -w\n```\n\nThis command runs TypeScript files in \"watch mode\", allowing the compiler to detect changes and recompile automatically.\n\n## Classes in TypeScript\n\nTypeScript offers full support for the class keyword introduced in ES2015.\n\n### Here’s the most basic class - an empty one:\n\n```typescript\nclass Empty {}\n```\n\n### Fields\n\nA field declaration creates a `public` writeable property on a class.\n\n```typescript\nclass Point {\n  x = 0;\n  y = 0;\n}\n\nconst pt = new Point();\nconsole.log(pt);\n\nclass User {\n  //All fields are marked 'public' by default\n  email: string;\n  name: string;\n  age: number;\n\n  /*Fields may be prefixed with the `readonly` modifier. \n  This prevents assignments to the field outside of the constructor.*/\n  readonly role: string = \"GUEST\";\n\n  constructor(email: string, name: string = \"Username\", age: number) {\n    this.email = email;\n    this.name = name;\n    this.age = age;\n  }\n}\n\nconst user1 = new User(\"test@gmail.com\", \"John Doe\", 22);\n\n//cannot change property \"role\" since readonly:\n//user1.role=\"ADMIN\" -\u003e not allowed\n\nconsole.log(user1);\n```\n\n### Getters \u0026 Setters\n\n- A getter method returns the property’s value. A getter is also called an \u003cem\u003eaccessor\u003c/em\u003e.\n\n- A setter method updates the property’s value. A setter is also known as a \u003cem\u003emutator\u003c/em\u003e.\n\n- A getter method starts with the keyword `get` and a setter method starts with the keyword `set`.\n\nNote: A set accessor cannot have a return type annotation.\n\n```typescript\nclass Rectangle {\n  height: number;\n  width: number;\n\n  private color: string = \"\";\n\n  constructor(height: number, width: number) {\n    this.height = height;\n    this.width = width;\n  }\n\n  get area(): number {\n    return Number((this.height * this.width).toFixed(2));\n  }\n\n  get rectColor(): string {\n    return this.color;\n  }\n\n  set rectColor(color: string) {\n    if (color === \"black\" || color === \"white\") {\n      throw new Error(\"Color not valid.\");\n    }\n    this.color = color;\n  }\n}\n\nconst rectangle = new Rectangle(2.2, 4.2);\n\nrectangle.rectColor = \"green\";\n\nconsole.log(\"Area: \" + rectangle.area + \".\");\nconsole.log(\"Color: \" + rectangle.rectColor + \".\");\n\nconsole.log(rectangle);\n```\n\n### Interfaces can be implemented by classes.\n\n```typescript\ninterface TakePhoto {\n  photoMode: string;\n  filter: string;\n}\n\nclass Instagram implements TakePhoto {\n  photoMode: string;\n  filter: string;\n  constructor(photoMode: string, filter: string) {\n    this.photoMode = photoMode;\n    this.filter = filter;\n  }\n}\n\nconst instaPhoto = new Instagram(\"Manual mode\", \"Black and White\");\nconsole.log(instaPhoto);\n```\n\n### Inheritance\n\nIt is the mechanism in which one class derives the properties of another class. In TypeScript a base class inherits a parent class using the `extends` keyword.\n\n- `private` keyword: private access modifier makes the field only accesssible inside a class.\n\n- `protected` keyword: protected members are only visible to subclasses of the class they’re declared in.\n\n- `super` keyword: It allows us to invoke constructor of parent class, call parent class methods within a subclass. The `super` keyword is useful for overriding and extending the behavior of methods defined in a parent class, especially when those methods are not declared as `private`.\n\nNote: Constructors for derived class must contain a `super` call. Super must be called before accessing `this` in the constructor of a derived class.\n\n```typescript\nclass Family {\n  public name: string;\n  protected role: string | undefined;\n  private access: boolean | undefined;\n\n  constructor(name: string) {\n    this.name = name;\n  }\n\n  protected setAccess() {\n    if (this.role === \"parent\") {\n      this.access = true;\n    } else {\n      this.access = false;\n    }\n  }\n\n  family(): void {\n    console.log(\"I belong to this family.\");\n  }\n}\n\nclass Parent extends Family {\n  constructor(name: string) {\n    super(name);\n    this.role = \"parent\"; //\"role\" from Family class. (couldn't have accessed if it was private)\n    this.setAccess();\n  }\n\n  family(): void {\n    super.family();\n    console.log(\"I am a Parent and I have access.\");\n  }\n}\n\nconst parent1 = new Parent(\"John\");\nparent1.family();\nconsole.log(parent1);\n\nclass Child extends Family {\n  constructor(name: string) {\n    super(name);\n    this.role = \"child\";\n    this.setAccess();\n  }\n\n  family(): void {\n    super.family();\n    console.log(\"I am a Child and I don't have access.\");\n  }\n}\n\nconst child1 = new Child(\"Doe\");\nchild1.family();\nconsole.log(child1);\n```\n\n### Abstract classes\n\nDefine an abstract class in Typescript using the `abstract` keyword. Abstract classes are mainly for inheritance where other classes may derive from them. We cannot create an instance of an abstract class.\n\nAn abstract class typically includes one or more abstract methods or property declarations. The class which extends the abstract class must define all the abstract methods.\n\n```typescript\nabstract class Animal {\n  type: string;\n  constructor(type: string) {\n    this.type = type;\n  }\n\n  //regular method -\u003e has to be implemented when declared.\n  animalType(): void {\n    console.log(`${this.type}.`);\n  }\n\n  //abstract method -\u003e can be only implemented in the child class.\n  abstract animalSound(): string;\n}\n\nclass Dog extends Animal {\n  breed: string;\n  constructor(type: string, breed: string) {\n    super(type);\n    this.breed = breed;\n  }\n\n  animalSound(): string {\n    return \"Woof! Woof!\";\n  }\n}\n\nconst dog = new Dog(\"Mammal\", \"Husky\");\nconst dogSound = dog.animalSound();\ndog.animalType();\nconsole.log(dog);\nconsole.log(dogSound);\n```\n\n## Generics\n\nGenerics allow creating 'type variables' which can be used to create classes, functions \u0026 type aliases that don't need to explicitly define the types that they use.\n\nThey allow you to create reusable components (such as functions, classes, or interfaces) that can work with any data type while still providing type safety.\n\n```typescript\nfunction identityAny(val: any): any {\n  return val;\n}\nconsole.log(identityAny(2));\n\nfunction identityGeneric\u003cType\u003e(val: Type): Type {\n  return val;\n}\nconsole.log(identityGeneric(2));\n```\n\nWhile using `any` is certainly generic in that it will cause the function to accept any and all types for the type of arg, we actually are losing the information about what that type was when the function returns. If we passed in a number, the only information we have is that any type could be returned.\n\nInstead, we need a way of capturing the type of the argument in such a way that we can also use it to denote what is being returned. Here, we will use a type variable, a special kind of variable that works on types rather than values.\n\n### Custom generic interface\n\n```typescript\ninterface Pair\u003cT, U\u003e {\n  first: T;\n  second: U;\n}\n\nfunction logPair\u003cT, U\u003e(pair: Pair\u003cT, U\u003e): void {\n  console.log(`First: ${pair.first}, Second: ${pair.second}`);\n}\n\nconst stringNumberPair: Pair\u003cstring, number\u003e = { first: \"Hello\", second: 123 };\nlogPair(stringNumberPair);\n\nconst booleanObjectPair: Pair\u003cboolean, { name: string }\u003e = {\n  first: true,\n  second: { name: \"John\" },\n};\nlogPair(booleanObjectPair);\n```\n\nIn the above example, when you define a generic type or interface like `Pair\u003cT, U\u003e`, T and U are placeholders for the actual types that will be used when instances of Pair are created. This allows you to create instances of Pair with different types for the first and second properties without having to define a separate interface for each combination of types.\n\n### Passing an array and using arrow functions\n\n```typescript\n//use either T[] or Array\u003cT\u003e\nfunction getArray\u003cT\u003e(list: Array\u003cT\u003e): Array\u003cT\u003e {\n  return list;\n}\nconsole.log(getArray([\"1\", \"2\", \"3\"]));\n```\n\n```typescript\nconst getArrayFirst = \u003cT\u003e(list: T[]): T =\u003e {\n  return list[0];\n};\nconsole.log(getArrayFirst([\"1\", \"2\", \"3\"]));\n```\n\n### Extending a generic type\n\n```typescript\ninterface database {\n  name: string;\n  email: string;\n  age: number;\n}\n\nfunction getUser\u003cT extends database, U extends database\u003e(\n  user1: T,\n  user2: U\n): object {\n  return {\n    user1,\n    user2,\n  };\n}\n\nconst user1 = {\n  name: \"John\",\n  email: \"john@gmail.com\",\n  age: 22,\n};\n\nconst user2 = {\n  name: \"Doe\",\n  email: \"doe@gmail.com\",\n  age: 24,\n};\n\nconsole.log(getUser(user1, user2));\n```\n\n### Generic class\n\n```typescript\ninterface Mobile {\n  model: string;\n  screenSize: number;\n  color: string;\n  brand: string;\n  operatingSystem: string;\n}\n\ninterface Shirt {\n  style: string;\n  size: string;\n  color: string;\n}\n\nclass ECommerce\u003cT extends Mobile | Shirt\u003e {\n  cart: T[] = [];\n\n  addToCart(product: T) {\n    this.cart.push(product);\n  }\n}\n\nconst order = new ECommerce();\n\norder.addToCart({\n  model: \"iPhone 15 pro\",\n  screenSize: 6.12,\n  color: \"Black Titanium\",\n  brand: \"Apple\",\n  operatingSystem: \"ios 17\",\n});\n\norder.addToCart({\n  style: \"Checked\",\n  size: \"medium\",\n  color: \"Navy Blue\",\n});\n\nconsole.log(\"\\n\" + \"MyCart:\");\nconsole.log(order.cart);\n```\n\n## Type Narrowing\n\nType narrowing in TypeScript refers to the process of refining the type of a variable within a certain block of code based on certain conditions. This helps TypeScript infer more specific types, improving type safety and enabling better code optimization.\n\n```typescript\nfunction printAll(strs: string | string[] | null) {\n  if (strs) {\n    if (typeof strs === \"object\") {\n      for (const s of strs) {\n        console.log(s);\n      }\n    } else if (typeof strs === \"string\") {\n      console.log(strs);\n    }\n  }\n}\n\nprintAll(\"John Doe\");\nprintAll([\"John\", \"Doe\"]);\n```\n\nWhile it might not look like much, there’s actually a lot going on under the covers here. Much like how TypeScript analyzes runtime values using static types, it overlays type analysis on JavaScript’s runtime control flow constructs like if/else, conditional ternaries, loops, truthiness checks, etc., which can all affect those types.\n\n### `in` operator\n\nThe `in` operator is used for type narrowing, which means it helps narrow down the type of an object, based on wether a property exists within it. It's particularly useful when working with union or discriminated types.\n\n```typescript\ninterface Square {\n  kind: \"square\";\n  size: number;\n}\n\ninterface Rectangle {\n  kind: \"rectangle\";\n  width: number;\n  height: number;\n}\n\ntype Shape = Square | Rectangle;\n\nfunction calculateArea(shape: Shape): number {\n  if (\"size\" in shape) {\n    // 'shape' is of type 'Square' here\n    return shape.size * shape.size;\n  } else {\n    // 'shape' is of type 'Rectangle' here\n    return shape.width * shape.height;\n  }\n}\n\nconst square: Square = { kind: \"square\", size: 5 };\nconst rectangle: Rectangle = { kind: \"rectangle\", width: 6, height: 4 };\n\nconsole.log(calculateArea(square));\nconsole.log(calculateArea(rectangle));\n```\n\n### `instanceof` operator\n\nThe `instanceof` keyword is used to check whether an object is an instance of a particular class or constructor function. It returns true if the object is an instance of the specified class or its subclasses; otherwise, it returns false.\n\n```typescript\nfunction logValue(x: Date | string) {\n  if (x instanceof Date) {\n    console.log(x.toUTCString());\n  } else {\n    console.log(x.toUpperCase());\n  }\n}\n\nlogValue(\"Sunday, 25th Dec\");\nlogValue(new Date());\n```\n\n### Type predicate\n\nA type predicate is a way to assert the type of a variable within a conditional statement. It allows you to narrow down the type of a variable within a certain block of code, based on some condition.\n\n```typescript\ntype Fish = { swim: () =\u003e void };\ntype Bird = { fly: () =\u003e void };\n\nfunction isFish(pet: Fish | Bird): pet is Fish {\n  return (pet as Fish).swim !== undefined;\n}\n\nfunction feedPet(pet: Fish | Bird) {\n  if (isFish(pet)) {\n    pet.swim();\n    console.log(\"Fish food\");\n  } else {\n    pet.fly();\n    console.log(\"Bird food\");\n  }\n}\n\nconst myFish: Fish = { swim: () =\u003e console.log(\"Fish is swimming\") };\nconst myBird: Bird = { fly: () =\u003e console.log(\"Bird is flying\") };\n\nfeedPet(myFish);\nfeedPet(myBird);\n```\n\nIn the `isFish` function, we define a type predicate `pet is Fish`, indicating that if the condition `(pet as Fish).swim !== undefined` is true, then `pet` is indeed a `Fish`. Within the `feedPet` function, when we call `isFish(pet)`, TypeScript narrows down the type of pet to Fish if the condition is met, allowing us to safely call `pet.swim()` without TypeScript throwing errors.\n\n### Discriminated Union \u0026 Exhaustive Checking\n\nA discriminated union is a union type that utilizes a common property in each of its constituent types to allow for more precise type checking. This common property, typically called a \"discriminant\" or \"tag\", is used to discriminate between the different possible shapes within the union.\n(`kind` in this case)\n\nExhaustive checking refers to ensuring that all possible cases of the discriminated union are handled within a switch statement or if-else chain. This helps TypeScript catch errors where you might forget to handle a specific case, ensuring that your code is more robust.\n\nThe `never` type is used to indicate that a value should never occur. When used in the context of exhaustive checking with discriminated unions, it helps TypeScript ensure that all possible cases are handled, leaving no room for unexpected values.\n\n```typescript\ninterface Circle {\n  kind: \"circle\";\n  radius: number;\n}\n\ninterface Square {\n  kind: \"square\";\n  side: number;\n}\n\ntype Shape = Circle | Square;\n\nfunction getShape(shape: Shape): string {\n  if (shape.kind === \"circle\") {\n    return \"Circle\";\n  }\n  return \"Square\";\n}\n\nfunction getArea(shape: Shape): number {\n  switch (shape.kind) {\n    case \"circle\":\n      return Math.PI * shape.radius ** 2;\n    case \"square\":\n      return shape.side * shape.side;\n    default:\n      const _defaultShape: never = shape;\n      return _defaultShape;\n  }\n}\n\nconst circle: Circle = {\n  kind: \"circle\",\n  radius: 2,\n};\n\nconst square: Square = {\n  kind: \"square\",\n  side: 5,\n};\n\nconsole.log(getShape(circle));\nconsole.log(getShape(square));\n\nconsole.log(Number(getArea(circle).toFixed(2)));\nconsole.log(Number(getArea(square).toFixed(2)));\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsougata-github%2Ftypescript","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsougata-github%2Ftypescript","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsougata-github%2Ftypescript/lists"}