Comparing JavaScript and TypeScript: Key Differences and Features
Tags: webdev, javascript, typescript, react
Published: 2024-06-23 02:41:58

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.

```javascript
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)
```

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.

```typescript
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
```

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.

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

const timer = new Timer();
```

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.

```javascript
let message = "Hello, world!";
message = 42; // No error, but not ideal
```

### TypeScript

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

```typescript
let message: string = "Hello, world!";
// message = 42; // Error: Type 'number' is not assignable to type 'string'
```

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.

```javascript
/**
 * @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);
```

### TypeScript

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

```typescript
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);
```

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.

```javascript
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
```

### TypeScript

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

```typescript
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
```

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.

```javascript
function identity(arg) {
    return arg;
}

console.log(identity(42)); // 42
console.log(identity("Hello")); // Hello
```

### TypeScript

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

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

console.log(identity<number>(42)); // 42
console.log(identity<string>("Hello")); // Hello
```

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.

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

### TypeScript

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

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

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.