首页 > 解决方案 > Typescript:作为两个接口之一的函数参数

问题描述

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

我有一个名为的函数parseScanResults接受一个作为对象的参数。对象可以是两种类型之一。但是,打字稿使用以下代码引发错误:

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

在此处输入图像描述

错误指出:

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)

标签: javascripttypescript

解决方案


您的问题是,要么parseScanUtils是一个以 a作为参数的函数,要么是一个以 a 作为参数的函数,而只有一个是正确的。在这种情况下,您的组件似乎正在接收两者中的第一个。IVoucherScanResultsIBlinkCardScanResults

要点是,拥有一个函数联合(其中每个函数都有一个特定的参数类型)和一个函数的参数是两种类型的联合之间是有区别的。

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

我相信它现在应该可以工作


推荐阅读