【问题标题】:How to exclude a key from an interface in TypeScript如何从 TypeScript 中的接口中排除键
【发布时间】:2017-12-12 12:09:24
【问题描述】:

在 TypeScript 中,你可以像这样组合两种接口类型

interface Foo {
    var1: string
}

interface Bar {
    var2: string
}

type Combined = Foo & Bar

我不想组合键,而是想将键从一个接口排除到另一个接口。无论如何你可以在 TypeScript 中做到这一点吗?

原因是,我有一个 HOC,它为像这样的其他包装组件管理属性值

export default function valueHOC<P> (
  Comp: React.ComponentClass<P> | React.StatelessComponent<P>
): React.ComponentClass<P> {
  return class WrappedComponent extends React.Component<P, State> {
    render () {
      return (
        <Comp
          {...this.props}
          value={this.state.value}
        />
      )
    }
}

这样我就可以写了

const ValuedComponent = valueHOC(MyComponent)

然后

<ValuedComponent />

但问题是,返回的组件类型也使用了给定组件的 props 类型,所以 TypeScript 会抱怨并要求我提供 value prop。结果,我将不得不写类似的东西

<ValuedComponent value="foo" />

无论如何都不会使用该值。我这里想要的是返回一个没有特定键的界面,我想要这样的东西

React.ComponentClass<P - {value: string}>

那么在返回的组件中将不需要value。现在可以在 TypeScript 中使用吗?

【问题讨论】:

  • 您可以查看Pick&lt;T, K keyof T&gt;。但是你不能动态地挑选东西。

标签: typescript


【解决方案1】:

在 TypeScript 2.8 中,您现在可以执行以下操作:

interface Foo {
    attribute1: string;
    optional2?: string;
    excludePlease: string;
}

// Typescript 3.5+ defines Omit for you.
// In earlier Typescript versions define Omit:
// type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>

// Use Omit to exclude one or more fields (use "excludePlease"|"field2"|"field3" etc to exclude multiple)
type Bar = Omit<Foo, "excludePlease">
const b: Bar = {
    attribute1: ''
};

因此,关于您的问题,以下可能是您想要的:

export default function valueHOC<P> (
  Comp: React.ComponentClass<P> | React.StatelessComponent<P>
): React.ComponentClass<Omit<P, "value">> {
  return class WrappedComponent extends React.Component<Omit<P, "value">, State> {
    render () {
      return (
        <Comp
          {...this.props}
          value={this.state.value}
        />
      )
    }
}

【讨论】:

  • 虽然这可行,但它会要求可选参数。你可以这样做来防止:stackoverflow.com/a/48216010/616589
  • 我不质疑答案的准确性,但这是实现简单目标的荒谬方式 - 任何阅读它的人都需要一些不平凡的时间才能解决到底是怎么回事。打字稿团队为什么要这样做有什么逻辑吗?
  • @havlock 抱歉,我最近对其进行了编辑以简化和改进答案,但我忘记在简化示例中也使用简化的 Omit 代码。现在好点了吗?
  • @AJP 我在抱怨打字稿,而不是你的答案,但是谢谢 - 是的,这稍微简单一些。他们让我们跳过如此荒谬的语法来实现简单的东西仍然绝对是疯狂的——如果他们真的认为这种事情是可以的,我担心这种语言。
  • @havlock :) 看看其他类型语言的例子会很有趣,这些例子可以更好地处理 :)
【解决方案2】:
interface MyDialogProps extends Omit<DialogProps, 'onClose'> {
  id: string;
  onClose: (reason: string) => void;
}

export const MyDialog: React.FC<MyDialogProps> = ({ id, onClose, ...props) => (
  <Dialog onClose={() => onClose("whatever")} {...props} />
);

【讨论】:

  • 非常感谢您的建议!
【解决方案3】:

utility-types 库具有Subtract 映射类型:

import { Subtract } from 'utility-types';

type Props = { name: string; age: number; visible: boolean };
type DefaultProps = { age: number };

type RequiredProps = Subtract<Props, DefaultProps>;
// Expect: { name: string; visible: boolean; }

【讨论】:

    【解决方案4】:

    您无法从现有接口中删除属性。即使尝试使用将value 属性设置为可选的接口来扩展现有接口,也会返回错误。

    为避免此问题,请修改目标组件的类型,以便 value 属性是可选的。

    例如

    // ...
    class MyComponent extends React.Component<{value?: string}, State> {
    // ...
    }
    

    然后是使用高阶函数时产生的组件

    const valuedComponent = valueHOC(MyComponent);
    

    不会要求 value 道具。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-30
      • 2019-01-20
      相关资源
      最近更新 更多