admin管理员组

文章数量:1404560

Given the following code:

import type {ReactNode} from 'react'
import React from 'react'

type Props = {
  title: ReactNode
}

export const Comp: React.FC<Props> = ({title}) => {
  return <div>{title}</div>
}

export const A = () => {
  return <Comp title="smth" />
}

I want to find out what type has the title property of Comp component with the Typescript Compiler API

There is the only thing I've managed to do:

import {readFileSync} from 'node:fs'
import ts from 'typescript'

function checkJSXProperties(typescript: typeof ts, ctx: ts.TransformationContext, sf: ts.SourceFile) {
  const visitor: ts.Visitor = node => {
    if (ts.isJsxSelfClosingElement(node)) {
      node.attributes.properties.forEach(attr => {
        if (!ts.isJsxAttribute(attr)) {
          return
        }

        const propertyName = attr.name.getText()

        if (typeChecker.getTypeAtLocation(attr.name).flags & ts.TypeFlags.String) {
          console.log(`Property '${propertyName}' inside props is of type 'string'`)
        } else {
          console.log(`Property '${propertyName}' inside props is NOT of type 'string'`)
        }
      })
    }

    return typescript.visitEachChild(node, visitor, ctx)
  }
  return visitor
}

const filePath = './src/scripts/index.tsx'

const program = ts.createProgram([filePath], {noEmit: true})
const typeChecker = program.getTypeChecker()

function transform(typescript: typeof ts): ts.TransformerFactory<ts.SourceFile> {
  return ctx => sf => typescript.visitNode(sf, checkJSXProperties(typescript, ctx, sf))
}

const content = readFileSync(filePath).toString()

ts.transpileModule(content, {
  compilerOptions: {
    module: ts.ModuleKind.CommonJS,
    jsxFactory: 'myJSXFactory',
    jsx: ts.JsxEmit.React,
  },
  transformers: {
    before: [transform(ts)],
  },
})

which outputs the following:

Property 'title' inside props is of type 'string'

But that's incorrect according to the Props type which tells that title prop if of type ReactNode

How can I get the type of jsx property, not the type of js property value with TS Compiler API? Or is there any other tool that can help me?

Given the following code:

import type {ReactNode} from 'react'
import React from 'react'

type Props = {
  title: ReactNode
}

export const Comp: React.FC<Props> = ({title}) => {
  return <div>{title}</div>
}

export const A = () => {
  return <Comp title="smth" />
}

I want to find out what type has the title property of Comp component with the Typescript Compiler API

There is the only thing I've managed to do:

import {readFileSync} from 'node:fs'
import ts from 'typescript'

function checkJSXProperties(typescript: typeof ts, ctx: ts.TransformationContext, sf: ts.SourceFile) {
  const visitor: ts.Visitor = node => {
    if (ts.isJsxSelfClosingElement(node)) {
      node.attributes.properties.forEach(attr => {
        if (!ts.isJsxAttribute(attr)) {
          return
        }

        const propertyName = attr.name.getText()

        if (typeChecker.getTypeAtLocation(attr.name).flags & ts.TypeFlags.String) {
          console.log(`Property '${propertyName}' inside props is of type 'string'`)
        } else {
          console.log(`Property '${propertyName}' inside props is NOT of type 'string'`)
        }
      })
    }

    return typescript.visitEachChild(node, visitor, ctx)
  }
  return visitor
}

const filePath = './src/scripts/index.tsx'

const program = ts.createProgram([filePath], {noEmit: true})
const typeChecker = program.getTypeChecker()

function transform(typescript: typeof ts): ts.TransformerFactory<ts.SourceFile> {
  return ctx => sf => typescript.visitNode(sf, checkJSXProperties(typescript, ctx, sf))
}

const content = readFileSync(filePath).toString()

ts.transpileModule(content, {
  compilerOptions: {
    module: ts.ModuleKind.CommonJS,
    jsxFactory: 'myJSXFactory',
    jsx: ts.JsxEmit.React,
  },
  transformers: {
    before: [transform(ts)],
  },
})

which outputs the following:

Property 'title' inside props is of type 'string'

But that's incorrect according to the Props type which tells that title prop if of type ReactNode

How can I get the type of jsx property, not the type of js property value with TS Compiler API? Or is there any other tool that can help me?

Share Improve this question edited Mar 10 at 17:25 Volok asked Mar 10 at 15:04 VolokVolok 736 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

In this use case, ReactNode is not the right option for determining if the property is a JSX element. With its flexibility, ReactNode can be applied to one of theese types:

  • Boolean
  • null or undefined
  • Number = String
  • A React element (result of JSX)
  • An array of any of the above

but it can't be ReactNode. when you want to get the type of the title property it would not be ReactNode, it will be one of the above types according to ReactNode type definition:

type ReactNode = string | number | bigint | boolean | React.ReactElement<any, string | React.JSXElementConstructor<any>> | Iterable<ReactNode> | React.ReactPortal | Promise<...> | null | undefined

use React.isValidElement() function

You can use isValidElement() which returns a boolean to determine if it is a JSX element or a React element or not.
export const Comp: React.FC<Props> = ({ title }) => {
  const isElement = React.isValidElement(title)
  
  console.log(isElement)

  return <div>{title}</div>
}

const Test = () => {
  return (
    <div>
      <p>test</p>
    </div>
  )
}

export const A = () => {
  return <Comp title={<Test />} /> // print: true
}

export const B = () => {
  return <Comp title={<p>hi</p>} /> // print: true
}

export const C = () => {
  return <Comp title={123} /> // print: false
}

if you want typescript to only consider jsx element you can use:

type Props = {
  title: React.ReactElement<keyof JSX.IntrinsicElements> // React components are not allowed.
}

本文标签: reactjsTS Compiler API Get jsx component property typeStack Overflow