Home > Enterprise >  renderable "type trait" in typescript react?
renderable "type trait" in typescript react?

Time:12-29

I guess I'm still a C programmer at heart. I was just spinning up a very simple helper class to clean up some debug UI:

const MaybeIfValue: React.FC<{text: string, value: any}> = ({text, value}) => {
    if (value === undefined) {
        return null;
    }
    if (value === null) {
        return null;
    }
    return (
        <div>
            <span>{text}</span>{value}<br/>
        </div>
    );
}

...and I was thinking, I really don't want any value here. Not that it's going to matter here in practice, this is a one off helper function. But plenty of times in development over the years I've accidentally tried to render something complex. I'm still only moderately experienced with Typescript, so I don't quite know the right term for it, but what I'm wondering about, is how do I static_assert that the compile-time typeof value is renderable? I'd call it a type trait in C . What's the term in typescript? Matching?

CodePudding user response:

I think the word you are looking for is simply "type". And avoiding the any is a very good practice, since it drops all type checking.

ReactNode is technically the type for something renderable, but it's a bit too permissive since it seems to allow objects and functions.

ReactChild is the type you want, I believe. It is the type of "something renderable", that can typically just be dropped into a pair of {}.

import React, { ReactChild } from 'react'

const MaybeIfValue: React.FC<{text: string, value: ReactChild}> = ({text, value}) => {
  //...
}

Tests:

// Works
const goodStr = <MaybeIfValue text='good' value='foo' />
const goodNum = <MaybeIfValue text='good' value={123} />
const goodJsx = <MaybeIfValue text='good' value={<>Foo</>} />

// type errors
const badObj = <MaybeIfValue text='good' value={{ obj: true }} />
const badFn = <MaybeIfValue text='good' value={() => null} />

Playground


Also, quick tip, the one time you're allowed to use == instead of === is for comparisons to null, which which be truthy for both null and undefined. It saves a lot of lines of code.

I, too, have a line much like this at the top of many of my own components.

if (value == null) return null

CodePudding user response:

Edit: updated example to constrain generic T by ReactChild after seeing this answer.

I'm not 100% sure I understand what you're asking, but it sounds like you want a strongly-typed component function.

Here's a link to the functions page in the TS handbook, specifically the section which addresses overload signatures, which is what I'm using in the snippet below to provide multiple call signatures, each with it's own return type that depends on the type of props.value.

TS Playground

import {default as React, ReactChild, ReactElement} from 'react';

function MaybeIfValue (props: { text: string; value?: null | undefined }): null;
function MaybeIfValue <T extends ReactChild>(props: { text: string; value: T }): ReactElement;
function MaybeIfValue ({text, value}: { text: string; value?: unknown }) {
  return (value === null || value === undefined) ? null : (
    <div>
      <span>{text}</span>{value}<br/>
    </div>
  );
};

MaybeIfValue({text: 'hello'}) // null
MaybeIfValue({text: 'hello', value: null}) // null
MaybeIfValue({text: 'hello', value: undefined}) // null

MaybeIfValue({text: 'hello', value: false}) /*
                             ^^^^^
Error: Type 'boolean' is not assignable to type 'ReactChild'.(2769) */

MaybeIfValue({text: 'hello', value: 12}) // ReactElement
MaybeIfValue({text: 'hello', value: 'hello'}) // ReactElement
// etc...
  • Related