I have a component that accepts a generic type parameter 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> {
...
}
When I use this component, I would expect it to prevent me from passing in unrecognized props, for example:
render() {
return (
<Form onSubmit={this.save}>
<Field foo="test" label="email" name="email" type="email" component={TextField} />
</Form>
)
}
Surprisingly, the above code compiles without even any warnings, despite the fact that the foo
prop is not defined anywhere. I tried simplifying the example and got the same result:
class Field<TChildProps = {}> extends React.PureComponent<FieldProps & TChildProps> {
...
}
// this still compiles without errors
<Field foo="test" label="email" name="email" type="email" component={TextField} />
Is TypeScript functioning the way it's supposed to here according to the type definition for React.PureComponent
(by the way I tested it on React.Component
and got the same result), or is this a bug?
Original Answer
Because <TChildProps = {}>
means that the default value of TChildProps
is {}
if the consumer does not assigns any value to TChilProps
. So TChildProps
can actually be anything if the consumer decides to pass value to it. That's why typescript allows any props passed to Field
. What you want to do is to enforce the type of TChildProps
using extends
keyword. So try class Field<TChildProps extends GenericFieldHTMLAttributes>
instead. As a result, the consumer needs to pass TChildProps
as GenericFieldHTMLAttributes
type. You can find more information on https://www.typescriptlang.org/docs/handbook/generics.html .
UPDATE
Here's the simplified situation ( UPDATE2 : I tend to think this is either a bug or a missing feature, here's why)
This Animal
class below accepting food as generics reflects React.Component
class that accepts props as generics.
class Animal<F> {
eat(food: F) {
return;
}
}
And the Lion
class below extending Animal
reflects your Field
class which extends React.Component
class Lion<F = {pizza: string}> extends Animal<F> {
}
And when the consumers consume it, if they do not specify what food a lion should eat, the lion can eat anything like so we should expect the lion to eat only pizza
const lion1 = new Lion();
lion1.eat(""); // Argument of type '""' is not assignable to parameter of type '{pizza: string}'
However this type check does not happen in case of tsx.
The workaround is to "alias both the type and interface to a non-generic specialization."
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
These two links should give you more info on how to use generic components as of now.
https://github.com/Microsoft/TypeScript/issues/3960 https://basarat.gitbooks.io/typescript/docs/types/generics.html#generics-in-tsx
And apparently they working on this.
This should be possible in Typescript 2.9 as seen in https://github.com/Microsoft/TypeScript/pull/22415 .
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.