Understanding TypeScript Types: Primitives, Objects, and Type Manipulations
Tags: beginners, programming, typescript, javascript
Published: 2023-07-19 05:24:04

## TypeScript Types

TypeScript is a strongly typed programming language that builds on JavaScript, giving you better tooling at any scale. TypeScript types are categorized into primitive types and object types. The basic types in TypeScript include string, boolean, number, array, tuple, and enum. TypeScript's type system allows you to build new types out of existing ones using a large variety of operators.

There are several types that you can use to define variables. Here are some commonly used types:

**Primitives Types:**
   - **number**: Represents numeric values like integers, floats, etc.
   - **string**: Represents textual data enclosed in single quotes ('') or double quotes ("").
   - **boolean**: Represents a logical value, either `true` or `false`.
   - **null**: Represents the absence of any object value.
   - **undefined**: Represents an uninitialized variable or an object that lacks a value.
   - **symbol**: Represents unique and immutable values that can be used as identifiers.

**Object Types:**
   - **object**: Represents a non-primitive type, i.e., anything that is not a number, string, boolean, null, or undefined.
   - **array**: Represents an ordered list of values of a specific type. You can define an array using square brackets ([]).
   - **tuple**: Represents an array with a fixed number of elements, where each element can have a different type. Tuples allow you to specify the type of each element in a specific order.
   - **enum**: Represents a set of named constants. Enums allow you to define a collection of related values that can be assigned to a variable.
   - **function**: Represents a function type. You can define the parameter types and return type of a function using function types.

**Other Types:**
   - **any**: Represents a dynamic type that can hold any value. When a variable is of type `any`, TypeScript doesn't perform type checking for that variable.
   - **unknown**: Represents a type-safe counterpart of `any`. Variables of type `unknown` can hold any value, but TypeScript enforces type checking before performing any operations on them.
   - **void**: Represents the absence of any type. Typically used as the return type of functions that don't return a value.
   - **never**: Represents a type for values that never occur. It is used for functions that don't have a reachable endpoint or for variables that cannot have a value.
   - **literal types**: Represents a specific value that a variable can hold. For example, you can define a variable of type `'success'`, which can only have the value `'success'`.

These are some of the commonly used types in TypeScript. Understanding and utilizing these types can help you write more robust and type-safe code.

## Type Assertion

Type Assertion is used in TypeScript to override the type inferred by the compiler. It is a mechanism that tells the compiler about the type of a variable. In TypeScript, type assertions can be performed using the "angle bracket" syntax or the "as" keyword.

Here's an example of Type Assertion in TypeScript:

```typescript
let code: any = 123;
let employeeCode = <number> code;
console.log(typeof(employeeCode)); // Output: number
```

In this example, we have a variable `code` of type `any`. We assign the value of this variable to another variable called `employeeCode` using type assertion. The compiler is informed that `code` is of type `number`. The `typeof` operator confirms that `employeeCode` is indeed of type `number`.

## Non-null Assertion

The non-null assertion operator (!) tells the TypeScript compiler that a value typed as optional cannot be null or undefined. It is placed after the variable name and is used to assert that the variable is not null or undefined.

Here's an example of how to use the non-null assertion operator in TypeScript:

```typescript
let word: string | null = null;
const num = 1;
if (num) {
  word = "Hello World!";
}
console.log(word!.toLowerCase());
```

In this example, the variable `word` is of type `string` or `null`. The non-null assertion operator (`!`) is used to assert that `word` is not null. Therefore, we can safely call the `toLowerCase` method on `word`.

## Type Inference

Type inference is used in TypeScript to provide type information when there is no explicit type annotation. It occurs when initializing variables, setting parameter default values, and determining function return types. TypeScript uses the best common type algorithm to select the best candidate types that are compatible with all variables. TypeScript also uses contextual typing to infer types of variables based on their usage in the code.

Here's an example of how type inference works in TypeScript:

```typescript
let x = 3;
```

In this example, the type of the variable `x` is inferred to be `number` because it is initialized with a numeric value.

## Type Compatibility 

TypeScript uses a structural type system, where a type is compatible with another type if it has at least the same members. TypeScript's type compatibility is based on the structure and shape of types rather than their explicit declarations.

Here's an example of type compatibility in TypeScript:

```typescript
interface Pet {
  name: string;
}

let pet: Pet;
let dog = { name: "Lassie", owner: "Rudd Weatherwax" };
pet = dog;
```

In this example, the `Pet` interface defines a property `name`. We declare a variable `pet` of type `Pet` and assign it an object `dog` with a `name` property. TypeScript considers the object `dog` to be compatible with the `Pet` interface because it has at least the same members.

## Combining Types

In TypeScript, combining types refers to the ability to create new types by combining existing types in various ways. These combinations can be done using union types, intersection types, and conditional types. Let's explore each of these combining types in TypeScript:

### Union Types
Union types allow you to combine multiple types using the `|` (pipe) symbol.

 A value of a union type can be of any of the constituent types. For example:

   ```typescript
   type MyType = string | number;
   let x: MyType;
   x = "hello"; // valid
   x = 10; // valid
   x = true; // invalid, as boolean is not in the union
   ```

   Union types are useful when a value can be one of multiple types.

### Intersection Types:
   Intersection types allow you to combine multiple types into a single type that has all the properties and methods of each constituent type. It is denoted by the `&` (ampersand) symbol. For example:

   ```typescript
   type FirstType = { name: string };
   type SecondType = { age: number };
   
   type CombinedType = FirstType & SecondType;
   
   let obj: CombinedType = { name: "John", age: 25 };
   console.log(obj.name); // "John"
   console.log(obj.age); // 25
   ```

   Intersection types are useful when you want to create a type that has the combined features of multiple types.

### Conditional Types:
   Conditional types introduce type transformations based on a condition. They are defined using the `extends` keyword and are denoted by the `?` (question mark) symbol. Conditional types are evaluated dynamically based on the given type parameter. For example:

   ```typescript
   type NonNullable<T> = T extends null | undefined ? never : T;
   
   let value: NonNullable<string | null>;
   value = "hello"; // valid
   value = null; // invalid
   
   let numberValue: NonNullable<number | undefined>;
   numberValue = 10; // valid
   numberValue = undefined; // invalid
   ```

   In this example, the `NonNullable` conditional type transforms the given type parameter by excluding `null` and `undefined` types.

These combining types provide powerful ways to create complex types in TypeScript, allowing you to express a wide range of scenarios and enforce type safety in your code.

## Type Guards and Narrowing in TypeScript

Type guards in TypeScript allow you to narrow down the type of a value within a conditional block based on a specific condition. Type narrowing is the process of reducing the possible types of a value within a certain scope. This helps you write more precise and type-safe code. Here's an example that demonstrates type guards and narrowing:

```typescript
interface Circle {
  kind: "circle";
  radius: number;
}

interface Square {
  kind: "square";
  sideLength: number;
}

type Shape = Circle | Square;

function getArea(shape: Shape): number {
  if (shape.kind === "circle") {
    // Type guard narrows down the type to Circle
    return Math.PI * shape.radius * shape.radius;
  } else if (shape.kind === "square") {
    // Type guard narrows down the type to Square
    return shape.sideLength * shape.sideLength;
  } else {
    // This code is unreachable because Shape only allows Circle or Square
    throw new Error("Invalid shape");
  }
}

const circle: Circle = { kind: "circle", radius: 5 };
console.log(getArea(circle)); // Output: 78.53981633974483

const square: Square = { kind: "square", sideLength: 4 };
console.log(getArea(square)); // Output: 16
```

In this example, we have defined two interfaces, `Circle` and `Square`, both of which have a `kind` property indicating the type of the shape. The `Shape` type is a union of `Circle` and `Square`. The `getArea` function takes a `Shape` parameter and uses type guards to narrow down the type within the conditional blocks.

When we call `getArea` with a `Circle` object, the type guard `shape.kind === "circle"` allows TypeScript to infer that the `shape` parameter is of type `Circle` within that block. Similarly, when `shape.kind === "square"`, TypeScript knows that the `shape` parameter is of type `Square`. This narrowing enables us to access the specific properties of each shape without type errors.

Type guards are a powerful feature in TypeScript, allowing you to write more reliable and type-safe code by narrowing down the possible types of values based on conditions.

---
🌟 **Thank You for Joining the Journey!** 🌟

>I hope you found this blog post informative and engaging. Your support means the world to me, and I'm thrilled to have you as part of my community. To stay updated on my latest content.


📌 **Follow me on Social Media!** 📌


🌐 [Visit my Website](https://raj-rathod.github.io/rajesh-rathore/)
📢 [Connect with me on Twitter](https://twitter.com/Rajesh946055)
📷 [Follow me on Instagram](https://www.instagram.com/raj_rathod1313/?hl=en)
📚 [Connect on LinkedIn](https://www.linkedin.com/in/rajesh-rathore-0501/)
📌 [Check out my GitHub](https://github.com/raj-rathod)

💌 **A Special Message to You!** 💌

To all my dedicated readers and fellow tech enthusiasts, I want to express my gratitude for your continuous support. Your engagement, comments, and feedback mean the world to me. Let's keep learning, growing, and sharing our passion for development!

👥 Let's Stay Connected! 👥
If you enjoy my content and want to stay in the loop with my latest posts, please consider following me on my social media platforms. Your support is invaluable.

Thank you for being a part of this amazing journey! 🚀

---