Skip to content

TSX

This page curates a list of example ast-grep rules to check and to rewrite TypeScript with JSX syntax.

TypeScript and TSX are different.

TypeScript is a typed JavaScript extension and TSX is a further extension that allows JSX elements. They need different parsers because of conflicting syntax.

TS allows both the as operator and angle brackets (<>) for type assertions. While TSX only allows the as operator because it interprets angle brackets as JSX elements.

Avoid && short circuit in JSX Has Fix

Description

In React, you can conditionally render JSX using JavaScript syntax like if statements, &&, and ? : operators. However, you should almost never put numbers on the left side of &&. This is because React will render the number 0, instead of the JSX element on the right side. A concrete example will be conditionally rendering a list when the list is not empty.

This rule will find and fix any short-circuit rendering in JSX and rewrite it to a ternary operator.

YAML

yaml
id: do-what-brooooooklyn-said
language: Tsx
rule:
  kind: jsx_expression
  has:
    pattern: $A && $B
  not:
    inside:
      kind: jsx_attribute
fix: "{$A ? $B : null}"

Example

tsx
<div>{ list.length && list.map(i => <p/>) }</div>

Diff

tsx
<div>{ list.length && list.map(i => <p/>) }</div> 
<div>{ list.length ?  list.map(i => <p/>) : null }</div> 

Contributed by

Herrington Darkholme, inspired by @Brooooook_lyn

Rewrite MobX Component Style Has Fix

Description

React and MobX are libraries that help us build user interfaces with JavaScript.

React hooks allow us to use state and lifecycle methods in functional components. But we need follow some hook rules, or React may break. MobX has an observer function that makes a component update when data changes.

When we use the observer function like this:

JavaScript
export const Example = observer(() => {…})

ESLint, the tool that checks hooks, thinks that Example is not a React component, but just a regular function. So it does not check the hooks inside it, and we may miss some wrong usages.

To fix this, we need to change our component style to this:

JavaScript
const BaseExample = () => {…}
const Example = observer(BaseExample)

Now ESLint can see that BaseExample is a React component, and it can check the hooks inside it.

YAML

yaml
id: rewrite-mobx-component
language: typescript
rule:
  pattern: export const $COMP = observer($FUNC)
fix: |-
  const Base$COMP = $FUNC
  export const $COMP = observer(Base$COMP)

Example

js
export const Example = observer(() => {
  return <div>Hello World</div>
})

Diff

js
export const Example = observer(() => { 
  return <div>Hello World</div>         
})                                      
const BaseExample = () => {             
  return <div>Hello World</div>         
}                                       
export const Example = observer(BaseExample) 

Contributed by

Bryan Lee

Avoid Unnecessary React Hook

Description

React hook is a powerful feature in React that allows you to use state and other React features in a functional component.

However, you should avoid using hooks when you don't need them. If the code does not contain using any other React hooks, it can be rewritten to a plain function. This can help to separate your application logic from the React-specific UI logic.

YAML

yaml
id: unnecessary-react-hook
language: Tsx
utils:
  hook_call:
    has:
      kind: call_expression
      regex: ^use
      stopBy: end
rule:
  any:
  - pattern: function $FUNC($$$) { $$$ }
  - pattern: let $FUNC = ($$$) => $$$
  - pattern: const $FUNC = ($$$) => $$$
  has:
    pattern: $BODY
    kind: statement_block
    stopBy: end
constraints:
  FUNC: {regex: ^use }
  BODY: { not: { matches: hook_call } }

Example

tsx
function useIAmNotHookActually(args) {
    console.log('Called in React but I dont need to be a hook')
    return args.length
}
const useIAmNotHookToo = (...args) => {
    console.log('Called in React but I dont need to be a hook')
    return args.length
}

function useTrueHook() {
    useEffect(() => {
      console.log('Real hook')
    })
}

Contributed by

Herrington Darkholme

Made with ❤️ with Rust