TypeScript Comprehensive Guide

To read this blog you must understand JavaScript first. If you are beginner and yet don’t know JavaScript you can explore other JavaScript blogs on our website. TypeScript is a superset of JavaScript, which means it includes all the features of JavaScript and extends it with static typing. Static typing allows you to define the types of variables, function parameters, and return values explicitly, which helps catch errors early in the development process and enhances code quality. Let’s compare some examples in TypeScript with their equivalent JavaScript code to illustrate the differences.

Setting up typescript development environment

Before getting started with TypeScript make sure you have development environment set up for it. First install Node and Vs Code Editor. Once installed follow along with me. Run the following code to create a starter project My-typescript-project

mkdir My-typescript-project
cd My-typescript-project
npm init // keep entering until finishes

Now open the folder My-typescript-project inside vs code editor. To work with TypeScript, you typically use a build step to compile TypeScript files ( .ts) into JavaScript files (.js). Then, you can run the compiled JavaScript code.

Install TypeScript Globally:

First, make sure you have TypeScript installed in your project. You can install it using npm or yarn, then you can check for typescript version.

npm install -g typescript
tsc --version 

Create a build command to compile Typescript files:

Add a build command to your start script that compiles TypeScript files into JavaScript using the tsc (TypeScript compiler) command. In the root of your project, create a tsconfig.json file, which is a configuration file for TypeScript. This file tells TypeScript how to compile your code and includes settings like output directory, target version, and more.

Basic tsconfig.json settings:

{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "outDir": "dist",
    "rootDir": "src",
    "strict": true
  },
  "include": ["src"]
}

The "outDir": "dist" setting tells TypeScript to place the compiled JavaScript files in the dist directory. The "rootDir": "src" setting indicates that TypeScript should look for .ts files inside the src directory. You can also use tsc --init to create tsconfig.json file.

cd My-typescript-project
tsc --init 

This creates the tsconfig.json file inside My-typescript-project with the required settings, you can manually create the file tsconfig.json and add the basic tsconfig settings provided by me.

Update the start script:

In your package.json, update the start script to include the build step before running the compiled JavaScript files:

{
  "scripts": {
    "start": "tsc && node dist/index.js"
  }
}

If you have single entry point file index.ts inside src then by above setting you can use regular npm start to compile all the files inside src folder to js and then run theindex.js file. In this example, the start script first runs the tsc command to compile TypeScript files. Then, it uses the node command to run the compiled JavaScript file located in the dist directory (in this case,index.js). With these changes, running npm start will build your TypeScript code and then execute the compiled JavaScript code. Make sure your entry point file (e.g., index.ts) is in the src directory as specified in the tsconfig.json.

Keep in mind that the exact configuration and settings may vary depending on your project’s structure and requirements, but these steps provide a basic setup for running TypeScript code in a Node.js environment.

Using ts-node for development environment:

install ts-node as development dependency locally for your project.

cd My-typescript-project
npm install -D ts-node 

this will install ts-node as development dependency.

ts-node is a TypeScript execution environment and REPL for Node.js, which allows you to directly run TypeScript files ( *.ts) without explicitly compiling them to JavaScript. It’s particularly useful for development because it eliminates the need to manually compile your TypeScript code before running it.

If you choose to use ts-node, you don’t have to worry about the build step and can directly run your TypeScript code like this:

ts-node src/index.ts

In this case, you won’t need a separate tsc compilation step in your package.json start script. However, it’s worth noting that while ts-node is convenient during development, it’s generally not recommended for production environments. For production, it’s better to pre-compile your TypeScript code to JavaScript using the TypeScript compiler (tsc) and then run the compiled JavaScript code using node. During development, ts-node can be helpful for a faster development workflow, but for production deployments, it’s better to compile your TypeScript code using tsc and then run the generated JavaScript files using node. This will ensure better performance and compatibility with standard production environments.

Different types in typescript

Now that you have successfully set up the development environment for typescript project , let’s understand different types in typescript. I will give codes in JavaScript and corresponding code in TypeScript.

Basic Variable Declaration:

In JavaScript

let age = 25;
age = 'thirty'; // No error, JavaScript is dynamically typed

In TypeScript

let age: number = 25;
age = 'thirty'; // Error: Type '"thirty"' is not assignable to type 'number'

In TypeScript, we specify that the age variable should have a type of number, so assigning a string to it will result in a compilation error.

Function parameter and return type:

In JavaScript

function add(a, b) {
  return a + b;
}
const result = add(5, '10'); // No error, JavaScript is dynamically typed

In TypeScript

function add(a: number, b: number): number {
  return a + b;
}
const result: number = add(5, '10'); // Error: Argument of type 'string' is not assignable to parameter of type 'number'

In TypeScript, we explicitly specify that the add function expects two parameters of type number and will return a value of type number. Attempting to pass a string as one of the arguments will trigger a compilation error.

Object with Specific Properties:

In JavaScript

const person = {
  name: 'Sam',
  age: 30,
};
person.address = '123 XYZ St'; // No error, JavaScript allows adding properties dynamically

In TypeScript

interface Person {
  name: string;
  age: number;
}
const person: Person = {
  name: 'Sam',
  age: 30,
};
person.address = '123 XYZ St'; // Error: Property 'address' does not exist on type 'Person'

In TypeScript, we can use interfaces to define the shape of an object. The Person interface specifies that an object should have name and age properties. Trying to add the address property to the person object will cause a compilation error.

Array with Specific Element Type:

In JavaScript

const numbers = [1, 2, 3, 'four'];

In TypeScript

const numbers: number[] = [1, 2, 3, 'four']; // Error: Type 'string' is not assignable to type 'number'

In TypeScript, we can specify that the numbers array should only contain elements of type number. Mixing a string and a number in the array will result in a compilation error.

Type Inference:

let name = 'Alice';
name = 25; // No error, JavaScript is dynamically typed
let name = 'Alice';
name = 25; // Error: Type 'number' is not assignable to type 'string'

TypeScript employs type inference to deduce the type of a variable based on its initial value. In this case, TypeScript infers that the name variable should have a type of string because it is initialized with a string value. Therefore, assigning a number to it later will result in a compilation error.

Optional Properties in Objects:

const person = {
  name: 'Bob',
  age: 30,
};
interface Person {
  name: string;
  age?: number; // The '?' indicates the property is optional
}
const person: Person = {
  name: 'Bob',
  // age: 30, // age is optional, so it's okay to omit it
};

In TypeScript, you can define optional properties in interfaces by adding a ? after the property name. This allows you to create objects that may or may not include the optional properties.

In TypeScript, you can define arrays and objects using explicit type annotations or inferred types.

Arrays Using Explicit Type Annotation:

   // Array of numbers
   let numbers: number[] = [1, 2, 3, 4, 5];

   // Array of strings
   let names: string[] = ['Alice', 'Bob', 'Charlie'];

   // Array of mixed types
   let mixedArray: (number | string)[] = [1, 'two', 3, 'four'];

Arrays Using Generic Array Type (Array<T>):

   // Array of numbers
   let numbers: Array<number> = [1, 2, 3, 4, 5];

   // Array of strings
   let names: Array<string> = ['Alice', 'Bob', 'Charlie'];

   // Array of mixed types
   let mixedArray: Array<number | string> = [1, 'two', 3, 'four'];

Objects Using Explicit Type Annotation:

   // Object with specified types for properties
   let person: { name: string; age: number } = {
     name: 'Alice',
     age: 30,
   };

   // Object with optional properties
   let car: { brand: string; model?: string } = {
     brand: 'Toyota',
   };

Objects Using Interfaces:

   // Define interfaces for objects
   interface Person {
     name: string;
     age: number;
   }

   interface Car {
     brand: string;
     model?: string;
   }

   // Use interfaces to define object types
   let person: Person = {
     name: 'Alice',
     age: 30,
   };

   let car: Car = {
     brand: 'Toyota',
   };

Using interfaces makes your code more maintainable, especially when dealing with complex object structures.

In TypeScript, you can also leverage type inference, allowing the compiler to automatically infer types based on the values assigned. For example:

let numbers = [1, 2, 3, 4, 5]; // TypeScript infers number[] type
let person = { name: 'Alice', age: 30 }; // TypeScript infers { name: string, age: number } type

In many cases, TypeScript can infer types without the need for explicit annotations. However, explicit annotations and interfaces can be helpful for providing clarity and documentation in your code.

Union Types:

function printId(id) {
  console.log(`ID: ${id}`);
}
printId(123);
printId('ABC');
function printId(id: number | string) {
  console.log(`ID: ${id}`);
}
printId(123);
printId('ABC');

In TypeScript, you can use union types to specify that a variable or parameter can have multiple types. In this example, the id parameter can be either a number or a string, allowing the function to accept both numeric and string IDs.

Type Aliases:

type Point = { x: number; y: number };
function printPoint(point: Point) {
  console.log(`x: ${point.x}, y: ${point.y}`);
}
const myPoint: Point = { x: 10, y: 20 };
printPoint(myPoint);

Type aliases allow you to create custom names for types, making complex type definitions more readable and reusable. In this example, we create a type alias Point representing an object with x and y properties. The function printPoint takes a parameter of type Point, and myPoint is an example of such an object.

Function Signatures:

type MathOperation = (a: number, b: number) => number;
const add: MathOperation = (a, b) => a + b;
const subtract: MathOperation = (a, b) => a - b;
console.log(add(5, 3)); // Output: 8
console.log(subtract(10, 5)); // Output: 5

In TypeScript, you can define function signatures using type aliases. Here, we define a type alias MathOperation for a function that takes two parameters of type number and returns a number. The add and subtract functions match this signature, allowing us to assign them to variables of type MathOperation.

Enumerations (Enums):

enum Color {
  Red,
  Green,
  Blue,
}
const selectedColor: Color = Color.Green;
console.log(selectedColor); // Output: 1

Enums allow you to define a set of named constants representing numeric values. By default, each constant is assigned an incrementing numeric value, starting from 0. In this example, Color is an enum with three constants, and selectedColor is assigned the value 1 (which corresponds to Color.Green).

Type Assertion (Type Cast):

let inputValue: any = 50;
let numericValue: number = (inputValue as number) + 10;
console.log(numericValue); // Output: 60 

Type of inputValue is declared as any but we know it’s number so we can assert the type of inputValue as number using the keyword “as” .Type assertions (also known as type casts) allow you to explicitly tell the TypeScript compiler about the type of a value. In this example, we assert that inputValue is of type number before adding 10 to it. The expression inputValue as number is a type assertion (also known as type casting) in TypeScript,

let inputValue: any = '50';
let numericValue: number = (inputValue as number) + 10;
console.log(numericValue); // Output: "5010" 
// Why not it's 60 ? Keep reading further to know.

Type assertions allow you to tell the TypeScript compiler explicitly the type of a value, overriding its inferred type or the declared type. However, type assertions don’t perform any actual runtime type conversion or validation. They are purely a way to inform the TypeScript compiler about your intended type for a value.

The inputValue variable is declared as any, which means it can hold any value without TypeScript enforcing type checks. When you write (inputValue as number), you are telling TypeScript that inputValue should be treated as if it were of type number, even though its actual type is any.

Now coming to the answer of question why output is not 60. The type assertion does not change the underlying type or value of inputValue. It’s still a string containing '50'. As a result, when you perform the addition with 10, it results in string concatenation instead of numerical addition, resulting in '5010'.

To correctly add 10 to 50, you need to convert inputValue to a number explicitly before performing the addition.

let inputValue: any = '50';
let numericValue: number = Number(inputValue) + 10;
//can also use parseInt(inputValue) in place of Number(inputValue)
console.log(numericValue); // Output: 60
console.log(typeof numericValue); // output: number

By using Number(inputValue), you convert the string '50' to the number 50, allowing the addition to behave as expected.

Now lets take an example

let inputValue: string = '50';
let numericValue: number = (inputValue as number) + 10;
console.log(numericValue);
//This will give error in console. As we are asserting string type as number.

Unknown Type:

In TypeScript, the unknown type is a type that represents values that are not yet known during runtime. It is a safer counterpart to the any type, which allows values of any type and essentially disables type checking for the values it is applied to. In contrast, unknown encourages more rigorous type checking while still allowing flexibility when dealing with unknown data.

The unknown type was introduced in TypeScript 3.0 to address some of the issues with using any. When a value is of type unknown, TypeScript enforces stricter rules before allowing operations on that value. You need to narrow down or assert the type before performing most operations on it.

function processValue(value: unknown): string {
  if (typeof value === 'string') {
    return value.toUpperCase();
  } else if (typeof value === 'number') {
    return value.toFixed(2);
  } else {
    return 'Unknown value';
  }
}
let unknownValue: unknown;
unknownValue = 'hello';
console.log(processValue(unknownValue)); // Output: "HELLO"
unknownValue = 42;
console.log(processValue(unknownValue)); // Output: "42.00"
unknownValue = true;
console.log(processValue(unknownValue)); // Output: "Unknown value"

In the example above, the function processValue takes an argument of type unknown and performs type checks using typeof before performing any operations. The type of the variable unknownValue can change, and TypeScript forces you to handle each case explicitly.

Using unknown instead of any can help prevent runtime errors and improve the maintainability and reliability of your code. It promotes better type safety because you are required to handle type assertions or narrowing before performing operations that depend on the specific type of the value.

Intersection Types:

interface Person {
  name: string;
}
interface Employee {
  employeeId: number;
}
type EmployeeInfo = Person & Employee;
const employee: EmployeeInfo = {
  name: 'Alice',
  employeeId: 12345,
};

Intersection types combine multiple types into a single type. In this example, EmployeeInfo is an intersection of Person and Employee interfaces, meaning an object of this type must have properties from both interfaces.

Type Guards:

function displayLength(value: string | number) {
  if (typeof value === 'string') {
    console.log(`Length of the string: ${value.length}`);
  } else {
    console.log('Input is not a string.');
  }
}
displayLength('hello'); // Output: Length of the string: 5
displayLength(123); // Output: Input is not a string.

Type guards allow you to filter the type of a variable inside the conditional block. In this example, we use the typeof operator to check whether the value is a string or not and perform specific actions accordingly.

Generic Types:

function identity<T>(arg: T): T {
  return arg;
}
// identity is generic type as it can accept any type T of argument. 

In TypeScript, the notation <T> or <> represents type parameters for generic types and functions. By using the angle bracket syntax (<T>), you introduce a type parameter T. This makes the function generic, and T becomes a placeholder for a specific type that will be provided when the function is called. The type parameter T can be inferred from the argument passed to the function, or you can explicitly specify it when calling the function. This version allows you to work with different types while preserving type safety.

function identity(arg: string): string {
  return arg;
}
let value: string = identity('hello'); // 'value' will be of type 'string'.
// this identity function is not generic as it accepts only string type argument and returns string type.

Using the generic version (function identity<T>(arg: T): T { ... }) allows to create more flexible and reusable functions that can work with different types, providing better type safety. On the other hand, the non-generic version (function identity(arg: string): string { ... }) restricts the function to operate with a single specific type, which can be useful in certain scenarios when you need a specialized implementation.

function identity<T>(arg: T): string {
  return arg;
} 
// here identity function is not generic

Here the function identity<T>(arg: T): string is not a generic function as the type parameter T is not used in the return type (string), making it unrelated to the argument type. To be considered a generic function, the return type should be dependent on the type parameter T.

Generic Function:

function identity<T>(arg: T): T {
  return arg;
}
// Usage with explicit type argument
let result1: string = identity<string>("Hello");
console.log(result1); // Output: "Hello"
// TypeScript can infer the type argument from the provided value
let result2 = identity(42); // result2 will be inferred as number
console.log(result2); // Output: 42

In this example, we have a generic function identity that takes a type parameter T. This function can work with any type, and it returns the same type that it receives as an argument. The type argument T is enclosed in angle brackets <T>.

Generic Array:

function reverseArray<T>(array: T[]): T[] {
  return array.reverse();
}
let numbers: number[] = [1, 2, 3, 4, 5];
let reversedNumbers: number[] = reverseArray(numbers);
console.log(reversedNumbers); // Output: [5, 4, 3, 2, 1]

In this example, we have a generic function reverseArray that takes an array of type T and returns an array of the same type T. The type parameter T is used within the angle brackets to indicate that the function can work with any type.

Generic Interface:

interface KeyValuePair<K, V> {
  key: K;
  value: V;
}
let entry: KeyValuePair<string, number> = {
  key: "age",
  value: 30,
};
console.log(entry); // Output: { key: "age", value: 30 }

In this example, we define a generic interface KeyValuePair that has two type parameters K and V. It represents a simple key-value pair with different types for the key and the value. When we create a variable entry of type KeyValuePair<string, number>, we specify the types for the key and value using the angle brackets <string, number>.

Using generic types and functions allows you to write more flexible and reusable code that can work with different types, providing better type safety and avoiding code duplication.

Conditional Types:

Conditional types are a powerful feature in TypeScript that allows you to create types that depend on a condition. Conditional types use the extends keyword and the ? operator to create type branches based on the evaluation of a type. They are primarily used with generic types to define more flexible and reusable type definitions. The basic syntax for conditional types is as follows:

T extends U ? X : Y

In TypeScript conditional types, T extends U ? X : Y does not necessarily mean that T and U are of the same type. Instead, it checks whether T is assignable to U. In other words, it checks if T is a subtype of U or if T can be assigned to a variable of type U.

  • T: The type parameter being tested.
  • U: The type to which T is being compared.
  • T extends U: This is a type predicate that checks if T is assignable to U. If T is a subtype of U, or if T can be assigned to a variable of type U, then the condition is true. Otherwise, it is false.
  • X: The type that the conditional type evaluates to when the condition T extends U is true.
  • Y: The type that the conditional type evaluates to when the condition T extends U is false.

Let’s look at an example to better understand this:

type IsSubtype<T, U> = T extends U ? true : false;
type Result1 = IsSubtype<number, number>; // Result1 is true, as number extends number (number is assignable to number).
type Result2 = IsSubtype<number, string>; // Result2 is false, as number does not extend string (number is not assignable to string).
type Result3 = IsSubtype<string, string | number>; // Result3 is true, as string extends (is assignable to) string | number.
// Another example with function types
type MyFunction = (x: number) => string;
type AnotherFunction = (y: string) => void;
type IsAssignable = IsSubtype<MyFunction, AnotherFunction>; // IsAssignable is false, MyFunction is not assignable to AnotherFunction.

So, in summary, T extends U in TypeScript conditional types means that T is assignable to U, which does not necessarily imply that T and U are of the same type. It checks for subtyping relationships between types and allows you to perform different type mappings based on this relationship using the conditional type syntax T extends U ? X : Y.

Extract a Property:

type ExtractPropertyName<T> = T extends { name: infer Name } ? Name : never;
interface Person {
  name: string;
  age: number;
}
type PersonName = ExtractPropertyName<Person>; // PersonName is "string"

In this example, ExtractPropertyName<T> is a conditional type that checks whether T is an object with a name property. If it is, the resulting type is the type of the name property; otherwise, it’s the never type. When used with the Person interface, PersonName will be inferred as the type "string" since Person has a name property.

Array or Not:

type IsArray<T> = T extends any[] ? true : false;
const isArray: IsArray<number[]> = true; // isArray is of type "true"
const isNotArray: IsArray<number> = false; // isNotArray is of type "false"

Here, the IsArray<T> conditional type checks if T is an array (any[]). If it is, the resulting type is true; otherwise, it’s false. When used with number[], the variable isArray is of type true, and when used with number, isNotArray is of type false.

Map a Union Type:

type MapToOptional<T> = {
  [K in keyof T]?: T[K];
};
interface Book {
  title: string;
  author: string;
}
type OptionalBook = MapToOptional<Book>;
/*
OptionalBook is equivalent to:
{
  title?: string;
  author?: string;
}
*/

The MapToOptional<T> conditional type maps each property in T to an optional version by adding ? to each property. When applied to the Book interface, it results in a type where each property is optional.

Let’s break down the code step-by-step and understand what’s happening:

type MapToOptional<T> = {
  [K in keyof T]?: T[K];
};

Here, MapToOptional<T> is a generic type that takes a type T as input and produces a new type. The purpose of this type is to transform the properties of T into optional properties, preserving their original types.

Now, let’s explain the concept with an example:

interface Book {
  title: string;
  author: string;
}
type OptionalBook = MapToOptional<Book>;

In this example, we have the Book interface, which represents an object with two properties: title and author, both of type string.

When we apply MapToOptional<Book>, the type transformation takes place as follows:

  1. keyof T: The keyof operator is used to obtain the union of all the keys of the type T. For the Book interface, keyof Book is equal to 'title' | 'author'.
  2. [K in keyof T]?: T[K]: This is a mapped type. It iterates over each key K in the union 'title' | 'author', and for each key, it creates a new property with the same key K in the resulting type, but with an optional modifier (?). Additionally, the type of the property is T[K], which retrieves the type of the original property in the Book interface.

So, after applying the MapToOptional<Book> type transformation, the resulting type is:

type OptionalBook = {
  title?: string;
  author?: string;
};

As you can see, the OptionalBook type is an object with properties title and author, both made optional by adding the ? modifier. The types of these properties (string) are preserved from the original Book interface.

This technique is quite useful when you want to create a variation of an existing type with some or all properties made optional. It’s especially handy when dealing with form data, API responses, or any scenario where certain properties are optional. The MapToOptional type is just one example of the power of mapped types in TypeScript, enabling you to perform various type transformations and operations on object types.

Infer and never keywords:

In TypeScript, “infer” and “never” are two special keywords used in conditional types to perform type inference and represent a type that will never occur, respectively.

infer:

The infer keyword is used in conditional types to allow TypeScript to infer a type based on another type. It is often used in conjunction with the extends keyword to extract a type parameter from a generic type and assign it to a new type variable.

Here’s an example of how infer can be used:

type ExtractReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// Example usage
function add(a: number, b: number): number {
  return a + b;
}
type AddReturnType = ExtractReturnType<typeof add>; // The type of AddReturnType will be inferred as "number"

In this example, ExtractReturnType is a conditional type that checks if T is a function type ((...args: any[]) => infer R). If it is, it assigns the return type of the function to the type variable R, which is then returned as the result of the conditional type. Otherwise, it returns never.

never:

The never type represents a type that will never occur. It is typically used to indicate that a function will never return or to handle exhaustiveness checks in switch statements.

function throwError(message: string): never {
  throw new Error(message);
}
function doSomething(value: string | number): void {
  if (typeof value === 'string') {
    // Handle the string case
  } else if (typeof value === 'number') {
    // Handle the number case
  } else {
    // This block will be reached if the value is neither string nor number
    const exhaustiveCheck: never = value;
    throw new Error(`Unexpected value: ${exhaustiveCheck}`);
  }
}

In the doSomething function, TypeScript uses the never type to handle the case when the value is neither a string nor a number. Since this case is not supposed to happen, TypeScript ensures that all other possibilities are covered in the function, preventing accidental bugs in the code. The variable exhaustiveCheck is of type never, and it’s initialized with the value, which is guaranteed to never happen. The function will throw an error in this case.

Conditional types allow for powerful type transformations, and they are heavily used in TypeScript’s standard library and various advanced type definitions. They provide a way to create generic types that can adapt to different scenarios and enhance type safety and code reusability.

These are just a few examples to showcase how TypeScript adds static typing and additional features to JavaScript, helping developers catch errors and improve code quality during development. It also enhances code readability and maintainability, making large-scale projects more manageable.

Overall, TypeScript helps you catch many potential errors at compile-time, providing better code integrity and maintainability. Additionally, it improves code documentation, as types make the code more self-explanatory and can be used as a form of documentation for developers working on the project.

Set in typescript:

In TypeScript, you can declare and use a Set just like in regular JavaScript. TypeScript provides type annotations, allowing you to specify the type of elements the Set will contain. Here’s how you can write and use a Set in TypeScript:

// Create an empty Set of numbers
const mySet: Set<number> = new Set();
// Add elements to the Set
mySet.add(42);
mySet.add(12);
mySet.add(99);
// Adding a duplicate value, but it will be ignored
mySet.add(42);
// Check if an element exists in the Set
const hasValue: boolean = mySet.has(99); // true
// Remove an element from the Set
mySet.delete(12);
// Get the number of elements in the Set
const size: number = mySet.size; // 2
// Iterate over the Set using forEach or for...of loop
mySet.forEach((value: number) => {
  console.log(value);
});
for (const value of mySet) {
  console.log(value);
}

In the example above, we declared a Set named mySet that will contain elements of type number. We can then add elements to the Set using the add method, check if an element exists using the has method, delete elements using the delete method, and get the number of elements using the size property. The forEach method and for...of loop are used to iterate over the elements of the Set.

TypeScript will provide type checking for the elements in the Set, ensuring that you only add elements of the specified type, and it will provide autocompletion and type information when working with the Set in your code.

How to get started with typescript in react project

To get started with TypeScript in a React project, you can follow these steps:

Step 1: Create a new React project
You can create a new React project using Create React App (CRA) with TypeScript template. Open your terminal and run the following command:

npx create-react-app my-app --template typescript

This command will create a new React project called “my-app” with TypeScript configuration.

Step 2: Set up TypeScript configuration (optional)
If you need to customize TypeScript configuration, you can create a tsconfig.json file in the root of your project. CRA already generates a basic tsconfig.json for you, but you can modify it to suit your project’s needs. For example you can modify target to es6. Hot reloading is already enabled so you don’t have to make any changes for that.

Step 3: Start coding with TypeScript in React
Now you’re ready to start coding your React components with TypeScript. Rename your .js files to .tsx (for React components with JSX) or .ts (for TypeScript-only files). For example, App.js can be renamed to App.tsx or App.ts.

Here’s an example of a basic React component written in TypeScript:

import React from 'react';
interface Props {
  websiteName: string;
}
const Welcome: React.FC<Props> = ({ websiteName }) => {
  return <h1>Welcome! to my, {websiteName}!</h1>;
};
export default Welcome;

In this example, we define a functional component called Welcome that accepts a prop websiteName of type string. The component returns an h1 element displaying a welcome message with the provided website name.

Step 4: Run the React application
To run your React application, go to the project directory in your terminal and run:

npm start

This will start the development server, and you can view your React app in the browser.

You’ve now set up a React project with TypeScript. You can continue building your React components and enjoy the benefits of static typing and TypeScript’s features in your development process.

Don't Miss Out! Subscribe to Read Our Latest Blogs.

If you found this blog helpful, share it on social media.

Subscription form (#5)

Pin It on Pinterest

Scroll to Top