【问题标题】:Typescript literal type union issue when type become more specific in react children当类型在反应儿童中变得更加具体时,打字稿文字类型联合问题
【发布时间】:2019-04-29 14:21:06
【问题描述】:

我正在努力将我们的 React/redux 项目转换为 TypeScript,并且遇到了 TypeScript 文字类型联合类型的问题。

问题来了:

我用 type 属性(一个字符串)实例化一个 Wrapper 组件:

//index.tsx
import * as React from "react";
import { render } from "react-dom";

import "./styles.css";

import Wrapper from "./wrapper";

function App() {
  return <Wrapper type="password" />;
}

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

我在这个 Wrapper 组件中定义了一个字符串 type 值的“列表”应该适合。为此,我使用 TypeScript Union 类型:type: "value1" | value2 | ...

//Wrapper.tsx

import * as React from "react";
import Input from "./input";
import Select from "./select";

interface ILabeledInput {
  type: "textarea" | "password" | "select" | "search" | "email" | "text";
}

export default function Wrapper(props: ILabeledInput) {
  switch (props.type) {
    case "select":
      return <Select {...props} />;

    case "password":
      return <Input {...props} />;
  }
}

这里我使用一个开关来实例化适当的子组件,而不管我在 index.tsx

中定义的类型

在子组件中,我也使用 typescript 接口来定义 type 属性类型。 使用此开关:

-type的类型可能只是一个字符串(只有一种可能性):

// select.tsx
import * as React from "react";

interface ISelect {
  type: "select";
}

export default function Input(props: ISelect) {
  return <p>{props.type}</p>; // fake code, juste to display something
}

-或者再次是 TypeScript Union 类型的字符串(但可能性比原始字符串小):

// input.tsx
import * as React from "react";

interface IInput {
  type: "password" | "search" | "email" | "text";
}

export default function Input(props: IInput) {
  return <p>{props.type}</p>; // fake code, juste to display something
}

type 的类型从 wrapper 更改为 select/input 组件会产生 TypeScript 错误:

Type '{ type: "textarea" | "password" | "select" | "search" | "email" | "text"; }' is not assignable to type 'ISelect'.
  Types of property 'type' are incompatible.
    Type '"textarea" | "password" | "select" | "search" | "email" | "text"' is not assignable to type '"select"'.
      Type '"textarea"' is not assignable to type '"select"'.ts(2322)

Type '{ type: "textarea" | "password" | "select" | "search" | "email" | "text"; }' is not assignable to type 'IInput'.
  Types of property 'type' are incompatible.
    Type '"textarea" | "password" | "select" | "search" | "email" | "text"' is not assignable to type '"password" | "search" | "email" | "text"'.
      Type '"textarea"' is not assignable to type '"password" | "search" | "email" | "text"'.ts(2322)

我知道 Typescript 检测到道具类型的变化,但我找不到如何让它考虑 Switch 条件。

提前感谢您的帮助!!

【问题讨论】:

    标签: javascript reactjs typescript


    【解决方案1】:

    您可以执行以下操作:

    export default function Wrapper(props: ILabeledInput) {
        const {type, ...restProps} = props;
      switch (type) {
        case "select":
          return <Select {type: 'select', ...restProps} />;
    
        case "password":
          return <Input {type: 'password', ...restProps} />;
      }
    }
    

    但是,由于您将props 声明为ILabeledInput 类型,根据您的类型定义,除了type 之外,不能有任何其他属性。

    【讨论】:

      【解决方案2】:

      假设我已经正确理解它并且它对我来说正常工作,我试图让你的例子更简洁。这是否解决了您的问题?

      type a = 'a';
      type b = 'b';
      type input = 'a' | 'b';
      
      function takeA(a: a) { }
      
      function takeB(b: b) { }
      
      function takeBoth(input: input) {
        switch (input) {
          case 'a':
            return takeA(input);
          case 'b':
            return takeB(input);
        }
      }
      

      【讨论】:

        【解决方案3】:

        非常感谢各位! 我花了我一个下午的时间,终于找到了解决这个问题的方法,它与您的解决方案 alex 相对应。

        我已经开始像你一样解构道具了:

        
        import Input from "./input";
        import Select from "./select";
        interface ILabeledInput {
          type: "textarea" | "password" | "select" | "search" | "email" | "text";
        }
        
        export default function Wrapper(props: ILabeledInput) {
          const { type, ...props } = props;
          switch (props.type) {
            case "select":
              return <Select {...props} type={type} />;
        
            case "password":
              return <Input {...props} type={type} />;
          }
        }
        

        但将 props.type 作为 switch 的参数仍然会产生问题。所以我决定发布这个主题。

        我终于可以使用这些解决方案了:

        import * as React from "react";
        
        import Input from "./input";
        import Select from "./select";
        interface ILabeledInput {
          type: "textarea" | "password" | "select" | "search" | "email" | "text";
        }
        
        export default function Wrapper(props: ILabeledInput) {
          switch (props.type) {
            case "select":
              return <Select {...props} type={props.type} />;
        
            case "password":
              return <Input {...props} type={props.type} />;
          }
        }
        

        import * as React from "react";
        
        import Input from "./input";
        import Select from "./select";
        interface ILabeledInput {
          type: "textarea" | "password" | "select" | "search" | "email" | "text";
        }
        
        export default function Wrapper(props: ILabeledInput) {
          const { type, ...rprops } = props;
          switch (type) {
            case "select":
              return <Select {...rprops} type={type} />;
        
            case "password":
              return <Input {...rprops} type={type} />;
          }
        }
        

        似乎 switch 参数应该与传递给子组件的一次相同

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-08-05
          • 2018-01-13
          • 1970-01-01
          • 2023-01-10
          • 1970-01-01
          • 2017-08-03
          • 2017-05-18
          相关资源
          最近更新 更多