Skip to main content

34 posts tagged with "typescript"

View All Tags

Type of React props.children in TypeScript

· One min read

In TypeScript, the type of props.children depends on the usage.

If props.children is expected to be a single React element, the type can be specified as React.ReactNode.

If props.children is expected to be an array of React elements, the type can be specified as React.ReactNodeArray.

For example, consider the following functional component:

interface Props {
children: React.ReactNode;
}

function MyComponent(props: Props) {
return <div>{props.children}</div>;
}

Here, props.children is expected to be a single React element, and its type is specified as React.ReactNode.

TypeScript Type For useReducer() action

· One min read

The TypeScript type of the action parameter in the useReducer hook depends on the type of the reducer function that you are using.

In general, the action parameter can have any type that is compatible with the action types that the reducer function expects. For example, if your reducer function expects actions of type { type: string, payload: any }, then the action parameter in useReducer should have that same type or a subtype of it.

Here's an example of how you could define a Reducer type that takes a specific action type:

type MyActionType = { type: string; payload: any };

type MyStateType = {
/* ... */
};

type MyReducerType = (state: MyStateType, action: MyActionType) => MyStateType;

In this example, MyActionType represents the shape of the actions that your reducer expects, MyStateType represents the shape of your state, and MyReducerType is the type of the reducer function that you will pass to useReducer.

Mark Object Property As Optional in TypeScript

· One min read

In TypeScript, you can mark an object property as optional by using a question mark ? after the property name. This indicates that the property is not required and can be omitted when creating or using objects that have this property.

Here's an example:

interface Person {
name: string;
age?: number;
}

const person1: Person = {
name: "Alice",
age: 30,
};

const person2: Person = {
name: "Bob",
};

In this example, the Person interface has an optional age property marked with a question mark. The person1 object includes the age property, while the person2 object omits it.

Note that when accessing an optional property, you should check whether it exists first to avoid runtime errors. You can use the in operator or the typeof operator to check for the existence of an optional property. For example:

if ("age" in person1) {
console.log(person1.age);
}

if (typeof person2.age === "number") {
console.log(person2.age);
}

What is React.FC Type?

· One min read

If we are using TypeScript along with React, we might use React.FC type. FC stands for function component.

If we write a function that returns a React component, we can use this type.

const App: React.FC = () => {
return <div>Hello</div>;
};

If the function does not return a React component, TypeScript will throw an error. Here is a function that will throw an error:

const App: React.FC = () => {
return "hello";
};

The error message will be:

Type 'string' is not assignable to type 'ReactElement<any, any>'

React.FC is the short version of React.FunctionComponent. We can use any type according to our convienience.

How To Enable Decorators in TypeScript

· One min read

In order to use or try Decorators in TypeScript, we need to enable it. Enabling Decorators can be done from tsconfig.json file.

If the tsconfig.json file has been created using tsc init command, the file would have a default configuration. The file also has many configurations that are commented.

Interface in TypeScript

· One min read

An interface describes the structure of an object. We create an interface using interface keyword. interface keyword exists only in TypeScript, not in vanilla JavaScript.

Let us define the structure of a car object using interface.

noImplicitAny Option in TypeScript

· One min read

noImplicitAny is an option under compilerOptions in tsconfig.json.

In order to understand the meaning of noImplicitAny option, consider the following TypeScript code. I have taken screenshot from VS Code to show the highlighted red error.

Compile TypeScript to ES6 Code

· One min read

When we do tsc --init to initialize a TypeScript project, it generates a tsconfig.json file. We can see a JSON object with compilerOptions property. We can generate ES6 output by setting target to ES6 or ES2015.

Watch Mode in TypeScript

· One min read

When working with a single TypeScript file, we repeatedly compile the file using tsc command. We can use watch mode to automatically compile the file on change.

Here is how you can use watch mode:

Object Types in TypeScript

· 3 min read

Objects in JavaScript consist of key value pairs. An object consist of different properties. Each property can contain a data value of certain type. Setting types of an object properties and recursively applying this process to nested objects defines the type of an object.

Here is a simple JavaScript object.

{
name: "Joby",
age: 35
}

As we can see, name property contains string value and age contains a number. Therefore, we can define the type of above object as:

{
name: string,
age: number
}

We saw the object and associated type. In actual code, this is how we can declare a variable and attach an object type.

let student: {
name: string;
age: number;
};

student = {
name: "Joby",
age: 35,
};

The object value we passed to student matches the defined type. What if we assign a different object to student as shown below?

student = {
name: "Joby",
age: 35,
year: 2022,
};

TypeScript in this case clearly points out the extra year property. See how detail TypeScript is in the error message.

error TS2322: Type '{ name: string; age: number; year: number; }' is not assignable to type '{ name: string; age: number; }'.
Object literal may only specify known properties, and 'year' does not exist in type '{ name: string; age: number; }'.

Object as Function Argument

When a function accepts an object as argument, we can again define the type of object accepted by the function. If there is a function foo() that accepts above student object, this is how the function declaration looks like.

function foo(student: { name: string; age: number }) {
return;
}

We can then call the function foo() by passing an object with only name and age property.

foo({
name: "Joby",
age: 35,
});

Adding an extra parameter to the argument object throws an error. Let us pass an object to foo() that contains 3 properties.

foo({
name: "Joby",
age: 35,
year: 2022,
});

TypeScript clearly points out the error. Let me paste the exact error message below. See for yourself how descriptive it is.

error TS2345: Argument of type '{ name: string; age: number; year: number; }' is not assignable to parameter of type '{ name: string; age: number; }'. Object literal may only specify known properties, and 'year' does not exist in type '{ name: string; age: number; }'.

Optional Properties in Objects

In an object, few properties can be optional. Consider the following object:

{
name: "Joby",
status: "married",
spouse: "Ninu"
}

In the above object, spouse key is not required if status is single. While defining type for such fields, we can mark a property as optional by ? operator. Here is the type definition for our object.

{
name: string
status: string
spouse?: string
}

Define Function Argument and Return Types in TypeScript

· 2 min read

Types of TypeScript variables can be explicitly set using annotations. Here is a TypeScript variable that is of string type.

let name: string;

Using the same annotation syntax, we can define the types of function arguments and return value. Here is a function that adds two numbers.

function add(a, b) {
return a + b;
}

Above function can also be used to concatenate 2 strings. To avoid that, we can specify number type for the function's arguments and return value. This is how we do that in TypeScript.

function add(a: number, b: number): number {
return a + b;
}

Above code says that function add() accepts only 2 numbers as arguments and return a number value. If we do not specify any types, by default the argument and return type will be any.

In the above typed function, we try to invoke the function with invalid arguments, eg: add(3, "a"), TypeScript will throw below error.

error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.

We can catch errors early and at the place of function definition, if we properly set the argument and return types.

Forcing a return value

When we define a return type for a function, TypeScript makes sure that a value of proper type is always returned.

function foo(): number {}

Above function foo() should return a number value. But it is clear that foo() is not returning any value. Therefore, TypeScript will throw below error.

error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.

TypeScript Variables and Values

· 4 min read

TypeScript can define types for variables. It can be through type inference or through explicit type declaration.

Inferred Type

In JavaScript, we can declare a variable using var, let or const. Here is a variable that is declared and initialized with a value 10.

let a = 10;

If above line is present in a TypeScript file, a is inferred as a number type variable by TypeScript. We do not have to explicitly set the type as number. If we try to assign a non-number value to a, TypeScript throws an error.

let a = 10;
a = "hello";

Above 2 lines are perfectly ok in JavaScript. But TypeScript is not happy with it. Here is the error message thrown by TypeScript:

error TS2322: Type 'string' is not assignable to type 'number'.

a = "hello";

Generally, TypeScript defines the type of a variable at the time of declaration itself. So, it is always good to plan the type of a variable in advance and assign it at the time of declaration itself.

Literal Type

Literals in JavaScript stands for specific data values like 6 or "hello". For a variable declared using const keyword, we cannot change the value later.

const a = 10;

As we learned in the previous section, TypeScript can infer the type of variable a. TypeScript is clever. It understands that a is a const variable. Therefore it can be assigned only with value 10. TypeScript defines the type of a as 10. Even though 10 is a value, for TypeScript it is called a literal type.

When a is assigned with a literal type of 10, assigning a different number results in a TypeScript error. For example, consider the following code.

const a = 10;
a = 20;

TypeScript will throw below error in the second line.

error TS2588: Cannot assign to 'a' because it is a constant.

What if we re-assign with value 10 itself? Can we expect TypeScript accepting that?

const a = 10;
a = 10;

Here also, TypeScript throws the same error:

error TS2588: Cannot assign to 'a' because it is a constant.

TypeScript respects the rules of JavaScript first. Then only it will check for its own rules. Re-assigning to a const variable is not allowed in JavaScript itself. So, TypeScript helps us by finding such errors at compile time itself.

Any Type

Sometimes we declare a variable without initialization. There can be different reasons for that. One being passing the value from one scope to another like below.

let apiResponse;

function callAPI() {
apiResponse = "data";
}

function printResponse() {
console.log(apiResponse);
}

In this case, we do not know what data will be set to apiResponse from the API. Therefore, what TypeScript will do is, it sets the type of apiResponse as any. As the name suggests, a variable with type any can accept any data values.

Below TypeScript code will not throw an error.

let a;
a = 10;
a = "hello";

That is because, the first line inferred any type for a. Now a can accept any data values.

Type Annotation

We can explicitly set the type of a variable using type annotations. It is better to use type annotations only if required. If TypeScript can infer the type automatically from the initialized value, we do not have to use type annotations. This keeps the code cleaner.

If we want to restrict a variable only to accept Date objects, we can do below annotation.

let dob: Date;

Now if we try to assign a number to dob, it will throw below error.

error TS2322: Type 'number' is not assignable to type 'Date'.

We now know how TypeScript treats variables and how types of variables are determined.

Happy learning!

Generate TypeScript .d.ts Declaration File

· 2 min read

In TypeScript project, if you find a .d.ts file, it is a TypeScript Declaration file. While compiling a TypeScript file, the compiler can strip all the type information and store it in this declaration file. The generated JavaScript file will not contain any TypeScript specific information.

In order to generate a separate .d.ts declaration file, we need to set declaration property value to be true in tsconfig.json.

{
"compilerOptions": {
"outDir": "dist",
"declaration": true, // highlight-line
"target": "ES6"
},
"include": ["src"]
}

index.ts

My index.ts file contains following code.

function add(a: number, b: number): number {
return a + b;
}

As per the TypeScript config file, the compiled output needs to be stored in /dist folder.

index.d.ts

When we compile the TypeScript code, a TypeScript declaration file with name index.d.ts is also created and stored along with the output index.js file. Here is how the declaration file looks like.

declare function add(a: number, b: number): number;

As we can see, the declaration file contains the function add() with only the type information. Where as the generated output JavaScript file contains pure JavaScript code without any hint of TypeScript.

// index.js
function add(a, b) {
return a + b;
}

Then what is the use of declaration file? For people who just requires JavaScript can take only the JavaScript file and use it. Whereas people who work with typescript can make use of the type declaration file to validate the code during development or while building the project.

TypeScript Setup & Compilation

· 4 min read

The goal of this article is to learn how to setup and compile a TypeScript program. We use tsc compiler command to perform the compilation. We also learn how to set the level of JavaScript that comes out of TypeScript compiler.

Project Setup

Create a folder anywhere to store the project files. I have created a folder named ts-intro. Inside the folder, create 3 files.

  1. package.json
  2. tsconfig.json
  3. src/index.ts

package.json

We need only one dependency for this project. That is typescript. So, here is how our package.json looks like.

{
"name": "ts-intro",
"devDependencies": {
"typescript": "^4.5.5"
},
"scripts": {
"dev": "tsc --watch --preserveWatchOutput"
}
}

The dev command will run the TypeScript compiler(tsc) along with some options. --watch flag watches for any file change and does compilation again if there is any source file change. --preserveWatchOutput is to retain the console outputs even after re-compilation. Now, that is it about package.json file. Let us understand the second file, tsconfig.json.

tsconfig.json

When we run tsc command, if a tsconfig.json file is there, TypeScript will parse that file for options. The settings in the file is applied during TypeScript compilation. Here is our tsconfig.json file.

{
"compilerOptions": {
"outDir": "dist",
"target": "ES3"
},
"include": ["src"]
}

We are passing 2 compiler options. outDir is for output directory. Here, we are telling TypeScript to store the output in dist directory. If we does not specify this option, TypeScript by default puts the output JavaScript file alongside the TypeScript files. Keeping a separate output directory makes development easier.

If we want the output JavaScript to support ES3 browsers(Eg: IE6), then we need to set the value of target to ES3. Note that, more backward compatibility means the generated JavaScript files will contain more code. So, we do not have to set this option to older versions unnecessarily.

By mentioning "src" in include array, we specify that all our TypeScript files will be in src folder. --watch flag will be watching for any file changes under the src folder.

We can pass all compiler options as command line flags. But as the project gets bigger, having a separate config file is easier to manage and version.

src/index.ts

We are going to write our TypeScript code in this index.ts file. First, paste below code to index.ts file.

let a = 10;

What we are going to test is:

  • Will TypeScript converts this ES6 code with let keyword to an ES3 code?
  • Will TypeScript saves the output JavaScript code under /dist folder?

Before starting the execution, we need to install the packages. For that navigate to project folder in terminal and run:

npm install

Above command will install typescript dependency. Once installation is complete, run the dev script using:

npm run dev

I could see /dist folder getting created. Inside the dist folder, an index.js file is present with following code:

var a = 10;

As we can see, TypeScript converted the let keyword to var for ES3 compatibility. What if we update the target value in tsconfig.json to ES6? Then let keyword is kept as such in the output JavaScript file.

TypeScript Code

So far we tried only JavaScript code in index.ts file. Let us try a simple TypeScript code.

function add(a: number, b: number): number {
return a + b;
}

Our aim is to restrict add() function to accept only numbers. After compilation the output JavaScript file contains following code:

function add(a, b) {
return a + b;
}

Wait! Where is the type check? I was expecting TypeScript to add type checking code like:

function add(a, b) {
if (typeof a === "number" && typeof b === "number") return a + b;
else return "error";
}

That will not happen. TypeScript is a static type checker. It does not do any code logic modification. Example, how can TypeScript decide what to do if an error occurs? Does it need to return a string with "error" or throw some exception. All these confusions will occur. So TypeScript will do only build time type check with the annotations given to it.

For example, in the above TypeScript code if we call the function with a string value like below, TypeScript can identify the error at build time itself.

function add(a: number, b: number): number {
return a + b;
}

add("a", "b");

Now TypeScript will throw below error in terminal:

src/index.ts:5:5 - error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.

5 add("a", "b");

Summary

We learned how to setup a simple project that can compile TypeScript files. We learned how to provide compiler options using tsconfig.json file. We understood how Static Type checking works in case of TypeScript. This article can be a starting point for further TypeScript experiments.

Introduction to TypeScript

· 2 min read

TypeScript is a syntactic superset of JavaScript. It means, it starts with basic JavaScript syntax, and then start building more type specific syntax on top of it. TypeScript is an open-source project maintained by Microsoft. The main goal is to add types to JavaScript.

Here is an example TypeScript code to declare a string type variable.

let name: string;

A TypeScript code is compiled by a TypeScript compiler and outputs JavaScript code.

Why Choose TypeScript?

One reason is developers can be bring their intent to the code. For example, below function is to add 2 numbers, NOT to concatenate 2 strings.

function add(a, b) {
return a + b;
}

But how to express our intent? We can do using TypeScript.

function add(a: number, b: number): number {
return a + b;
}

Now if somebody use the function like add("b", 3), TypeScript compiler will throw an error.

Second reason is TypeScript can detect some errors at compile time rather than at run time. Examples are:

  • Errors occuring due to null or undefined values in variables
  • Bad reference variables. You changed the name of a function, but forgot to replace the new name in all occurences.
  • Breakage around internal code contract. ie, earlier an argument was optional, but now it is mandatory.

Third reason is the code development experience with TypeScript. TypeScript gels well with Visual Studio Code. Both are from Microsoft. When working with TypeScript, the intellisense support and documentation support is great.

TypeScript Online Editor

If you want to try TypeScript syntax quickly, TypeScript language provides an online editor at https://www.typescriptlang.org/play. Also https://www.typescriptlang.org/ is the official website of TypeScript.

Typed Future JavaScript

Another question that comes to developers is about future JavaScript. Will JavaScript in near future bring type support? I don't think so.

Assume, JavaScript bring following syntax tomorrow:

string name = "Jack";

It is going to break a lot of JavaScript code floating on web starting from 90s. So it is good and advisable to learn TypeScript.