当类型在react子代中变得更加具体时,Typescript文字类型联合问题

时间:2019-04-29 14:21:06

标签: javascript reactjs typescript

我正在将我们的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 的类型从包装器更改为选择/输入组件会产生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检测到道具类型发生了变化,但是我找不到如何考虑开关条件的方法。

提前感谢您的帮助!

3 个答案:

答案 0 :(得分:0)

假设我已经正确理解了它,并且我认为它可以正常工作,我试图使您的示例更简洁。这可以解决您的问题吗?

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);
  }
}

答案 1 :(得分:0)

您可以执行以下操作:

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 :(得分:0)

非常感谢你们! 我花了整整一个下午的时间,终于找到了解决方法,它与您的解决方案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用作开关的参数仍然会产生问题。所以我决定发表这个话题。

我终于可以在以下解决方案中使用它了:

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参数应该与传递给次部件的

相同。