[React+Tailwind] Form を TypeScript に書き直してみた | 心を無にして始める React

準備

書き直すベースはこちらの Form コンポーネントです。

JavaScript

ベースの Form.js です。

import { forwardRef } from 'react';
import {
  Checkbox as FlowbiteCheckbox,
  FileInput as FlowbiteFileInput,
  Label as FlowbiteLabel,
  Radio as FlowbiteRadio,
  Select as FlowbiteSelect,
  Textarea as FlowbiteTextarea,
  TextInput as FlowbiteTextInput,
  ToggleSwitch as FlowbiteToggleSwitch,
} from 'flowbite-react';

const Form = forwardRef((
  {
    children,
    ...otherProps
  },
  ref,
) => {
  return (
    <form
      ref={ref}
      {...otherProps}
    >
      {children}
    </form>
  );
});

const Checkbox = forwardRef((
  {
    ...otherProps
  },
  ref,
) => {
  return (
    <FlowbiteCheckbox
      ref={ref}
      {...otherProps}
    />
  );
});

const FileInput = forwardRef((
  {
    color = "gray",
    helperText,
    sizing = "md",
    ...otherProps
  },
  ref,
) => {
  return (
    <FlowbiteFileInput
      color={color}
      helperText={helperText}
      ref={ref}
      sizing={sizing}
      {...otherProps}
    />
  );
});

const Label = forwardRef((
  {
    children,
    color = 'default',
    disabled = false,
    value,
    ...otherProps
  },
  ref,
) => {
  return (
    <FlowbiteLabel
      color={color}
      disabled={disabled}
      ref={ref}
      value={value}
      {...otherProps}
    >
      {children}
    </FlowbiteLabel>
  );
});

const Radio = forwardRef((
  {
    ...otherProps
  },
  ref,
) => {
  return (
    <FlowbiteRadio
      ref={ref}
      {...otherProps}
    />
  );
});

const Select = forwardRef((
  {
    addon,
    children,
    color = 'gray',
    helperText,
    icon,
    shadow = false,
    sizing = 'md',
    ...otherProps
  },
  ref,
) => {
  return (
    <FlowbiteSelect
      addon={addon}
      color={color}
      helperText={helperText}
      icon={icon}
      ref={ref}
      shadow={shadow}
      sizing={sizing}
      {...otherProps}
    >
      {children}
    </FlowbiteSelect>
  );
});

const Textarea = forwardRef((
  {
    color = 'gray',
    helperText,
    shadow = false,
    ...otherProps
  },
  ref,
) => {
  return (
    <FlowbiteTextarea
      color={color}
      helperText={helperText}
      ref={ref}
      shadow={shadow}
      {...otherProps}
    />
  );
});

const TextInput = forwardRef((
  {
    addon,
    color = 'gray',
    helperText,
    icon,
    shadow = false,
    sizing = 'md',
    ...otherProps
  },
  ref,
) => {
  return (
    <FlowbiteTextInput
      addon={addon}
      color={color}
      helperText={helperText}
      icon={icon}
      ref={ref}
      shadow={shadow}
      sizing={sizing}
      {...otherProps}
    />
  );
});

const ToggleSwitch = forwardRef((
  {
    checked = false,
    label,
    onChange,
    ...otherProps
  },
  ref,
) => {
  return (
    <FlowbiteToggleSwitch
      checked={checked}
      label={label}
      onChange={onChange}
      ref={ref}
      {...otherProps}
    />
  );
});

export default Object.assign(Form, {
  Checkbox,
  FileInput,
  Label,
  Radio,
  Select,
  Textarea,
  TextInput,
  ToggleSwitch,
});

TypeScript

Form.tsx として編集します。

import { ComponentProps, forwardRef, LegacyRef, Ref, PropsWithChildren } from 'react';
import {
  Checkbox as FlowbiteCheckbox,
  CheckboxProps as FlowbiteCheckboxProps,
  FileInput as FlowbiteFileInput,
  FileInputProps as FlowbiteFileInputProps,
  Label as FlowbiteLabel,
  LabelProps as FlowbiteLabelProps,
  Radio as FlowbiteRadio,
  RadioProps as FlowbiteRadioProps,
  Select as FlowbiteSelect,
  SelectProps as FlowbiteSelectProps,
  Textarea as FlowbiteTextarea,
  TextareaProps as FlowbiteTextareaProps,
  TextInput as FlowbiteTextInput,
  TextInputProps as FlowbiteTextInputProps,
  ToggleSwitch as FlowbiteToggleSwitch,
  ToggleSwitchProps as FlowbiteToggleSwitchProps,
} from 'flowbite-react';

type FormProps = PropsWithChildren<ComponentProps<'form'>>;

const Form = forwardRef((
  {
    children,
    ...otherProps
  }: FormProps,
  ref: LegacyRef<HTMLFormElement>,
) => {
  return (
    <form
      ref={ref}
      {...otherProps}
    >
      {children}
    </form>
  );
});

type CheckboxProps = {} & FlowbiteCheckboxProps;

const Checkbox = forwardRef((
  {
    ...otherProps
  }: CheckboxProps,
  ref: Ref<HTMLInputElement>,
) => {
  return (
    <FlowbiteCheckbox
      ref={ref}
      {...otherProps}
    />
  );
});

type FileInputProps = {} & FlowbiteFileInputProps;

const FileInput = forwardRef((
  {
    color = "gray",
    helperText,
    sizing = "md",
    ...otherProps
  }: FileInputProps,
  ref: Ref<HTMLInputElement>,
) => {
  return (
    <FlowbiteFileInput
      color={color}
      helperText={helperText}
      ref={ref}
      sizing={sizing}
      {...otherProps}
    />
  );
});

type LabelProps = {} & FlowbiteLabelProps;

const Label = forwardRef((
  {
    children,
    color = 'default',
    disabled = false,
    value,
    ...otherProps
  }: LabelProps,
  ref: LegacyRef<HTMLLabelElement>,
) => {
  return (
    <FlowbiteLabel
      color={color}
      disabled={disabled}
      ref={ref}
      value={value}
      {...otherProps}
    >
      {children}
    </FlowbiteLabel>
  );
});

type RadioProps = {} & FlowbiteRadioProps;

const Radio = forwardRef((
  {
    ...otherProps
  }: RadioProps,
  ref: Ref<HTMLInputElement>,
) => {
  return (
    <FlowbiteRadio
      ref={ref}
      {...otherProps}
    />
  );
});

type SelectProps = {} & FlowbiteSelectProps;

const Select = forwardRef((
  {
    addon,
    children,
    color = 'gray',
    helperText,
    icon,
    shadow = false,
    sizing = 'md',
    ...otherProps
  }: SelectProps,
  ref: Ref<HTMLSelectElement>,
) => {
  return (
    <FlowbiteSelect
      addon={addon}
      color={color}
      helperText={helperText}
      icon={icon}
      ref={ref}
      shadow={shadow}
      sizing={sizing}
      {...otherProps}
    >
      {children}
    </FlowbiteSelect>
  );
});

type TextareaProps = {} & FlowbiteTextareaProps;

const Textarea = forwardRef((
  {
    color = 'gray',
    helperText,
    shadow = false,
    ...otherProps
  }: TextareaProps,
  ref: Ref<HTMLTextAreaElement>,
) => {
  return (
    <FlowbiteTextarea
      color={color}
      helperText={helperText}
      ref={ref}
      shadow={shadow}
      {...otherProps}
    />
  );
});

type TextInputProps = {} & FlowbiteTextInputProps;

const TextInput = forwardRef((
  {
    addon,
    color = 'gray',
    helperText,
    icon,
    shadow = false,
    sizing = 'md',
    ...otherProps
  }: TextInputProps,
  ref: Ref<HTMLInputElement>,
) => {
  return (
    <FlowbiteTextInput
      addon={addon}
      color={color}
      helperText={helperText}
      icon={icon}
      ref={ref}
      shadow={shadow}
      sizing={sizing}
      {...otherProps}
    />
  );
});

type ToggleSwitchProps = {} & FlowbiteToggleSwitchProps;

const ToggleSwitch = forwardRef((
  {
    checked = false,
    label,
    onChange,
    ...otherProps
  }: ToggleSwitchProps,
  ref: Ref<HTMLButtonElement>,
) => {
  return (
    <FlowbiteToggleSwitch
      checked={checked}
      label={label}
      onChange={onChange}
      ref={ref}
      {...otherProps}
    />
  );
});

export default Object.assign(Form, {
  Checkbox,
  FileInput,
  Label,
  Radio,
  Select,
  Textarea,
  TextInput,
  ToggleSwitch,
});

結果

変化はないので、前と同じ。

はい、できました。