最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

reactjs - TS Compiler API: Get jsx component property type - Stack Overflow

programmeradmin3浏览0评论

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.
}
发布评论

评论列表(0)

  1. 暂无评论