javascript - Typescript:函数参数是两个接口(interface)之一

标签 javascript typescript

我正在查看this question ,我认为这与我的问题有关。但是,它与我的用例有点不同。

我有一个名为 parseScanResults 的函数,它接受一个对象参数。该对象可以是两种类型之一。但是, typescript 使用以下代码抛出错误:

const ScanForm: React.FC<IScanFormProps> = ({ children, onSubmit, parseScanResults }) => {
  const [scannerActive, toggleScannerActive] = useState(false);

  const closeScanner = (): void => {
    toggleScannerActive(false);
  };

  const handleScanResults = (results: IVoucherScanResults | IBlinkCardScanResults): void => {
    const { cardString, stringMonth, stringYear } = parseScanResults(results);

    setValue('cardNumber', cardString);
    setValue('expMonth', stringMonth);
    setValue('expYear', stringYear);

    toggleScannerActive(false);
  };

  return (
    <Form onSubmit={handleSubmit(onSubmit)}>
      {children({ scannerActive, closeScanner, handleScanResults })}
    </Form>
  );
};

import CreditCardBarcodeScanner from 'src/components/scanners/credit_card_barcode_scanner';

import { IVoucherScanResults, IScannerProps, IParsedScanResults } from '../scanners/card_scanners';
import ScanForm from './scan-form';

function CreditCardBarcodeForm(): JSX.Element {
  const onSubmit = (data: { expMonth: string; expYear: string; securityCode: string; cardNumber: string }): void => {
    // Do something with form data
    console.log(data);
  };

  const parseScanResults = (results: IVoucherScanResults): IParsedScanResults => {
    const { text } = results;

    const [cardString, expirationString] = text.slice().split('/');
    const stringMonth = expirationString.slice(0, 2);
    const stringYear = expirationString.slice(2, 4);

    return { cardString, stringMonth, stringYear };
  };

  return (
    <ScanForm onSubmit={onSubmit} parseScanResults={parseScanResults}>
      {({ scannerActive, closeScanner, handleScanResults }: IScannerProps) => (
        <CreditCardBarcodeScanner
          scannerActive={scannerActive}
          closeScanner={closeScanner}
          handleScanResults={handleScanResults}
        />
      )}
    </ScanForm>
  );
}

export default CreditCardBarcodeForm;

export interface IBlinkCardScanResults {
  cardNumber: string;
  cvv: string;
  expiryDate: {
    day?: number;
    empty?: boolean;
    month: number;
    originalString?: string;
    successfullyParsed?: boolean;
    year: number;
  };
}

export interface IVoucherScanResults {
  text: string;
  timestamp: number;
  format: number;
  numBits: number;
}

export interface IParsedScanResults {
  cardString: string;
  stringMonth: string;
  stringYear: string;
}

export interface IScannerProps {
  scannerActive: boolean;
  closeScanner: () => void;
  handleScanResults: (results: IVoucherScanResults | IBlinkCardScanResults) => void;
}

export interface IScanFormProps {
  children: (props: ICardScannerProps) => React.ReactElement;
  onSubmit: (data: { expMonth: string; expYear: string; securityCode: string; cardNumber: string }) => void;
  parseScanResults: (results: IBlinkCardScanResults | IVoucherScanResults) => IParsedScanResults;
}

enter image description here

错误状态:

Type '(results: IVoucherScanResults) => IParsedScanResults' is not assignable to type '(results: IBlinkCardScanResults | IVoucherScanResults) => IParsedScanResults'.
  Types of parameters 'results' and 'results' are incompatible.
    Type 'IBlinkCardScanResults | IVoucherScanResults' is not assignable to type 'IVoucherScanResults'.
      Type 'IBlinkCardScanResults' is missing the following properties from type 'IVoucherScanResults': text, timestamp, format, numBitsts(2322)

最佳答案

你的问题是parseScanUtils是一个函数,它得到 IVoucherScanResults作为参数,或函数,获取 IBlinkCardScanResults作为参数,而只有一个为真。在这种情况下,您的组件似乎正在接收两个中的第一个。

主要的一点是,拥有一个函数的联合(其中每个函数都获取特定的参数类型)和拥有一个其参数是两种类型的联合的函数之间存在差异。

  parseScanResults:
    ((results: IBlinkCardScanResults) => IParsedScanResults)
    | ((results: IVoucherScanResults) => IParsedScanResults);

对比

parseScanResults:
    ((results: IBlinkCardScanResults | IVoucherScanResults) => IParsedScanResults)

编辑

您可以做的是使用泛型,而不是键入组件函数,您可以显式键入参数:

让我们首先使界面通用:

export interface IScanFormProps<T extends IBlinkCardScanResults | IVoucherScanResults> {
  children: (props: ICardScannerProps) => React.ReactElement;
  onSubmit: (data: { expMonth: string; expYear: string; securityCode: string; cardNumber: string }) => void;
  parseScanResults: (results: T) => IParsedScanResults;
}

您可以像这样更新您的功能组件:

const ScanForm = <T extends IBlinkCardScanResults | IVoucherScanResults>({ children, onSubmit, parseScanResults }: T) => {

还有你的handleScanResults功能:

const handleScanResults = (results: T): void => {
  ...rest of code...
}

然后剩下要做的就是调用具有所需类型的组件(例如 IBlinkCardScanResults ):

<ScanForm<IBlinkCardScanResults> onSubmit={onSubmit} parseScanResults={parseScanResults}>

我相信现在应该可以了

关于javascript - Typescript:函数参数是两个接口(interface)之一,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68024197/

相关文章:

reactjs - create-react-app typescript 不会运行 npm start

typescript - 将泛型类型限制为 Typescript 中的几个类之一

Visual Studio 2013 update 3 中并排使用 typescript/jscript

javascript - MVC 根据之前的下拉菜单过滤下拉菜单,重用代码

javascript - 由于 Observable 订阅发生在组件上,因此我很难将逻辑重构到 Angular 2 应用程序的服务层中

php - 如何在 codeigniter 中的不同页面上显示多个 View

javascript - 在通过meteor的publish方法提供数据之前添加来自另一个mongodb集合的信息

javascript - 使用 CSS 将 Muses 广播播放器居中

javascript - React Styleguidist 开始懒散

javascript - 什么是public app和express.Application