How To Strongly Type process.env
Learn how to strongly type process.env in TypeScript by either augmenting global type or validating it at runtime with t3-env.
If you've worked with TypeScript and JSX for enough time, you'll probably have run into a situation where you want to restrict a component's children to be of a certain type.
Let's say you're working on a Select
component that takes Option
components as children. You want to make sure that the Option
components are the only children that can be passed to Select
.
tsx
// GOOD<Select><Option value="1">One</Option><Option value="2">Two</Option><Option value="3">Three</Option></Select>// BAD<Select><div>One</div><div>Two</div><div>Three</div></Select>
It might feel like there are options available to you. Perhaps using ReactElement
, or ReactNode
, or ReactChild
. But as it turns out, nothing works.
You can't tell TypeScript 'only use this type of component as children'.
<Component />
is Always JSX.Element
The reason for this comes down to the way that TypeScript interprets JSX.
Whenever TypeScript sees a JSX tag, it treats it as a JSX.Element
. It doesn't matter what the component is, or what its props are. It's always a JSX.Element
.
tsx
constelement1 = <Component />;constelement2 = <div />;constelement3 = <span />;
Let's imagine that we wanted to make our Select
component only accept Option
components as children. We might try to do something like this:
tsx
type SelectProps = {children: ReactElement<OptionProps>[];};
But our Option
components always return JSX.Element
.
This is even true if we use as
to override the return type of the component:
tsx
constOption = () =>(<option />) as any as "I should be showing below!";constelement = <Option />;
Funnily enough, this does work if you call Option()
manually:
tsx
constelement =Option ();
Because this bypasses the JSX interpretation, TypeScript is able to understand that Option
returns our special string.
But calling Option()
manually is a terrible idea in React - it breaks all sorts of assumptions React makes about your code and will immediately cause bugs.
So, this approach just doesn't work.
It's not possible to restrict the children of a React component to a certain type in TypeScript.
But here's something to ponder - would you even really want to?
The magic of React is that components can be composed in any way you like. By restricting the children of a component, you're breaking that composability.
Perhaps instead of restricting the type of children passed to a component, you could use a prop instead:
tsx
<Selectoptions={[{ value: "1", label: "One" },{ value: "2", label: "Two" },{ value: "3", label: "Three" },]}/>
This way, you can still restrict the type of the children, but you're not breaking the composability of your components. Learn why it's not possible to restrict the type of children in React component with TypeScript Explore alternative approaches for component composition.
Share this article with your friends
Learn how to strongly type process.env in TypeScript by either augmenting global type or validating it at runtime with t3-env.
Discover when it's appropriate to use TypeScript's any
type despite its risks. Learn about legitimate cases where any
is necessary.
Learn why TypeScript's types don't exist at runtime. Discover how TypeScript compiles down to JavaScript and how it differs from other strongly-typed languages.
Improve React TypeScript performance by replacing type & with interface extends. Boost IDE and tsc speed significantly.
In this book teaser, we discuss deriving vs decoupling your types: when building relationships between your types or segregating them makes sense.
Learn how TypeScript's new utility type, NoInfer, can improve inference behavior by controlling where types are inferred in generic functions.