# TypeScript for React Developers Series: How to Get Started

By adopting TypeScript, React developers take advantage of the language’s type safety, code analysis, and refactoring capabilities. In this article, we’ll explore the steps needed to adopt TypeScript.

## **Prerequisites:**

* Familiar with basic TypeScript types
    
* Familiar with React.js
    
* Reading the [previous article](https://blog.othmane.me/typescript-for-react-developers-series-why-you-should-adopt-it)
    

## **Naming Conventions:**

* use PascalCase for class, component and Enum names.
    
* use camelCase for variable and function names.
    
* do not use `I` as prefix of the interface instead use `ComponentNameProps` or `FunctionNameParams`.
    
* use `.generated.*` as suffix for the files are generated and you don't edit them.
    

```typescript
interface ComponentNameProps {
  position: 'left' | 'right'
}
function ComponentName({ position }: ComponentNameProps) {
  if (position === 'left') {
    return <div>left</div>
  }
  return <div>right</div>
}

interface FunctionNameParams {
  args: string[]
}
function functionName({ args }: FunctionNameParams) {
  return args.join(' ')
}
```

## **Types:**

* put the shared types in `types.ts` file.
    
* use generics to avoid duplicating the components/functions.
    
* do not export functions/types that not shared across multi-components.
    
* in your component file, the type definitions must comes the first.
    
* use [Utility Types](https://www.typescriptlang.org/docs/handbook/utility-types.html) to avoid duplicating of the type definitions.
    
* use `readonly` to define the data coming from your API
    

## **Components:**

* don’t use `FunctionComponent<T>` or `FC<T>` to define your component, because it's explicit about the return type, while the normal function version is implicit, read more about this [codemod-replace-react-fc-typescript](https://github.com/gndelia/codemod-replace-react-fc-typescript).
    
* use `ReactNode` instead `JSX.Element` to define your children when use the normal functions, because `JSX.Element` is the return value of `React.createElement`, while `ReactNode` is return value of a component
    
* use generic components to make your component more flexible, [read more about this topic](https://www.developerway.com/posts/typescript-generics-for-react-developers).
    
* use the `React.ComponentPropsWithoutRef<T>` as Wrapping/Mirroring a HTML Element when you want to make component that takes all the normal props of this Element.[read more about this topic](https://react-typescript-cheatsheet.netlify.app/docs/advanced/patterns_by_usecase)
    

```typescript
// bad
interface MyCustomLinkProps {
  href: string
  children?: React.ReactNode
}
function MyCustomLink(props: MyCustomLinkProps) {
  const { children, href } = props
  return <a href={href}>{children}</a>
}
// most recommended
function MyCustomLink(props: React.ComponentPropsWithoutRef<'a'>) {
  return <a {...props}>{children}</a>
}
```

* use `Fragment` or use the shorthand `<>` instead `div` in wrapping the component.
    

```typescript
// bad
function ComponentName() {
  return (
    <div>
      <section></section>
      <section></section>
    </div>
  )
}

// most recommended
function ComponentName() {
  return (
    <>
      <section></section>
      <section></section>
    </>
  )
}
```

## **Forms and Events:**

* inline event handler:
    

```typescript
const InputComponent = () => (
  <input
    // TypeScript automatically inffers it
    onChange={(e) => {
      // logic here
    }}
  />
)
```

* separated event handler:
    

```typescript
const InputComponent = () => {
  const onChnageInputHandler = (e: React.FormEvent<HTMLInputElement>): void => {
    // your logic here
  }
  return <input onChange={onChnageInputHandler} />
}
```

or:

```typescript
const InputComponent = () => {
  const onChnageInputHandler: React.ChangeEventHandler<HTMLInputElement> = (
    e
  ) => {
    // your logic here
  }
  return <input onChange={onChnageInputHandler} />
}
```

* `onSubmit` definition for uncontrolled form component
    

```typescript
const Form = () => (
  <form
    onSubmit={(e: React.SyntheticEvent) => {
      e.preventDefault()
      const target = e.target as typeof e.target & {
        email: { value: string }
        password: { value: string }
      }
      const email = target.email.value // typechecks!
      const password = target.password.value // typechecks!
      // etc...
    }}
  >
    <label>
      Email:
      <input type="email" name="email" />
    </label>
    <label>
      Password:
      <input type="password" name="password" />
    </label>
    <button type="submit">Login</button>
  </form>
)
```

you can use formik or any other library for handling Forms, those libraries are built on the principles of controlled components

## **Hooks:**

* `useState` hook :
    

```typescript
const Component = () => {
  //1- inferred type:
  // `state` is inferred to be a number
  const [state, setState] = useState(0)
  // `stringState` is inferred to be a string
  const [stringState, setStringState] = useState('')
  //2- implicit type:
  const [state1, setState2] = useState<number | undefined>()
  return <></>
}
```

* `useCallback` hook :
    

```typescript
//   it's just like any other function
interface MemoizedFunctionParams {
params:string[]
}
const memoizedFunction = useCallback(
  (args:MemoizedFunctionParams) => {
    return 'text'
  },
  [...],
);
```

* `useReducer` hook:
    

```typescript
import { useReducer } from 'react'

const initialState = { count: 0 }
type ACTIONTYPE =
  | { type: 'increment'; payload: number }
  | { type: 'decrement'; payload: string }
function reducer(state: typeof initialState, action: ACTIONTYPE) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + action.payload }
    case 'decrement':
      return { count: state.count - Number(action.payload) }
    default:
      throw new Error()
  }
}
function Counter() {
  // If you don't define return type, TypeScript automatically infers it.
  const [state, dispatch] = useReducer(reducer, initialState)
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: 'decrement', payload: '5' })}>
        -
      </button>
      <button onClick={() => dispatch({ type: 'increment', payload: 5 })}>
        +
      </button>
    </>
  )
}
```

* `useRef` hook :
    

```typescript
import { useRef, useEffect } from 'react'
// This is just an example, React recommended you to use Controlled components
function Component() {
  const inputRef = useRef<HTMLInputElement>(null)
  const divRef = useRef<HTMLDivElement>(null)
  useEffect(() => {
      if (!inputRef) {
        throw Error('input not mounted !!!!')
      }
      inputRef.current.value = 'assign value'
    }, [])
    const onChnageButtonHandler = () => {
      divRef.current.innerHTML = 'we change this text here'
    }
  return (
    <div>
      <input ref={inputRef} />
      <div ref={divRef}>this is div content</div>
      <button onClick={onChnageButtonHandler}>change the content</button>
    </div>
  )
}
```

# **Context:**

* with default value:
    

```typescript
import { createContext, useState, useContext } from 'react'

type ThemeContextType = 'light' | 'dark'
const ThemeContext = createContext<ThemeContextType>('light')
const App = () => {
  const [theme, setTheme] = useState<ThemeContextType>('light')
  return (
    <ThemeContext.Provider value={theme}>
      <MyComponent />
    </ThemeContext.Provider>
  )
}
const MyComponent = () => {
  const theme = useContext(ThemeContext)
  return <p>The current theme is {theme}.</p>
}
```

* without default value:
    

```typescript
import { createContext, useState, useContext } from 'react'

type userContextType = { name: string; email: string }
const UserContext = createContext<userContextType>()
const App = () => {
  const [user, setUser] = useState<userContextType>({
    name: 'name',
    email: 'email@mail.com',
  })
  return (
    <ThemeContext.Provider value={...{ user, setUser }}>
      <MyComponent />
    </ThemeContext.Provider>
  )
}
const MyComponent = () => {
  const { user } = useContext(UserContext)
  return <p>The current theme is {user?.name}.</p>
}
```

# **Conclusion:**

In conclusion, using TypeScript with React can write safer, more maintainable code, and catch errors earlier in the development process. Additionally, TypeScript’s static typing can help improve developer productivity by providing better tooling and documentation.
