DEV Community

Vishal Yadav
Vishal Yadav

Posted on

Comparing JavaScript and TypeScript: Key Differences and Features

JavaScript and TypeScript share many similarities, but TypeScript extends JavaScript by adding static types and other powerful features that enhance code quality and development experience. In this blog, we will compare various aspects of JavaScript (JS) and TypeScript (TS), including handling this, type annotations, and error handling, among others.

Handling this

JavaScript

In JavaScript, the value of this depends on how a function is called. This can lead to some confusion and unexpected behavior.

const obj = {
    name: "Alice",
    greet: function() {
        console.log(this.name);
    },
};

obj.greet(); // Alice

const greet = obj.greet;
greet(); // undefined (or global object in non-strict mode)
Enter fullscreen mode Exit fullscreen mode

In the example above, this refers to obj when greet is called as a method, but when the method is assigned to a variable and called, this is not bound to obj.

TypeScript

TypeScript doesn't change how this works in JavaScript, but it provides better tools to ensure this is used correctly.

class Person {
    name: string;

    constructor(name: string) {
        this.name = name;
    }

    greet() {
        console.log(this.name);
    }
}

const alice = new Person("Alice");
alice.greet(); // Alice

const greet = alice.greet;
greet(); // undefined
Enter fullscreen mode Exit fullscreen mode

TypeScript classes help mitigate this issues by enforcing type checks and providing compile-time errors if this is used incorrectly.

Arrow Functions

Both JavaScript and TypeScript support arrow functions, which do not have their own this context. Instead, they inherit this from the enclosing scope.

function Timer() {
    this.seconds = 0;
    setInterval(() => {
        this.seconds++;
        console.log(this.seconds);
    }, 1000);
}

const timer = new Timer();
Enter fullscreen mode Exit fullscreen mode

In the example above, the arrow function inside setInterval ensures this refers to the Timer instance.

Type Annotations

JavaScript

JavaScript is a dynamically typed language, meaning variables can change types at runtime.

let message = "Hello, world!";
message = 42; // No error, but not ideal
Enter fullscreen mode Exit fullscreen mode

TypeScript

TypeScript introduces static types, allowing developers to declare variable types explicitly. This helps catch type-related errors during development.

let message: string = "Hello, world!";
// message = 42; // Error: Type 'number' is not assignable to type 'string'
Enter fullscreen mode Exit fullscreen mode

Using type annotations, TypeScript provides compile-time checks, preventing type mismatches.

Interfaces

JavaScript

JavaScript doesn't have built-in support for interfaces, but developers often use objects and JSDoc annotations to mimic interface-like behavior.

/**
 * @typedef {Object} User
 * @property {string} name
 * @property {number} age
 */

/**
 * @param {User} user
 */
function printUser(user) {
    console.log(`Name: ${user.name}, Age: ${user.age}`);
}

const user = { name: "Alice", age: 30 };
printUser(user);
Enter fullscreen mode Exit fullscreen mode

TypeScript

TypeScript has native support for interfaces, providing a clear and concise way to define the shape of an object.

interface User {
    name: string;
    age: number;
}

function printUser(user: User) {
    console.log(`Name: ${user.name}, Age: ${user.age}`);
}

const user: User = { name: "Alice", age: 30 };
printUser(user);
Enter fullscreen mode Exit fullscreen mode

TypeScript interfaces ensure objects adhere to the specified structure, improving code reliability and readability.

Classes and Inheritance

JavaScript

JavaScript classes were introduced in ES6, providing a syntactical sugar over the existing prototype-based inheritance.

class Person {
    constructor(name) {
        this.name = name;
    }

    greet() {
        console.log(`Hello, my name is ${this.name}`);
    }
}

class Employee extends Person {
    constructor(name, jobTitle) {
        super(name);
        this.jobTitle = jobTitle;
    }

    greet() {
        console.log(`Hello, my name is ${this.name} and I am a ${this.jobTitle}`);
    }
}

const alice = new Employee("Alice", "Developer");
alice.greet(); // Hello, my name is Alice and I am a Developer
Enter fullscreen mode Exit fullscreen mode

TypeScript

TypeScript builds on JavaScript classes by adding type annotations, visibility modifiers, and better type safety.

class Person {
    protected name: string;

    constructor(name: string) {
        this.name = name;
    }

    greet() {
        console.log(`Hello, my name is ${this.name}`);
    }
}

class Employee extends Person {
    private jobTitle: string;

    constructor(name: string, jobTitle: string) {
        super(name);
        this.jobTitle = jobTitle;
    }

    greet() {
        console.log(`Hello, my name is ${this.name} and I am a ${this.jobTitle}`);
    }
}

const alice = new Employee("Alice", "Developer");
alice.greet(); // Hello, my name is Alice and I am a Developer
Enter fullscreen mode Exit fullscreen mode

TypeScript classes enhance JavaScript classes by providing features like private and protected members, ensuring better encapsulation and code safety.

Generics

JavaScript

JavaScript doesn't have built-in support for generics. Developers often use flexible types and runtime checks to handle generic-like behavior.

function identity(arg) {
    return arg;
}

console.log(identity(42)); // 42
console.log(identity("Hello")); // Hello
Enter fullscreen mode Exit fullscreen mode

TypeScript

TypeScript supports generics, enabling developers to create reusable components with flexible types while maintaining type safety.

function identity<T>(arg: T): T {
    return arg;
}

console.log(identity<number>(42)); // 42
console.log(identity<string>("Hello")); // Hello
Enter fullscreen mode Exit fullscreen mode

Generics in TypeScript allow for creating functions and classes that can operate on various types while still providing compile-time type checks.

Error Handling

JavaScript

JavaScript uses try, catch, and finally for error handling, allowing developers to catch and handle runtime errors.

try {
    throw new Error("Something went wrong!");
} catch (error) {
    console.error(error.message);
} finally {
    console.log("Cleanup code here");
}
Enter fullscreen mode Exit fullscreen mode

TypeScript

TypeScript uses the same error handling mechanisms as JavaScript but benefits from type annotations to improve error detection during development.

try {
    throw new Error("Something went wrong!");
} catch (error) {
    if (error instanceof Error) {
        console.error(error.message);
    }
} finally {
    console.log("Cleanup code here");
}
Enter fullscreen mode Exit fullscreen mode

TypeScript's type system can catch errors related to incorrect handling of exceptions, providing additional safety and reliability.

Conclusion

JavaScript and TypeScript are both powerful tools for web development. JavaScript offers flexibility and ease of use, while TypeScript builds on this foundation by adding static types, interfaces, generics, and other features that improve code quality and developer experience. By understanding the differences and leveraging the strengths of both languages, developers can write more robust, maintainable, and scalable applications. Whether you stick with JavaScript or adopt TypeScript, mastering these concepts will enhance your coding skills and project success.

Top comments (1)