Skip to main content

Flow: React RestrictedElement

· 2 min read

RestrictedElement<typeof MenuItem>

export type RestrictedElement<+TElementType: React$ElementType> = {|
+type:
| TElementType
| ClassComponentRender<RestrictedElement<TElementType>>
| FunctionComponentRender<RestrictedElement<TElementType>>,
+props: any, // The props type is already captured in the type field,
// and using ElementProps recursively can get very expensive. Instead
// of paying for that computation, I decided to use any

+key: React$Key | null,
+ref: any,
|};

flow.org/try, alternative to: https://flow.org/en/docs/react/children/#toc-only-allowing-a-specific-element-type-as-children

The one recommended in the docs is just for children with exactly that type. RestrictedElement lets you also use things that render things of that type. So suppose you have some Button class. Let's say the button has a disabled flag. You might want to make another class:

class DisabledButton {
render(): React.Element<typeof Button> { ... }
}

You can't use DisabledButton inside a children array for React.Element<typeof Button> but you can in RestrictedElement<typeof Button>. (via @jbrown215)

Real example:

// @flow

import * as React from 'react';

import { type RestrictedElement } from './RestrictedElement';

class Button extends React.Component<{| +disabled?: boolean |}> {
render() {
return null;
}
}

class DisabledButton extends React.Component<{||}> {
// The return type is not necessary - it's here only to demonstrate what is going on.
render(): React.Element<typeof Button> {
return <Button disabled={true} />;
}
}

class WrapperStupid extends React.Component<{|
children: React.ChildrenArray<
// You have to specify every single supported component here.
React.Element<typeof Button> | React.Element<typeof DisabledButton>,
>,
|}> {}

class WrapperSmart extends React.Component<{|
// Type `RestrictedElement` understands what is being rendered so it accepts even `DisabledButton` (because it returns `Button`).
children: React.ChildrenArray<RestrictedElement<typeof Button>>,
|}> {}

const testStupid = (
<WrapperStupid>
<Button />
<Button />
<DisabledButton />
</WrapperStupid>
);

const testSmart = (
<WrapperSmart>
<Button />
<Button />
<DisabledButton />
</WrapperSmart>
);