#A react component that forwards the valid props for a dynamic one?

22 messages · Page 1 of 1 (latest)

flint sage
#

I have this component I want to create that can take an element and then forward the props for that element as valid props. Anyone know why this doesn't work?

Bonus: Would be great also to be able to pass any component and not just a valid html element name such as button

type Props<TTag extends keyof JSX.IntrinsicElements> = {
  as: TTag
} & React.ComponentProps<TTag>

function Box<TTag extends keyof JSX.IntrinsicElements>({ as: As, ...restProps }: Props<TTag>) {
  return <As {...restProps} />;
}
ancient sentinel
#

You'd have to distribute for one:

type AugmentedProps = {
    [K in keyof JSX.IntrinsicElements]: { as: K } & React.ComponentProps<K>
}

function Box<TTag extends keyof JSX.IntrinsicElements>({ as: As, ...restProps }: AugmentedProps[TTag]) {
  return <As {...restProps} />;
}
#

But you're still running into the correspondence problem I think

lean kelp
#

also looks like As in Box would just be a string with this setup. the "element name" in a JSX tag needs to be a function

#

or maybe it's supposed to be a value with a .render property or something like that? i don't actually use react, but i know for sure that it's not just a string

ancient sentinel
#

it is

#

for raw HTML elements

lean kelp
#

wait... are you saying you can do this in react?

const element = 'div'
<element>hi</element>
ancient sentinel
#

yes

lean kelp
#

neat, TIL, thanks

ancient sentinel
#

no wait

#

@lean kelp

#

You'd have to use Element

#

otherwise it will try to create the tag <element>

lean kelp
#

i guess that's to make nonsense like this impossible:

const p = 'div'
<p>hi</p>
ancient sentinel
#

well that plus you also have to support custom tags

#

it interprets every lowercase tag as an intrinsic tag

lean kelp
#

i think i learned that at some point, but don't touch react often enough to remember it

lean kelp
#

looked at this again, it is the correspondence problem, just with a confusing error message obscuring that fact. this makes it clearer:

wild gateBOT
#
mkantor#7432

Preview:```ts
import React from "react"

type AugmentedProps = {
[K in keyof JSX.IntrinsicElements]: {
as: K
} & React.ComponentProps<K>
}

function Box<
TTag extends keyof JSX.IntrinsicElements

({as: As, ...restProps}: AugmentedProps[TTag]) {
const Element: TTag =
...```

lean kelp
#

!:correspondence-problem

wild gateBOT
#
Retsam19#2505
`!retsam19:correspondence-problem`:

There's a particular pattern that is safe but hard for the Typescript compiler to handle, which I call the "correspondence problem":

const functionsWithArguments = [
  { func: (arg: string) => {}, arg: "foo" },
  { func: (arg: number) => {}, arg: 0 },
];

for (const { func, arg } of functionsWithArguments) {
  func(arg);
//     ^^^
// Argument of type 'string | number' is not assignable to parameter of type 'never'.
//   Type 'string' is not assignable to type 'never'.
}

The problem is that func is typed as (x: string) => void | (x: number) => void and arg is string | number, but the compiler can't prove that they "correspond": that, for example, arg is only a string when func accepts strings.

As far as the type are concerned, arg could be number, and func could be (arg: string) => void, and that would be a type-error. It's easy for us to see that that won't happen, but that requires understanding the program at a higher-level than the level the compiler operates.

Depending on the specifics there's sometimes clever fixes, but usually I recommend using a type assertion and ignoring the issue:

func(arg as never);