Exploring Child Types in Typescript and JSX minus the React framework

It seems like there's a missing piece of the puzzle that I can't quite figure out. Despite going through the documentation on JSX in non-React settings, I'm still unable to spot my mistake. Let's examine the following code:

/** @jsx pragma */

function pragma(node: any, props: any, ...children: any[]) {
  return node;
}

declare namespace JSX {
  type Element = string;
  interface ElementChildrenAttribute {
    children: {}; // specify children name to use
  }
}

interface PropsType {
  children: string;
  name: string;
}

export const Prop = (props: PropsType) => {};

const foo = <Prop name="foo">hello</Prop>;

The error message I encounter (on the last line) is:

Property 'children' is missing in type '{ name: string; }' but required in type 'PropsType'.

I could "fix" this by doing:

const foo = <Prop name="foo" children="bye">hello</Prop>;

However, I expected that the hello would automatically be treated as the children. Isn't it unnecessary to explicitly specify a children property? What am I overlooking here? Wasn't the original code supposed to work without generating an error since the children should be filled with the string value "hello" automatically (without needing me to add a children prop)?

What is the missing element here? Do I need to complete some other field or type in the JSX namespace to make this function?

Clarification: My concern lies in TypeScript not assigning the string "hello" to children. If it did, everything would fall into place. It appears as if it's not an issue with my prop types but rather a gap in how I redefine the JSX namespace for handling my "custom" JSX.

Additionally, although having children in my props is not obligatory, it is permissible and desired because it helps refine the type. This practice is indeed allowed (refer to the second paragraph here) for exactly this reason. Adding it here does not introduce a new prop; rather, it simply specifies what was outlined in JSX.ElementChildrenAttribute.

Answer №1

In the world of React, there is a handy type called PropsWithChildren<T>. While React relies on it, you have the freedom to craft your own version and substitute the React-specifics with JSX.

This versatile type should cover all scenarios for what Children may consist of.

type Children = JSX.Element[] | JSX.Element | string | number | boolean | null | undefined;

type PropsWithChildren<P> = P & {
    children?: Children;
}

type PropsType = {
    name: string;
}

export const Bar = ({ name, children }: PropsWithChildren<PropsType>) => (
    <div>
        <p>{name}</p>
        <div>{children}</div>
    </div>
);

I've thoroughly tested these snippets, and they are free from any type errors.

export const ElementArray = () => (
    <Bar name="Foo">
        <div>Hello</div>
        <div>World</div>
    </Bar>
)

export const Element = () => (
    <Bar name="Foo">
        <div>Hello World</div>
    </Bar>
)

export const String = () => (
    <Bar name="Foo">Hello World</Bar>
)

export const Number = () => (
    <Bar name="Foo">{43110}</Bar>
)

export const Boolean = () => (
    <Bar name="Foo">{true}</Bar>
)

export const Null = () => (
    <Bar name="Foo">{null}</Bar>
)

export const Undefined = () => (
    <Bar name="Foo" />
)

You also have the ability to define your own FC (Functional Component) type by utilizing PropsWithChildren, allowing you to use it similarly to how it's employed in React.

type FC<P = {}> = {
    (props: PropsWithChildren<P>): JSX.Element;
}

// example
export const FooBar: FC<PropsType> = ({ name, children }) => (
    <div>
        <p>{name}</p>
        <p>{children}</p>
    </div>
);

Answer №2

Your original code lacks a minor detail, the inclusion of declare global. By adding it in, everything functions smoothly without any errors:

/** @jsx pragma */

function pragma(node: any, props: any, ...children: any[]) {
  return node;
}

declare global {
  namespace JSX {
    type Element = string;
    interface ElementChildrenAttribute {
      children: {}; // defining children name
    }
  }
}

interface PropsType {
  children: string;
  name: string;
}

export const Prop = (props: PropsType) => { return null };

const foo = <Prop name="foo">hello</Prop>; // assuming children

Answer №3

Modify

interface PropsType {
  children: string;
  name: string;
}

turn it into

interface PropsType {
  name: string;
}

as children is already defined in the ElementChildrenAttribute. You're essentially replacing children as a property rather than as a special attribute

Answer №4

After some investigation, I finally managed to resolve the issue at hand. Surprisingly, the documentation failed to shed light on why my initial code was ineffective and why the revised approach proved successful.

Upon examining my code closely, I identified a minor error. The Prop component was not returning a value of type

JSX.Element</code as it should have been. Although rectifying this mistake was straightforward, it did not address the primary concern:</p>
<pre><code>export const Prop = (props: PropsType) => "world";
export const Buz = (props: { id: string }) => "test2";

Furthermore, I made a crucial realization during this process. Visual Studio Code issued a warning indicating that the JSX value in my code was unused. Initially, I assumed this declaration existed solely for the compiler's benefit and disregarded it. However, upon relocating it to a separate file named jsx.d.ts, I noticed a significant improvement. Here is the contents of my jsx.d.ts file:

declare namespace JSX {
  type Element = string;
  interface ElementChildrenAttribute {
    children: {}; // specify children name to use (type seems irrelevant)
  }
}

The updated code within a .tsx file now appears as follows:

/** @jsx pragma */

function pragma(node: any, props: any, ...children: any[]) {
  return node;
}

export interface PropsType {
  name: string;
  children: string;
}

export const Prop = (props: PropsType) => "test";
export const Buz = (props: { id: string }) => "test2";

Subsequent to this modification, all lines of code started behaving according to my expectations:

const ok1 = <Prop name="foo">hello</Prop>; // Compiles!
const err1 = <Prop name="bar" />; // Error children missing
// more examples here

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Exploring Local Gems with Google Places API in Ionic 2

I recently integrated the Google Maps API into my app and now I am looking to incorporate Google Places as well. I have managed to successfully implement Geolocation, but I am facing difficulties when trying to perform a nearby search for "supermarkets" in ...

Resolve the issue pertaining to the x-axis in D3 JS and enhance the y-axis and x-axis by implementing dashed lines

Can anyone assist with implementing the following features in D3 JS? I need to fix the x-axis position so that it doesn't scroll. The values on the x-axis are currently displayed as numbers (-2.5, -2.0, etc.), but I want them to be shown as percentag ...

Using immer JS to update a nested value has been successfully completed

What is the most efficient way to recursively change a value using a recursive function call in a Produce of Immer? The WhatsappState represents the general reducer type, with Message being the message structure for the application/db. type WhatsappState = ...

unable to retrieve information from the redis cache

Attempting to retrieve data from cache using the readData function in the controller file. Encountering an issue where the someVal variable is initially undefined after calling readData, but eventually gets populated with data after receiving a callback ...

Utilize Pipe for every instance of a variable in the Controller

In my controller, I have multiple requests (POST, GET etc.) where the path includes an id parameter that needs to be a number string. I want to validate this parameter once and have it apply to all instances. Here is the current code snippet: @Get(&apo ...

Tips for extracting key values from an array of objects in Typescript

I am working with an array called studyTypes: const studyTypes = [ { value: "ENG", label: "ENG-RU", }, { value: "RU", label: "RU-ENG", }, ]; Additionally, I have a state variable set ...

PhpStorm is unable to resolve the @ionic/angular module

I have encountered a peculiar issue with my Ionic v4 project. While the project runs smoothly, PhpStorm seems unable to locate my references to @ionic. https://i.stack.imgur.com/umFnj.png Interestingly, upon inspecting the code, I realized that it is act ...

Guide on setting a default value for a variable within a Typescript class

In the process of developing a component to manage information about fields for form use, I am in need of storing different data objects in order to establish generic procedures for handling the data. export class DataField<T> { /** * Field ...

Create a function in JavaScript that is able to accept a variable number of objects as arguments

I have a good grasp of how to pass infinite parameters in a function in JavaScript. But what about accepting any number of objects as parameters in a function? This is my current implementation: function merge<T>(objA: T, objB: T){ return Object. ...

In TypeScript, the error "Property does not exist on type 'any[]'" indicates that a specific property is not recognized on

Working on my project using Textscript in Next Js has been mostly smooth sailing, but I keep encountering warnings in my script that say 'Property does not exist on type any[ ]'. The red line under the name, image, and price properties is a sourc ...

Service function in Angular 2 is returning an undefined value

There are two services in my project, namely AuthService and AuthRedirectService. The AuthService utilizes the Http service to fetch simple data {"status": 4} from the server and then returns the status number by calling response.json().status. On the ot ...

Can we verify if this API response is accurate?

I am currently delving into the world of API's and developing a basic response for users when they hit an endpoint on my express app. One question that has been lingering in my mind is what constitutes a proper API response – must it always be an o ...

How can I display JSON values without revealing the parent in Angular 5 and Ionic 3?

I am trying to extract values from JSON without the parent keys. Here is the JSON structure I have: [ { "companies": [{ "id": 1, "name": "Prueba", "company_number": "23423423A", "latitude": 241241.12, "lo ...

Creating a backup link for a video player component in NextJs

My aim is to make sure that two video player components in a NextJS application can still access and play videos even when the application is running locally using npm run dev without an internet connection. Currently, these two components.. <HoverVi ...

When you call Subscribe(), you will receive the output "complete: () => void; 'complete' is defined in this context. What exactly does this signify?

Currently, I am following a tutorial and after meticulously checking and rechecking, I can confidently say that my code matches exactly with the one the professor is using. The tutorial involves creating a simple Single Page Application in Angular using Ty ...

Instructions for utilizing ObjectId with a string _id on the client side

Is there a way to retrieve a document using the _id in string format? Here is an example of the code on the client side: 'use client' ... const Page(){ ... fetch("api/get_data", { method: 'POST', ...

Reasons why a functional component may not trigger a rerender after a state change using useReducer()

When using react Hooks, specifically useReducer, I found that although the state changes, the functional component does not rerender. Additionally, when trying to open the drawer by pressing a button in the menu, even though the state changes the drawer re ...

Guide on incorporating text input areas into specific positions within a string

Looking for a way to replace specific words in a string with input fields to enter actual values? For example... Dear Mr. [Father_name], your son/daughter [name] did not attend class today. This is what I want it to look like... Dear Mr. Shankar, your ...

What is the best way to pass a variable from a class and function to another component in an Angular application?

One of the components in my project is called flow.component.ts and here is a snippet of the code: var rsi_result: number[]; @Component({ selector: 'flow-home', templateUrl: './flow.component.html', styleUrls: ['./flow.comp ...

What causes TypeScript to automatically infer a default property when dynamically importing a JavaScript file that lacks a default export?

While dynamically importing a javascript file that exports multiple functions (without a default export), I encountered this issue: const sayHi = import('./sayHi.js') I was expecting the type of sayHi to be Promise<{name1: function, name2: fu ...