【问题标题】:Can I use conditional generic to set a callback return type in typescript?我可以使用条件泛型在打字稿中设置回调返回类型吗?
【发布时间】:2021-03-12 22:50:17
【问题描述】:

我有一个反应组件,我正在向它传递一个泛型。基于这个泛型,我想更改回调负载类型。

在下面的示例中,我传递的 RelationType 可以是“one”或“many”,基于此,回调应该是字符串或字符串数​​组。

import React from 'react';

export enum RelationType {
  one = 'one',
  many = 'many',
}

interface Props<R extends RelationType> {
  callback(newValue?: R extends RelationType.one ? string : string[]): void;
  relationType: R;
}

export const Component = <R extends RelationType>({ onUpdate, relationType }: Props<R>) => {
  return (
    <button onClick={() => {
      if (relationType === RelationType.one) {
        callback('foo'); // TS2345: Argument of type '"foo"' is not assignable to parameter of type '(R extends RelationType.one ? string : string[]) | undefined'.
      } else {
        callback(['foo', 'bar']); // TS2345: Argument of type 'string[]' is not assignable to parameter of type 'R extends RelationType.one ? string : string[]'.
      }
    }}/>
  )
};

Typescript 可以做到这一点吗?

【问题讨论】:

    标签: reactjs typescript


    【解决方案1】:

    Props 类型中的泛型类型参数RComponent 函数对您没有帮助。在Component() 内部,您试图检查relationType 以缩小callback,但是像R 这样的泛型类型参数永远不会通过TypeScript 中的控制流分析来缩小范围。请参阅microsoft/TypeScript#24085 了解更多信息。

    最好只使用discriminated union 类型的非泛型Props,相当于您原来的Props&lt;RelationType.one&gt; | Props&lt;RelationType.many&gt;

    type Props = { [R in RelationType]: {
      callback(newValue?: R extends RelationType.one ? string : string[]): void;
      relationType: R;
    } }[RelationType];
    
    /* type Props = {
        callback(newValue?: string | undefined): void;
        relationType: RelationType.one;
    } | {
        callback(newValue?: string[] | undefined): void;
        relationType: RelationType.many;
    } */
    

    在上面我已经让编译器通过mapping你原来的Props&lt;R&gt;定义在RelationType上以编程方式计算该联合,以形成一个对象类型,其属性我立即look up


    那么您的Component() 函数可以使用Props 类型的参数。另一个警告是,如果您希望编译器跟踪两个值之间的关系,则不能在实现签名中将其解构为 relationTypecallback。 TypeScript 不支持我一直在调用的相关联合类型(请参阅microsoft/TypeScript#30581)。如果您想要您正在寻找的行为,您需要将这两个值保留为单个 props 参数的属性:

    const Component = (props: Props) => {
      return (
        <button onClick={() => {
          if (props.relationType === RelationType.one) {
            props.callback('foo');
          } else {
            props.callback(['foo', 'bar']);
          }
        }} />
      )
    };
    

    我认为这可以按预期工作。

    Playground link to code

    【讨论】:

    • 太棒了,谢谢!我实际上尝试了区分联合类型,但遇到了破坏警告。非常感谢您的帮助! ?
    猜你喜欢
    • 2021-10-04
    • 2020-06-29
    • 2021-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-07-07
    • 2021-09-17
    • 1970-01-01
    相关资源
    最近更新 更多