Reputation: 754
interface PageProps {
foo?: Function;
bar: number;
}
export class PageComponent extends React.Component<PageProps, {}> {
public static defaultProps: Partial<PageProps> = {
foo: () => alert('Did foo')
};
private doFoo() {
this.props.foo(); // Typescript Error: Object is possibly 'undefined'
}
public render(): JSX.Element {
return (
<div>
<span>Hello, world! The number is {this.props.bar}</span>
<button onClick={() => this.doFoo()}>Do the Foo</button>
</div>
);
}
}
Is there a way to tell Typescript that props.foo
will always be defined?
There is a very good SO question and answer that discusses how to properly define the types for props on a component. It even discusses how you would let TS know about defaultProps
on a stateless component.
However Typescript will still complain inside a regular component definition that your props might be undefined (as illustrated in my example above).
You can call it using a bang (this.props.foo!()
) and that prevents TS from throwing an error, but it also prevents any kind of type checking on whatever you've passed in. In the example above, not a big deal. In more complex code, it has already bitten me.
So I am currently implementing things like this instead:
private doFoo() {
if (this.props.foo) this.props.foo();
}
That has one drawback, which I think is rather important: It makes my code a little confusing. In reality, I always want to call this.props.foo()
, but it seems like, without investigating the class more closely, there are cases where nothing will happen inside the doFoo
method. (Yes I could add a comment explaining that the if
is only for Typescript to be happy, but I'd rather my code speak for itself when it can.)
My code becomes even more unnecessarily complex when I actually need to return something from this.props.foo()
.
I am currently using @types/react for my type definitions.
Upvotes: 8
Views: 5217
Reputation: 71891
So, the compiler is saying that the object is possibly undefined
. You know better that it's not, so you just want to write this.props.foo()
. The right way to do it is using the null assertion type operator
this.props.foo!()
If you say that this has bit you before, then that's your own fault for not checking it :), which means the compiler was -right- that the object could have been undefined
. And if there is a possibility it could be undefined
, you should always check it. Perhaps you like a more shorter version of the if
statement? It's one character less:
private doFoo() {
this.props.foo && this.props.foo();
}
or with the typescript >= 3.8:
private doFoo() {
this.props.foo?.();
}
On the other hand you can make an extra interface which extends
the PageProps
. For the fun of it, let's call it PagePropsFilled
:
interface PagePropsFilled extends PageProps {
foo: Function;
}
You can now set your this.props
to PagePropsFilled
, and it won't show you an error anymore
Upvotes: 7