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 badges1 Answer
Reset to default 0In 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.
}