Reputation: 19242
Let's say I have this component class:
interface SomeComponentProps {
someOptionalProperty?: string;
}
interface SomeComponentState { /* ... */ }
class SomeComponent extends React.Component<SomeComponentProps, SomeComponentState> {
static defaultProps = {
someOptionalProp = 'some default value',
}
method() {
const shouldNotBeOptional = this.props.someOptionalProp;
}
}
How can I assert that this.props.someOptionalProp
is not undefined within the class but is also optional when trying to using the component?
What I've been doing is just ignoring defaultProps
altogether by creating getters that
return `this.props.someOptionalProp || 'the default value'`;
but I'd like to use defaultProps
because my team uses it.
Upvotes: 2
Views: 1994
Reputation: 482
This issue has been discussed and resolved in here. Now you simply can do that:
interface Props {
thing: string; // note: not optional within class, but is optional when writing due to the default
}
class Thing extends React.Component<Props> {
static defaultProps = {
thing: 'hello'
}
render() {
console.log(this.props.thing) // strict type from `Props`
// ...
}
}
// ..
<Thing /> // works, thanks to `defaultProps`
It works for me in Typescript 3.1.6 and React 16.5.0
Upvotes: 4
Reputation: 542
I use a higher order component with Typescript 2.8 in my codebase:
const withDefaultProps = <P extends object, DP extends Partial<P>>(
defaultProps: DP,
component: React.ComponentType<P>
) => {
type ActualProps = Partial<DP> & Pick<P, Exclude<keyof P, keyof DP>>
component.defaultProps = defaultProps
return component as React.ComponentType<ActualProps>
}
interface IComponentProps {
someOptionalProp: string
}
class SomeComponent extends React.Component<IComponentProps, {}> {
method() {
// this is always defined
const shouldNotBeOptional = this.props.someOptionalProp;
}
}
// when using this like <SomeComponent />, someOptionalProp is optional
export default withDefaultProps(
{ someOptionalProp: 'defaultValue' },
SomeComponent
)
Upvotes: 1
Reputation: 249676
You could use a higher order component (HOC) that will just change the type of the props to be optional where they are present in defaultProps
interface SomeComponentProps {
required: string;
someOptionalProperty: string;
trulyOptional?: string;
}
interface SomeComponentState { /* ... */ }
class SomeComponent extends React.Component<SomeComponentProps, SomeComponentState> {
static defaultProps = {
someOptionalProperty : 'some default value',
}
method() {
const shouldNotBeOptional = this.props.someOptionalProperty;
}
}
type SameTypeOrCustomError<T1, T2> = T1 extends T2 ? T2 extends T1 ? T1 : "Error Different Types": "Error Different Types";
type FilterOptional<T> = Exclude<{ [P in keyof T]: undefined extends T[P]? P: never }[keyof T], undefined>;
type d = FilterOptional<SomeComponentProps>
type PropsWithDefault<TProps extends TDefaults, TDefaults> =
{ [P in Exclude<keyof TProps, keyof TDefaults | FilterOptional<TProps>>]: TProps[P] } &
{ [P in keyof TDefaults | FilterOptional<TProps>]?: TProps[P] };
type ReactProps<T> = T extends React.Component<infer Props, any> ? Props: never;
type ReactState<T> = T extends React.Component<any, infer TState> ? TState: never;
type ChangeReactProps<TComponent extends React.Component<any, any>, TNewProps> = {
new (props: TNewProps, context?: any) : React.Component<TNewProps, ReactState<TComponent>>
};
function withDefaults<T extends { new (...args: any[]) : any, defaultProps: Partial<ReactProps<InstanceType<T>>> }>(ctor: T)
: ChangeReactProps<InstanceType<T>, PropsWithDefault<ReactProps<InstanceType<T>>, T['defaultProps']>> {
return ctor; // we just chage type, we can return the same class
}
const SomeComponent2 = withDefaults(SomeComponent);
let d = <SomeComponent2 required="" /> //ok
let x = <SomeComponent2 required="" trulyOptional="" /> //ok
Upvotes: 1
Reputation: 1895
Not really possible as it stands with TS 2.8.3, there are some issues open about it already
On react types: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/11640
On the TS repo: https://github.com/Microsoft/TypeScript/issues/23812
Best you can achieve currently I believe is by using the !
operator to tell the compiler that you know something can't be null
const shouldNotBeOptional = this.props.someOptionalProp!;
Upvotes: 1