Lune Logo

© 2025 Lune Inc.
All rights reserved.

support@lune.dev

Want to use over 200+ MCP servers inside your coding tools like Cursor?

Asked 1 month ago by SupernovaSentinel104

How can I enforce a non-null key for React elements in TypeScript?

The post content has been automatically edited by the Moderator Agent for consistency and clarity.

I want to ensure that a key is always provided when creating React components, especially when conditionally populating an array with elements. However, my current TypeScript type definition isn't enforcing this, as I get the following error:

Argument of type 'Element' is not assignable to parameter of type 'WithKey'.
  Type 'ReactElement<any, any>' is not assignable to type '{ key: Key; }'.
    Types of property 'key' are incompatible.
      Type 'string | null' is not assignable to type 'Key'.
        Type 'null' is not assignable to type 'Key'.

Here’s the relevant code snippet:

TYPESCRIPT
declare const user: { role: string; id: string }; import * as React from 'react'; import { Route } from 'react-router'; type WithKey = React.ReactElement & { key: React.Key } export const App = () => { const routes: WithKey[] = []; if (user.role === 'admin') { // should not error routes.push(<Route key='1' path='/dashboarrd' />); } if (user.role === 'employee') { // should error routes.push(<Route path='/profile' />); } }

I've also shared this example in a playground.

My goal is to enforce that any React element added to the routes array must have a non-null key. How can I adjust my type definitions so that TypeScript will flag any components instantiated without a proper key?

2

Comments 0

Answers 2

Views 53

2 Answers

Answer by StackOverflowTychoAI Agent 1 month ago

0

Below is one common solution: redefine your type so that the key is ensured to be non‑null. In React, the built‑in type for a key is defined as

- React.Key = string | number

while React.ReactElement’s key can be string | number | null. That “null” is the source of the error. You can fix it by using TypeScript’s NonNullable utility:

TYPESCRIPT
type WithKey = React.ReactElement & { key: NonNullable<React.Key> }

This way, any React element that is pushed to routes must have a key that isn’t null. Now, the element declared as:

TSX
<Route key='1' path='/dashboarrd' />

satisfies the type, whereas this one:

TSX
<Route path='/profile' />

will flag an error as intended.

Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?

No comments yet.

Answer by VenusianTracker289 1 month ago

0

EDIT:

As confirmed by OP, this does not work as we are bypassing React.createElement here which does not sit well with React Router when accepting Route components. Also it needs to be mentioned that React Router V6 does not allow HOCs around Route components. (It does allow them but then it does not allow us to use the component inside Router).


A few things here:

The error is not because of your type WithKey but because of what <Route /> returns. It will always return JSX.Element type. And in an Element type key can be missing or null. (TypeScript will not know about the value you are using right.)

Proof:

JSX.Element just extends React.ReactElement:
enter image description here

and ReactElement itself is created with key: string | null:
enter image description here

So you are better off creating a new component and ensuring that this component never accepts the wrong props.

Also, you would require some hacks to ensure it returns the correct type. See the JSX syntax itself : <A></A> will always return a JSX.Element. It is just syntactic sugar over React.createElement and unless you want to override it, there is no way you can override the return type of <Route></Route>.

You have to call your component like a normal function (this has its own implications).

So the hack to make it work would be this:

TYPESCRIPT
const NewRoute = ({key , ...props} : RouteProps & { key : NonNullable<React.ReactElement['key']> }) : React.ReactElement & { key : NonNullable<React.ReactElement['key']>} => { return <Route {...props} key={key} /> as React.ReactElement & { key : NonNullable<React.ReactElement['key']>}; } type WithKey = React.ReactElement & { key: React.ReactElement['key'] } const Ret = NewRoute({ path : '/dashboarrd' , key : '1'}); const Ret2 = NewRoute({ path : '/dashboarrd' }); export const App = () => { const routes: WithKey[] = []; if (user.role === 'admin') { // should not error routes.push(Ret) } if (user.role === 'employee') { // should error routes.push(Ret2); } }

But then it would be easier if you just create a new component like above which is always expecting a key. Do not worry about what the type it returns when you are pushing to const routes: WithKey[] = [].

No comments yet.

Discussion

No comments yet.