myHotTake

Tag: TypeScript safety

  • Why Enable TypeScript’s Strict Mode? Benefits & Examples

    If you find this story helpful, feel free to like or share it!


    I’m running a bakery, and my bakery is like a TypeScript project. Every day, I’m juggling different ingredients: flour, sugar, eggs, and more. Now, in this bakery, I have a special assistant named Strict Mode. Enabling Strict Mode is like having a meticulous quality control inspector who ensures that every ingredient is measured precisely.

    One day, I decide to enable Strict Mode. Immediately, my inspector starts scrutinizing everything. “Is this flour fresh?” they ask, “Are these eggs cracked?” At first, it feels a bit overwhelming. I think about how, before the inspector, I could just toss in ingredients without much fuss. I didn’t have to worry too much about little details, and things generally turned out okay. But now, every step is under the microscope.

    This attention to detail means my baking process slows down initially. I have to double-check measurements and inspect the ingredients more closely. It’s a bit frustrating because it feels like I’m spending more time preparing than actually baking. However, as I get used to the inspector’s guidelines, I notice something interesting: my pastries are turning out consistently better. The cookies are perfectly chewy, the cakes rise just right, and the flavors are balanced.

    Enabling Strict Mode, like having this inspector, comes with trade-offs. It requires more time and attention upfront, but it also brings a higher level of confidence in the quality of my baked goods. I can serve my customers knowing that what they’re getting is made with precision and care. So, while it might take a bit longer, the end result is worth the extra effort. And in the world of TypeScript, just like in my bakery, precision and reliability are the secret ingredients to success.


    In JavaScript, without any type checking, you might write a function like this:

    function mixIngredients(ingredient1, ingredient2) {
        return ingredient1 + ingredient2;
    }
    
    const result = mixIngredients("flour", 2);
    console.log(result); // Outputs: "flour2"

    Here, I mixed a string with a number, and JavaScript didn’t complain. My pastries might end up tasting a bit strange, but I won’t realize the mistake until it’s too late.

    Now, let’s see how enabling TypeScript’s strict mode changes the game:

    function mixIngredients(ingredient1: string, ingredient2: number): string {
        return ingredient1 + ingredient2.toString();
    }
    
    const result = mixIngredients("flour", 2);
    console.log(result); // Outputs: "flour2"

    With strict mode, TypeScript will ensure that I specify the types of my ingredients. If I try to pass a number where a string is expected or vice versa, TypeScript will throw an error before I even run the code. This is like my inspector catching a cracked egg before it makes it into the batter. It prevents unexpected results, allowing me to fix the problem right away.

    Here’s another example showing how strictNullChecks—a part of strict mode—helps:

    function getIngredient(ingredient: string | null): string {
        if (ingredient === null) {
            throw new Error("Ingredient cannot be null");
        }
        return ingredient;
    }
    
    const ingredient = getIngredient(null); // TypeScript error: Argument of type 'null' is not assignable to parameter of type 'string'.

    By enforcing checks for null or undefined, strict mode ensures that I never accidentally try to bake without all my ingredients.

    Key Takeaways:

    1. Precision and Safety: Enabling strict mode in TypeScript is like having a meticulous inspector in a bakery. It ensures that all variables (ingredients) are used correctly, catching errors early on.
    2. Initial Overhead: There might be more setup and configuration upfront, similar to spending extra time measuring ingredients precisely. It can slow down initial development but leads to more reliable outcomes.
    3. Consistency: Just as a bakery benefits from consistent quality, a codebase benefits from the reliability that strict mode brings, reducing bugs and enhancing maintainability.
  • Why Are Strict Null Checks Essential in JavaScript?

    If you find this story engaging and helpful, feel free to like or share it with others who might appreciate it!


    I have a backpack that can hold anything I need throughout the day. This backpack is amazing, but there’s a catch: it can only carry items that I have explicitly told it about. If I forget to mention an item or assume it’s in there without checking, I could reach inside and find nothing but empty space when I need it most.

    In the world of JavaScript, strict null checks are like that backpack. They ensure that I’m always aware of what I’ve packed and what I haven’t. By enabling strict null checks, I’m making a pact with my backpack to always be clear about what should be inside. If I try to pull out my water bottle, but I forgot to pack it, the backpack will immediately let me know with a gentle reminder rather than leaving me thirsty later on.

    This becomes crucial when I’m depending on various items throughout my day—like my phone, keys, or wallet. If I’m not diligent, I might assume I have everything, only to find myself locked out of my house or unable to call a friend. Similarly, in JavaScript, if I assume a variable has a value when it doesn’t, I could run into errors that stop my program in its tracks.

    By using strict null checks, I’m encouraged to double-check my backpack’s contents at every step. It makes me more mindful about what I pack and ensures I’m never caught off guard. This way, I can move through my day with confidence, knowing that everything I need is actually there.

    If you enjoyed this analogy, liking or sharing it would be awesome! It might just help someone else understand this concept too.


    I have a function that fetches my favorite book from the backpack:

    function getFavoriteBook(backpack: { favoriteBook?: string }): string {
      return backpack.favoriteBook;
    }

    Without strict null checks, I might assume the book is always packed. But if I reach for it and it’s not there, my program will throw an error, much like reaching into an empty backpack.

    By enabling strict null checks in TypeScript, like so:

    {
      "compilerOptions": {
        "strictNullChecks": true
      }
    }

    I am prompted to handle the possibility that my favoriteBook might be undefined. This means I need to check if the book is there before trying to read it:

    function getFavoriteBook(backpack: { favoriteBook?: string }): string {
      if (backpack.favoriteBook) {
        return backpack.favoriteBook;
      } else {
        throw new Error("Favorite book is not in the backpack!");
      }
    }

    With this setup, I’m always reminded to verify the presence of my favorite book before trying to read it, preventing unexpected errors.

    Here’s another example with nullish coalescing:

    function getFavoriteBookTitle(backpack: { favoriteBook?: string }): string {
      return backpack.favoriteBook ?? "No book found";
    }

    In this case, if the favoriteBook isn’t there, I’m prepared with a default message, just like having a backup plan if I forget to pack my book.

    Key Takeaways:

    • Awareness: Enabling strict null checks in TypeScript forces us to be explicit about our assumptions, preventing unexpected runtime errors.
    • Safety: Just like checking my backpack before leaving the house, strict null checks ensure that I handle potential null or undefined values gracefully.
    • Confidence: With strict null checks, I can write more reliable code, knowing that I’ve accounted for all scenarios, much like having confidence that my backpack contains everything I need.
  • How Does TypeScript Prevent Null and Undefined Errors?

    If you find this story helpful, feel free to give it a like or share it with others who might benefit!


    I’m the captain of a ship, and TypeScript is my trusty first mate. Our mission is to navigate through the unpredictable seas of JavaScript, where hidden obstacles like null and undefined errors lurk beneath the surface. These errors are like treacherous icebergs that can unexpectedly damage the ship, causing chaos in our journey.

    As the captain, I rely on my first mate, TypeScript, to constantly scan the horizon with a high-powered telescope. This telescope is special—it can detect hidden icebergs that aren’t visible to the naked eye. These icebergs represent the null and undefined values that can cause havoc if not spotted early.

    TypeScript, with its keen eye, marks these dangerous spots on our map, giving us a clear warning before we sail too close. This advanced warning system allows me to adjust the ship’s course, ensuring a smooth and safe voyage. By alerting me to potential problems, TypeScript helps me make informed decisions, steering the ship away from danger.

    Furthermore, my first mate doesn’t just point out the icebergs; TypeScript also suggests alternative routes, offering safer paths to reach our destination. This proactive approach minimizes the risk of encountering unexpected obstacles and keeps the crew and the ship secure.

    In this way, TypeScript acts as an essential navigator, helping me avoid the hidden dangers of null and undefined values in the sea of JavaScript. With TypeScript by my side, I can confidently sail towards my goals, knowing that my journey will be as smooth and error-free as possible.


    Here’s how TypeScript helps prevent null and undefined errors with some examples:

    Example 1: Non-nullable Types

    In JavaScript, we might write:

    function greet(name) {
      console.log("Hello, " + name.toUpperCase());
    }
    
    greet(null); // This will throw an error at runtime!

    Without guidance, we might accidentally crash into an iceberg by passing null or undefined to greet.

    With TypeScript, we can specify that name must be a string and cannot be null or undefined:

    function greet(name: string) {
      console.log("Hello, " + name.toUpperCase());
    }
    
    // greet(null); // TypeScript will give us an error at compile time!

    TypeScript acts as the lookout, preventing us from running into this issue by flagging the problem before we even set sail (at compile time).

    Example 2: Optional Chaining and Nullish Coalescing

    In JavaScript, accessing nested properties can be risky:

    let person = { name: "Alice", address: { street: "123 Main St" } };
    console.log(person.address.city.toUpperCase()); // Error if 'city' is undefined!

    TypeScript offers optional chaining, which acts like a safe detour around potential hazards:

    let person = { name: "Alice", address: { street: "123 Main St" } };
    console.log(person.address?.city?.toUpperCase() ?? "City not available");
    // Safely handles undefined properties

    Here, TypeScript helps us navigate safely by ensuring we don’t crash into undefined properties.

    Key Takeaways:

    1. Prevention Over Cure: TypeScript identifies potential null and undefined errors at compile time, helping to prevent runtime crashes.
    2. Guidance and Alternatives: It provides tools like non-nullable types, optional chaining, and nullish coalescing to handle these issues safely and efficiently.
    3. Confidence in Code: By catching errors early, TypeScript allows us to write more robust and reliable JavaScript code, much like how a vigilant first mate ensures a smooth voyage.
  • How Do Strict Null Checks Secure Your TypeScript Code?

    If you enjoy the story, feel free to like or share it!


    I’m an astronaut preparing for a mission to explore a mysterious new planet. Before I embark, I need to ensure that my spacecraft is in perfect condition. In this analogy, my spacecraft is like a TypeScript project, and setting it up with strict null checks is akin to performing a comprehensive pre-flight checklist to avoid any unforeseen issues.

    As an astronaut, I approach my spacecraft with a checklist in hand. This checklist is my tsconfig.json file, the configuration blueprint for my TypeScript mission. At the top of my checklist, I have a crucial item to address: “Enable strict null checks.” This is like double-checking that all my oxygen tanks are securely fastened and functioning properly. Without it, I could find myself in a precarious situation, much like encountering unexpected null or undefined values in my code.

    By flipping the switch to enable strict null checks, I ensure that every component of my spacecraft is accounted for and operational, just as I ensure that every variable in my TypeScript project is initialized correctly. This means that before I even take off, TypeScript will alert me if something is amiss—like a loose bolt or an unconnected fuel line—by throwing a compilation error if a variable might be null or undefined without being handled.

    With this precaution in place, I’m confident that my journey will be smoother and safer. I won’t have to worry about unexpected malfunctions, much like I won’t have to face runtime errors caused by null or undefined values. As I embark on my celestial adventure, I know that my spacecraft, fortified with strict null checks, is ready to tackle the unknown challenges of the cosmos.


    As I journey through the cosmos with my TypeScript-enabled spacecraft, I occasionally encounter regions where TypeScript’s strict null checks become vital tools for navigation. Suppose I have a mission to collect space samples, and I’ve got a function onboard that processes these samples. In JavaScript, this might look like:

    function processSample(sample) {
      console.log(sample.toString());
    }

    In the expanse of space (or code), it’s possible that a sample might not be collected correctly, leading to a null or undefined value. If I try to process a non-existent sample, I might face a catastrophic error, much like encountering unexpected cosmic debris.

    With TypeScript’s strict null checks enabled, my function gains an extra layer of safety:

    function processSample(sample: string | null) {
      if (sample !== null) {
        console.log(sample.toString());
      } else {
        console.log("No sample to process");
      }
    }

    Here, the strict null checks act like an onboard diagnostic system, alerting me if I attempt to handle a sample that might not exist. This ensures that my mission continues smoothly, without unanticipated run-ins with runtime errors.

    Moreover, thanks to TypeScript, if I accidentally forget to check for null:

    function processSample(sample: string | null) {
      console.log(sample.toString()); // Error: Object is possibly 'null'.
    }

    TypeScript will catch this during compilation, much like my pre-flight checklist would catch an unsecured hatch door, preventing a potentially dangerous situation in space.

    Key Takeaways:

    • Safety Net: Enabling strict null checks in TypeScript provides a safety net, catching potential null or undefined mishaps at compile time rather than at runtime.
    • Code Robustness: Just as a spacecraft needs thorough checks before launch, rigorous type checks make code more robust and error-free.
    • Early Error Detection: Catching errors early in the development process saves time and effort, much like addressing spacecraft issues on the ground rather than in orbit.
  • Unknown vs. Any in TypeScript: What’s the Real Difference?

    Hey there! If you find this story enlightening, feel free to give it a like or share it with someone who might enjoy it too!


    I’m in an ancient marketplace, with traders and storytellers. I’m on a quest to gather stories, and each storyteller here is like a data type, eager to share their tales. Now, I have two pouches: one embroidered with the word “unknown” and the other with “any.”

    First, I approach a mysterious old man. His stories are intriguing, but I can’t quite decipher their nature just from a glance. So, I use my “unknown” pouch. This pouch is special; it keeps the stories safe, but I must inspect them closely before I share them with others or weave them into my own narrative. It ensures I handle each story cautiously, taking time to understand its essence before using it.

    Next, I meet a lively merchant who eagerly hands out tales of every kind—some fascinating, others quite mundane. For these, I have my “any” pouch. It’s like a catch-all satchel, accepting any story without question. However, there’s a downside: I must be vigilant, as the stories can be unpredictable. If I share them carelessly, they might not fit well into my own tales, causing confusion or even chaos.

    As I wander the marketplace, I realize the importance of choosing the right pouch. The “unknown” pouch is my go-to when I want to delve deeper and ensure a story’s fit before sharing it. Meanwhile, the “any” pouch allows for quick collection but demands more caution when it’s time to use the gathered tales.

    So, as I continue my exploration, I learn to balance curiosity with caution, ensuring each story finds its rightful place in my collection. If this story resonated with you, feel free to share it with others who might appreciate the magic of understanding the unknown and any in our narrative quests!


    Returning to my marketplace analogy, let’s picture the “unknown” and “any” pouches as JavaScript variables, and the stories as data we want to manage. Here’s how they play out in the JavaScript world:

    // Using 'unknown'
    let unknownStory: unknown;
    unknownStory = "A tale of mystery"; // Assigning a string
    unknownStory = 42;                  // Reassigning a number
    
    // Before using unknownStory as a specific type, I need to ensure its type
    if (typeof unknownStory === "string") {
        console.log("The story is a string: " + unknownStory.toUpperCase());
    }
    
    // Using 'any'
    let anyStory: any;
    anyStory = "A tale of adventure"; // Assigning a string
    anyStory = { title: "Epic Quest" }; // Reassigning an object
    
    // I can use anyStory without type checking, but it may lead to errors
    console.log(anyStory.title); // Works, but risky if anyStory changes type

    Key Takeaways:

    1. Type Safety with unknown: Just like my “unknown” pouch, the unknown type in TypeScript requires a type check before you can perform operations on it. This ensures safety and prevents runtime errors, as I must confirm the story’s nature before telling it.
    2. Flexibility with any: The any type is like my “any” pouch, accepting any data without question. While flexible, it lacks the safety net, making it easy to introduce errors if I’m not careful about how the stories are used.
    3. Choosing the Right Tool: Use unknown when you want to enforce type checks and ensure data integrity. Opt for any when you need flexibility and are confident in handling the data with care.
  • How Do Custom Type Guards Enhance TypeScript Safety?

    If you enjoyed this story and find it helpful, feel free to like or share it with others who might benefit!


    I’m a superhero with a unique ability: I can see through disguises. In the world of TypeScript, everything is an object, and sometimes these objects wear masks, pretending to be something they’re not. My mission is to unmask them and reveal their true identities. This is where my power, which I call a “custom type guard,” comes into play.

    In this world, we have different creatures, like “Cats” and “Dogs,” each with distinct traits. A Cat might have a “meow” method, while a Dog might have a “bark” method. However, in this world, some creatures are sneaky and like to pretend they’re something they’re not. I need to ensure that if I’m talking to a Cat, it’s truly a Cat.

    So, how do I use my superpower? Picture me holding a special magnifying glass that can reveal the truth. When a creature approaches me, I hold up my magnifying glass and look for specific traits. If I see a “meow” method, I proclaim, “Aha! This is indeed a Cat!” This process is my custom type guard in action.

    To create one of these magnifying glasses, or custom type guards, I define a function. Let’s say I call it isCat. This function takes a creature as an argument and checks for the presence of the “meow” method. If it’s there, my magnifying glass glows, confirming the creature is a Cat. I can then confidently interact with it, knowing it won’t suddenly start barking like a Dog.

    In this way, my custom type guards help me navigate this world with certainty, ensuring that I’m always aware of the true nature of the creatures I encounter. It’s a bit like being a detective with a supernatural tool, always ready to uncover the truth behind the mask.


    Continuing from where I left off, my magnifying glass is actually a TypeScript function that acts as a custom type guard. Here’s how I craft it in code to ensure I’m dealing with a Cat and not a mysterious imposter:

    interface Cat {
      meow: () => void;
    }
    
    interface Dog {
      bark: () => void;
    }
    
    // Here's my magnifying glass, or custom type guard function
    function isCat(creature: any): creature is Cat {
      return (creature as Cat).meow !== undefined;
    }
    
    // Now, let's see it in action with some creatures
    const unknownCreature1 = { meow: () => console.log("Meow!") };
    const unknownCreature2 = { bark: () => console.log("Woof!") };
    
    if (isCat(unknownCreature1)) {
      unknownCreature1.meow(); // Confidently call meow because I know it's a Cat
    } else {
      console.log("This is not a Cat.");
    }
    
    if (isCat(unknownCreature2)) {
      unknownCreature2.meow(); // This won't run because it's not a Cat
    } else {
      console.log("This is not a Cat.");
    }

    In this code, I create a function isCat that checks if the meow method is present, which is my way of exposing the true identity of the creature. This function returns a boolean, but it also informs TypeScript that if the function returns true, the type of the argument can safely be treated as a Cat.

    Key Takeaways:

    1. Custom Type Guards: They are functions that help TypeScript infer more specific types based on runtime checks. They use the syntax creature is Cat to refine types.
    2. Type Safety: By using custom type guards, we can safely access properties or methods specific to a type without risking runtime errors.
    3. Flexibility: Custom type guards provide flexibility in handling objects whose types may not be immediately apparent, especially in dynamic or loosely typed data scenarios.
    4. Efficiency: They allow us to write code that’s both efficient and expressive, leveraging TypeScript’s type system to manage complex data interactions.
  • How to Ensure Exhaustive Checks in TypeScript Switch Cases

    If you find this story helpful, feel free to like or share it!


    I’m a detective in a city, solving a mysterious case. The city is divided into different districts, each with its own unique character and set of rules. My mission is to ensure every district is accounted for in the investigation, leaving no stone unturned.

    In this case, the districts represent the different cases in a TypeScript switch statement. Each district (or case) has its own way of being handled, much like how each case in a switch statement has its distinct block of code. As a meticulous detective, I need to ensure that every district is considered, so I won’t miss any crucial clues.

    Now, as I’m working through the districts, I have a trusty sidekick, TypeScript, who helps me enforce exhaustive checks. TypeScript is like my magnifying glass, highlighting any districts that haven’t been accounted for. It ensures that I don’t overlook any part of the city, just like it ensures I don’t miss any possible cases in my switch statement.

    As I wrap up my investigation, I encounter an unexpected twist—a new district that wasn’t on my map. This is where TypeScript’s “never” type comes into play. If I miss a district, TypeScript throws a red flag, much like my sidekick nudging me to pay attention. It tells me, “Hey, detective, there’s a district you haven’t considered!”

    By using this approach, I ensure that my investigation is thorough and complete, much like how TypeScript ensures that all possible cases in a switch statement are covered. With TypeScript’s help, I can confidently solve the mystery, knowing that every district has been accounted for and no crucial details have been missed.

    And just like that, with a complete investigation, I can close the case with confidence, knowing that every part of my city has been explored.


    In the world of TypeScript, each district corresponds to a case in a switch statement. Let’s say I’m investigating different types of cases represented by a union type, like so:

    type CaseType = "burglary" | "fraud" | "vandalism";
    
    function investigateCase(caseType: CaseType) {
        switch (caseType) {
            case "burglary":
                console.log("Investigating burglary...");
                break;
            case "fraud":
                console.log("Investigating fraud...");
                break;
            case "vandalism":
                console.log("Investigating vandalism...");
                break;
            default:
                // This should never happen
                const _exhaustiveCheck: never = caseType;
                throw new Error(`Unhandled case: ${caseType}`);
        }
    }

    In this investigation, CaseType is a union type representing all the possible districts (case types) I need to explore. Each case in the switch corresponds to a district with its own handling logic.

    Here’s where the magic happens: the default case. In my detective work, this is like my trusty sidekick, TypeScript, ensuring I didn’t miss any district. The line const _exhaustiveCheck: never = caseType; is my safety net. If I ever encounter a case type that I forgot to handle, TypeScript will alert me by throwing a compile-time error. This tells me, “Detective, there’s a district you haven’t covered!”

    By enforcing this check, I ensure that any future additions to CaseType—like a new district popping up—won’t slip through unnoticed. TypeScript forces me to handle them, ensuring my investigation remains exhaustive.

    Key Takeaways:

    1. Exhaustiveness: Using TypeScript’s type system, you can enforce exhaustive checks in switch statements to ensure all possible cases are covered.
    2. Safety Net: The never type acts as a guard, alerting you at compile time if there’s an unhandled case, much like a detective’s sidekick ensuring no district is missed.
    3. Future-Proof: This approach makes your code robust against future changes, ensuring new cases are handled as soon as they’re introduced.
  • How Does TypeScript’s Type Inference Simplify JavaScript?

    Hey there! If you’re enjoying this story and find it helpful, feel free to like or share it with others who might appreciate it too.


    I’m a detective. Not one with a magnifying glass and a deerstalker hat, but one who specializes in identifying the unknown. My job is to walk into a room full of mysterious objects and instantly understand what each one is and how it should be used.

    So, here I am, stepping into a room filled with various items. There’s a tall glass of water, a shiny red apple, and a sleek silver laptop. As a detective, I don’t need anyone to tell me what these objects are — I can infer their identities just by looking at them. That glass of water? It’s for drinking. The apple? A healthy snack. The laptop? Perfect for typing up reports.

    Now, let’s transport this analogy to the world of TypeScript. In the vast landscape of programming, TypeScript is like me, the detective. When I write code, I might declare a variable and immediately assign it a value, like let age = 25. TypeScript, using its detective skills, looks at the value 25 and instantly knows that age is a number. I didn’t have to explicitly say, “Hey TypeScript, age is a number.” It just knows.

    This inference saves me from having to label everything manually. Just like I don’t need to put a sticker on the apple saying “APPLE” for me to know what it is, TypeScript doesn’t need extra instructions to understand the types of many variables based on the values I give them.

    But just like any good detective, sometimes I need to be crystal clear. If an object is ambiguous, like a mysterious, unmarked bottle, I might need to investigate further to ensure it’s safe. Similarly, in TypeScript, when the type isn’t obvious, I can step in and explicitly inform it, keeping everything clear and precise.

    So, in the world of my detective work, TypeScript’s type inference is like my ability to walk into a room and understand the nature of things without needing every detail spelled out. It’s efficient, intuitive, and keeps the code organized and understandable. And that’s how TypeScript’s type inference works, making our coding lives a little bit easier and more intuitive.


    In code terms, this freedom looks like this:

    let mysteryItem = 42; // Initially, it's a number
    mysteryItem = 'Now I am a string'; // Later, it's a string

    As a JavaScript detective, I have to be on my toes. I need to be aware that mysteryItem could change its identity at any moment. This flexibility is powerful but can be tricky to manage as projects grow.

    Enter TypeScript, my trusty detective partner, ensuring the mystery stays solved. TypeScript steps in and says, “Let’s keep things consistent.” When I declare a variable with an initial value, TypeScript remembers its type:

    let mysteryItem: number = 42; // Clearly defined as a number
    // mysteryItem = 'Now I am a string'; // Error: Type 'string' is not assignable to type 'number'

    TypeScript uses its type inference skills to understand that mysteryItem is a number, and it makes sure I don’t accidentally change it into something else later. This brings clarity and safety to my investigation.

    Here’s another example of how TypeScript helps keep things organized:

    function add(a: number, b: number) {
      return a + b;
    }
    
    let result = add(5, 10); // TypeScript knows 'result' is a number

    In this function, TypeScript deduces that add returns a number because both a and b are numbers. It keeps track of this information without me having to spell it out every time.

    Key Takeaways:

    1. Type Inference: TypeScript acts as a detective, inferring the types of variables based on the values assigned to them. This minimizes the need for explicit typing, making code cleaner and more readable.
    2. Safety Nets: By understanding the types, TypeScript helps prevent errors that might occur if a variable changes its type unexpectedly, offering a safety net that pure JavaScript doesn’t provide.
    3. Clarity and Consistency: TypeScript offers clarity and consistency, making it easier to manage large codebases by ensuring that variables and functions behave as expected.