Managing types in React Typescript, the right way

Managing types in React Typescript

Introduction

As developers, we love clean, maintainable code. When writing React with Typescript we often deal with types spread across multiple files. If you are obsessed with clean code, this leaves much to be desired. Here are a few things you can do to make your React code cleaner and readable.

React types basics

The Basics

Strong basics are important when writing code. Here are some basic habits to keep in mind when writing typescript code.

Naming Conventions

Typescript allows you to disregard conventions and write code your way. Conventions are used because they are helpful, and as the saying goes “When in Rome…”

Here are some of the conventions that should be followed to write clean and readable code:

  • Use PascalCase for type names.
  • Do not use the I prefix for interfaces. (Something that was copied from statically typed languages)
  • Use _ prefix for private properties.
  • Use consistent naming for component props types (For example, type CustomComponentProps)

Components and Types

Following the component-based coding style of React, we separate components into multiple different files. When using typescript, it’s always a good idea to export the Props types along with the component.

https://medium.com/media/a281d8cfa2eb815f19872cd642ffecc1/href

In the tooltip.tsx file, you can see the type TooltipProps being exported along with the component. This is especially useful when your component is taking props from the state of a parent component.

Another useful habit to follow is extending your base HTML component props with custom properties (using React.ComponentPropsWithoutRef ). Especially when you are creating stylized versions of base HTML tags.

Namespaces

As your project size increases, so will the number of types. There is a good chance that there will be name collisions. Namespaces are the solution to this problem. A namespace will not only avoid multiple type declarations but also provide an organisational structure to your projects. Using namespaces effectively can make your codebase clean. Here is an example of namespace Validation.

namespace Validation {
export interface StringValidator {
isAcceptable(s: string): boolean;
}
const lettersRegexp = /^[A-Za-z]+$/;
const numberRegexp = /^[0-9]+$/;
export class LettersOnlyValidator implements StringValidator {
isAcceptable(s: string) {
return lettersRegexp.test(s);
}
}
export class ZipCodeValidator implements StringValidator {
isAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s);
}
}
}

Keyof, Typeof

We are familiar with the typeof operator in JavaScript. The typeof operator in JavaScript helps with getting the type of a variable in a string format. But the typeof operator does not give you the exact type of the object. For example:

type X = {a: number; b: boolean;};
const x: X = {a: 1, b: false};
/* Here the typeof operator will only give the type of x as object instead of the type X */
console.log(typeof x);

Typescript is just a development tool, and it can use the typeof operator to get the exact type definition of a variable. For example:

type X = {a: number; b: boolean;};
const x: X = {a: 1, b: false};
// Here type Y will evaluate to type X = {a: number; b: boolean;}
type Y = typeof x;

The typeof operator helps infer types directly from passed values. Instead of importing every type wherever required, typeof can be used to infer types.

The keyof operator is introduced by typescript. It gives the properties of an object type in form of a union. For example:

type X = {a: number; b: boolean;};
const x: X = {a: 1, b: false};
// Y = 'a' | 'b'
type Y = keyof X;
const y: Y = 'a';
/* Without explicitly specifying y as type Y TS will infer its type as string and will throw an error about indexing type X using string */
console.log(x[y]);

You will often be using typeof and keyof operators in combination, typeof to get the type of an object and keyof to index it.

Project scope types (@types)

@types is a special directory in typescript. The declaration file (*.d.ts files, e.g. index.d.ts) are auto recognised by your project’s tsconfig as the root types files. The types defined in these files can be used as global types in your project.

There will be times when certain type definitions will be required frequently. @types is the answer to this. You can use @types to overwrite or extend your type definitions. Here is an example of @types/index.d.ts:

https://medium.com/media/d5b1f2dabb96f78365b1918a43808052/href

In this example, you can see the type Optional being defined as well as a new namespace Api being declared. The types in React namespace are also being extended to include React.ClassicFunctionalComponent (or its shorthand React.CFC).

Enums are an exception to @types. Types defined in typescript are not part of the JavaScript bundle, but enums are compiled. Defining enums in @types will make them globally accessible in the project scope, but they might throw errors in the build. It is always better to define and export enums separately in a *.ts/tsx file.

Utility Types

Typescript provides several utility types which help you transform your type definitions. Some of my commonly used utility types are:

  • Partial<Type> constructs type with all the properties of the type set to optional.
type X = {a: number;}
// Y = Partial<X> = {a?: number;} or {a: number | undefined;}
type Y = Partial<X>
  • Required<Type> constructs type with all the properties of the type set to required.
type X = {a?: number;}
// Y = Required<X> = {a: number;}
type Y = Required<X>
  • Record<Keys, Type> constructs an object type with property key of type Keys and property value of type Type.
// X = {[key: string]: number;}
type X = Record<string, number>
  • Omit<Type, Keys> constructs a type from type Type after removing key Keys (string literal or union of strings).
type X = {a: number; b: boolean; c: string;}
// Y = {b: boolean; c: string;}
type Y = Omit<X, 'a'>
// Z = {c: string;}
type Z = Omit<X, 'a' | 'b'>

There are many more Utility Types in typescript and these will help you avoid rewriting your type definitions for every individual or derivable type.

You can also write custom utility types, like the Optional type provided in the @types/index.d.ts file. Optional type is used to make only selected properties optional in a type.

Generics

Generics allow the developer to write reusable type-safe code/components. In the world of statically typed languages like C# and Java, generics are commonly used for writing reusable code. Typescript takes many things from these languages, generics included.

Generics take type(s) as input and use them to derive the type of variables or functions. One of the examples is the utility type Partial<T>, which takes type T as input and gives a new type. Generics can take more than one type as input and can also have default types. Here is an example of generics with function.

/* Type is passed to the function either explicitly or implicitly */
function identity<Type>(arg: Type): Type {
return arg;
}
/* Explicitly: Type is assigned type string explicitly and the function takes string argument arg */
identity<string>("Hello");
/* Implicitly: Type implicitly takes type string based on the type of argument arg */
identity("World")

Use generics to make your code reusable without writing multiple type definitions.

Wrapping it up

Wrapping up

Using typescript with react seems like extra work in the beginning. Adding types to react JSX might make your code dirty when added without structure. Following the aforementioned habits, guidelines and conventions, I have been able to keep my code cleaner and easier to manage. Hopefully, you (the reader) were able to pick up a few helpful tips from this as well.

References ✍️

P.S. The information mentioned here is as I understand it and might not be completely accurate. If you want more details, you can check the typescript documentation or the references section.


Managing types in React Typescript, the right way was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.


This content originally appeared on Level Up Coding - Medium and was authored by Ahmed Mawia

Managing types in React Typescript

Introduction

As developers, we love clean, maintainable code. When writing React with Typescript we often deal with types spread across multiple files. If you are obsessed with clean code, this leaves much to be desired. Here are a few things you can do to make your React code cleaner and readable.

React types basics

The Basics

Strong basics are important when writing code. Here are some basic habits to keep in mind when writing typescript code.

Naming Conventions

Typescript allows you to disregard conventions and write code your way. Conventions are used because they are helpful, and as the saying goes “When in Rome…”

Here are some of the conventions that should be followed to write clean and readable code:

  • Use PascalCase for type names.
  • Do not use the I prefix for interfaces. (Something that was copied from statically typed languages)
  • Use _ prefix for private properties.
  • Use consistent naming for component props types (For example, type CustomComponentProps)

Components and Types

Following the component-based coding style of React, we separate components into multiple different files. When using typescript, it’s always a good idea to export the Props types along with the component.

In the tooltip.tsx file, you can see the type TooltipProps being exported along with the component. This is especially useful when your component is taking props from the state of a parent component.

Another useful habit to follow is extending your base HTML component props with custom properties (using React.ComponentPropsWithoutRef ). Especially when you are creating stylized versions of base HTML tags.

Namespaces

As your project size increases, so will the number of types. There is a good chance that there will be name collisions. Namespaces are the solution to this problem. A namespace will not only avoid multiple type declarations but also provide an organisational structure to your projects. Using namespaces effectively can make your codebase clean. Here is an example of namespace Validation.

namespace Validation {
export interface StringValidator {
isAcceptable(s: string): boolean;
}
const lettersRegexp = /^[A-Za-z]+$/;
const numberRegexp = /^[0-9]+$/;
export class LettersOnlyValidator implements StringValidator {
isAcceptable(s: string) {
return lettersRegexp.test(s);
}
}
export class ZipCodeValidator implements StringValidator {
isAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s);
}
}
}

Keyof, Typeof

We are familiar with the typeof operator in JavaScript. The typeof operator in JavaScript helps with getting the type of a variable in a string format. But the typeof operator does not give you the exact type of the object. For example:

type X = {a: number; b: boolean;};
const x: X = {a: 1, b: false};
/* Here the typeof operator will only give the type of x as object instead of the type X */
console.log(typeof x);

Typescript is just a development tool, and it can use the typeof operator to get the exact type definition of a variable. For example:

type X = {a: number; b: boolean;};
const x: X = {a: 1, b: false};
// Here type Y will evaluate to type X = {a: number; b: boolean;}
type Y = typeof x;

The typeof operator helps infer types directly from passed values. Instead of importing every type wherever required, typeof can be used to infer types.

The keyof operator is introduced by typescript. It gives the properties of an object type in form of a union. For example:

type X = {a: number; b: boolean;};
const x: X = {a: 1, b: false};
// Y = 'a' | 'b'
type Y = keyof X;
const y: Y = 'a';
/* Without explicitly specifying y as type Y TS will infer its type as string and will throw an error about indexing type X using string */
console.log(x[y]);

You will often be using typeof and keyof operators in combination, typeof to get the type of an object and keyof to index it.

Project scope types (@types)

@types is a special directory in typescript. The declaration file (*.d.ts files, e.g. index.d.ts) are auto recognised by your project’s tsconfig as the root types files. The types defined in these files can be used as global types in your project.

There will be times when certain type definitions will be required frequently. @types is the answer to this. You can use @types to overwrite or extend your type definitions. Here is an example of @types/index.d.ts:

In this example, you can see the type Optional being defined as well as a new namespace Api being declared. The types in React namespace are also being extended to include React.ClassicFunctionalComponent (or its shorthand React.CFC).

Enums are an exception to @types. Types defined in typescript are not part of the JavaScript bundle, but enums are compiled. Defining enums in @types will make them globally accessible in the project scope, but they might throw errors in the build. It is always better to define and export enums separately in a *.ts/tsx file.

Utility Types

Typescript provides several utility types which help you transform your type definitions. Some of my commonly used utility types are:

  • Partial<Type> constructs type with all the properties of the type set to optional.
type X = {a: number;}
// Y = Partial<X> = {a?: number;} or {a: number | undefined;}
type Y = Partial<X>
  • Required<Type> constructs type with all the properties of the type set to required.
type X = {a?: number;}
// Y = Required<X> = {a: number;}
type Y = Required<X>
  • Record<Keys, Type> constructs an object type with property key of type Keys and property value of type Type.
// X = {[key: string]: number;}
type X = Record<string, number>
  • Omit<Type, Keys> constructs a type from type Type after removing key Keys (string literal or union of strings).
type X = {a: number; b: boolean; c: string;}
// Y = {b: boolean; c: string;}
type Y = Omit<X, 'a'>
// Z = {c: string;}
type Z = Omit<X, 'a' | 'b'>

There are many more Utility Types in typescript and these will help you avoid rewriting your type definitions for every individual or derivable type.

You can also write custom utility types, like the Optional type provided in the @types/index.d.ts file. Optional type is used to make only selected properties optional in a type.

Generics

Generics allow the developer to write reusable type-safe code/components. In the world of statically typed languages like C# and Java, generics are commonly used for writing reusable code. Typescript takes many things from these languages, generics included.

Generics take type(s) as input and use them to derive the type of variables or functions. One of the examples is the utility type Partial<T>, which takes type T as input and gives a new type. Generics can take more than one type as input and can also have default types. Here is an example of generics with function.

/* Type is passed to the function either explicitly or implicitly */
function identity<Type>(arg: Type): Type {
return arg;
}
/* Explicitly: Type is assigned type string explicitly and the function takes string argument arg */
identity<string>("Hello");
/* Implicitly: Type implicitly takes type string based on the type of argument arg */
identity("World")

Use generics to make your code reusable without writing multiple type definitions.

Wrapping it up

Wrapping up

Using typescript with react seems like extra work in the beginning. Adding types to react JSX might make your code dirty when added without structure. Following the aforementioned habits, guidelines and conventions, I have been able to keep my code cleaner and easier to manage. Hopefully, you (the reader) were able to pick up a few helpful tips from this as well.

References ✍️

P.S. The information mentioned here is as I understand it and might not be completely accurate. If you want more details, you can check the typescript documentation or the references section.

Managing types in React Typescript, the right way was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.


This content originally appeared on Level Up Coding - Medium and was authored by Ahmed Mawia


Print Share Comment Cite Upload Translate Updates
APA

Ahmed Mawia | Sciencx (2022-10-24T01:31:34+00:00) Managing types in React Typescript, the right way. Retrieved from https://www.scien.cx/2022/10/24/managing-types-in-react-typescript-the-right-way/

MLA
" » Managing types in React Typescript, the right way." Ahmed Mawia | Sciencx - Monday October 24, 2022, https://www.scien.cx/2022/10/24/managing-types-in-react-typescript-the-right-way/
HARVARD
Ahmed Mawia | Sciencx Monday October 24, 2022 » Managing types in React Typescript, the right way., viewed ,<https://www.scien.cx/2022/10/24/managing-types-in-react-typescript-the-right-way/>
VANCOUVER
Ahmed Mawia | Sciencx - » Managing types in React Typescript, the right way. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2022/10/24/managing-types-in-react-typescript-the-right-way/
CHICAGO
" » Managing types in React Typescript, the right way." Ahmed Mawia | Sciencx - Accessed . https://www.scien.cx/2022/10/24/managing-types-in-react-typescript-the-right-way/
IEEE
" » Managing types in React Typescript, the right way." Ahmed Mawia | Sciencx [Online]. Available: https://www.scien.cx/2022/10/24/managing-types-in-react-typescript-the-right-way/. [Accessed: ]
rf:citation
» Managing types in React Typescript, the right way | Ahmed Mawia | Sciencx | https://www.scien.cx/2022/10/24/managing-types-in-react-typescript-the-right-way/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.