从 React 组件中导出 TypeScript 类型的字符串属性值?

Posted

技术标签:

【中文标题】从 React 组件中导出 TypeScript 类型的字符串属性值?【英文标题】:Export TypeScript typed string prop values from a React component? 【发布时间】:2020-09-11 22:52:40 【问题描述】:

我有一个 React / TypeScript 组件。在 Button.tsx 中:

type Props = 
  type: 
    | "primary"
    | "secondary"
    | "tertiary"


const Button = React.FC<Props> = ( type ) => 
    const color = (() => 
        switch (type) 
          case "primary":
            return 'red';
          case "secondary":
            return 'blue';
          case "tertiary":
            return 'green';
          default:
            throw new Error("A backgroundColor condition was missed");
        
    )();

    return(
        <button style= background: color >Im a button</button>
    )

我可以在其他组件中使用它。在 Page.tsx 中:

const Page = () => 
    return(
        <div>
            <h1>Heading</h1>
            <Button type="primary" />
        </div>
    )

在 Storybook 中,我需要使用所有类型值。在 Button.stories.js 中:

const types = [
  "primary",
  "secondary",
  "tertiary",
];

export const AllButtons = () => 
    return(
        types.map(type=>
            <Button type=type key=type />
        )
    )

不必重复“主要”、“次要”、“第三级”,有没有办法可以从 Button.tsx 导出它们?这样,如果添加了新类型,Storybook 文件将自动拥有它。

我可以在 Button.tsx 中使用枚举:

export enum Types 
  primary = "primary",
  secondary = "secondary",
  tertiary = "tertiary",


type Props = 
  type: Types;
;

但是,使用 Button 的组件不能只传递一个字符串,每次使用 Button 时都必须导入枚举,这是不值得的。在 Page.tsx 中:

import  Type  from './Button'

const Page = () => 
    return(
        <div>
            <h1>Heading</h1>
            <Button type=Type.primary />
        </div>
    )

【问题讨论】:

你绝对可以做这样的事情。看看这个答案 - ***.com/a/43101184/13058340 【参考方案1】:

在 TypeScript 中,您可以从值中获取类型(使用 typeof),但您永远无法从类型中获取值。所以如果你想消除重复,你需要使用一个值作为你的真实来源,并从中派生出类型。

例如,如果您将按钮类型数组作为事实来源,那么您可以使用const assertion (as const) 从中派生类型:

// Button.tsx
export const BUTTON_TYPES = [
    "primary",
    "secondary",
    "tertiary",
] as const;

type Types = typeof BUTTON_TYPES[number];

type Props = 
    type: Types;


const Button: React.FC<Props> = ( type ) => 
    // ...

    return(
        <button style= background: color >Im a button</button>
    )

然后您可以在您的故事中导入BUTTON_TYPES 并对其进行迭代。

您还可以制作从按钮类型到颜色的映射,并将其用作您的真实来源。这可以让您从组件中消除 color 函数:

const TYPE_TO_COLOR = 
  primary: 'red',
  secondary: 'blue',
  tertiary: 'green',
 as const;

type Types = keyof typeof TYPE_TO_COLOR;
// type Types = "primary" | "secondary" | "tertiary"
export const BUTTON_TYPES = Object.keys(TYPE_TO_COLOR);

type Props = 
  type: Types;


const Button: React.FC<Props> = ( type ) => 
  const color = TYPE_TO_COLOR[type];
  if (!color) 
    throw new Error("A backgroundColor condition was missed");
  

  return (
    <button style= background: color >Im a button</button>
  );

【讨论】:

【参考方案2】:

您可以生成声明对象并从中声明类型。这样,您将能够遍历其键,并且每次更改时类型都是最新的。

Button.tsx

import * as React from "react";

export const ButtonSkins = 
  primary: "primary",
  secondary: "secondary",
  tertiary: "tertiary"
;

export type ButtonSkin = keyof typeof ButtonSkins;
export type ButtonProps = 
  skin: ButtonSkin;
;

export const Button: React.FC<ButtonProps> = ( skin ) => (
  <button className=skin>skin</button>
);

App.tsx(我把循环放在这里,但你当然可以在故事书中使用它)

import * as React from "react";
import  render  from "react-dom";
import  Button, ButtonSkins, ButtonSkin  from "./Button";

const App = () => (
  <div>
    (Object.keys(ButtonSkins) as Array<ButtonSkin>).map(skin => 
      return <Button skin=skin />;
    )
    <h2>Start editing to see some magic happen "\u2728"</h2>
  </div>
);

render(<App />, document.getElementById("root"));

https://codesandbox.io/s/recursing-browser-qkgzb?file=/src/index.tsx

【讨论】:

以上是关于从 React 组件中导出 TypeScript 类型的字符串属性值?的主要内容,如果未能解决你的问题,请参考以下文章

在 React TypeScript 组件中导入 Javascript

React Redux Firebase - 您可能忘记从定义组件的文件中导出组件,或者您可能混淆了默认导入和命名导入

在 react-typescript 中导入 html 文件

您可能忘记从其定义的文件中导出组件,或者您可能混淆了默认导入和命名导入

如何在 React 应用程序的 typescript 文件中导入 js 模块(使用绝对路径)?

为啥在另一个 Typescript 项目中导入时没有得到导出的对象?