TypeScript不强制执行泛型交集类型(React组件道具)

时间:2018-02-28 21:19:13

标签: reactjs typescript

我有一个接受泛型类型参数TChildProps的组件:

type GenericFieldHTMLAttributes =
    | InputHTMLAttributes<HTMLInputElement>
    | SelectHTMLAttributes<HTMLSelectElement>
    | TextareaHTMLAttributes<HTMLTextAreaElement>

interface FieldProps {
    name: string
    label?: string | React.ReactNode
    component?: string | React.ComponentType
    initialValue?: any
    children?: React.ReactNode | FieldRenderFunction
}

class Field<TChildProps = GenericFieldHTMLAttributes> extends React.PureComponent<FieldProps & TChildProps> {
    ...
}

当我使用这个组件时,我希望它能阻止我传入无法识别的道具,例如:

render() {
    return (
        <Form onSubmit={this.save}>
            <Field foo="test" label="email" name="email" type="email" component={TextField} />
        </Form>
    )
}

令人惊讶的是,尽管foo道具未在任何地方定义,但上述代码甚至没有任何警告编译。我尝试简化示例并获得相同的结果:

class Field<TChildProps = {}> extends React.PureComponent<FieldProps & TChildProps> {
    ...
}

// this still compiles without errors
<Field foo="test" label="email" name="email" type="email" component={TextField} />

根据React.PureComponent的类型定义(我在React.Component上测试并获得相同的结果),TypeScript的运作方式与此相符,或者这是一个错误?

1 个答案:

答案 0 :(得分:1)

原始答案

如果消费者未向<TChildProps = {}>分配任何值,则TChildProps表示默认值{}TChilProps。因此,如果消费者决定将价值传递给它,TChildProps实际上可以是任何东西。这就是为什么typescript允许任何道具传递给Field的原因。您要做的是使用TChildProps关键字强制执行extends类型。所以请尝试使用class Field<TChildProps extends GenericFieldHTMLAttributes>。因此,消费者需要将TChildProps作为GenericFieldHTMLAttributes类型传递。您可以在https://www.typescriptlang.org/docs/handbook/generics.html找到更多信息。

<强>更新

以下是简化情况( UPDATE2 :我倾向于认为这是一个错误或缺少的功能,这就是原因)

以下Animal课程接受食物作为仿制品反映了React.Component课程,它接受道具作为仿制品。

class Animal<F> {
  eat(food: F) {
    return;
  }
}

以下Lion类扩展Animal反映了Field类,其中React.Component

class Lion<F = {pizza: string}> extends Animal<F> {

}

当消费者食用它时,如果他们没有说明狮子应该吃什么食物,那么狮子可以吃任何类似食物我们应该期待狮子只吃披萨

const lion1 = new Lion();
lion1.eat(""); // Argument of  type '""' is not assignable to parameter of type '{pizza: string}'

但是在tsx的情况下不会发生这种类型的检查。

解决方法是“将类型和接口别名为非泛型专业化。”

const TextField = Field as { new (): Field<{value: string}>};

<TextField foo="test" label="email" name="email" type="email" value="asdf"/> // error: Property 'foo' does not exist

这两个链接应该为您提供有关如何使用通用组件的更多信息。

https://github.com/Microsoft/TypeScript/issues/3960 https://basarat.gitbooks.io/typescript/docs/types/generics.html#generics-in-tsx

显然他们working on这个。

2018年4月更新

这应该可以在Typescript 2.9中实现,如https://github.com/Microsoft/TypeScript/pull/22415中所示。