Reputation: 107
I have an interface for a react component. There are a bunch of properties there, but I want one property to affect the others. So, what I have is:
export interface IMyAwesomeComponentProps<T = {}> {
className: string
defaultPath?: ISomeOtherInterface<T>
// more properties...
// ...and finally
isDraggable?: boolean
onDrag?: () => {}
}
This thing is implemented like so:
export class MyAwesomeComponent<T> extends React.Component<IMyAwesomeComponentProps<T>> {
//...
}
So, what I what to do is when I pass the "isDraggable" as "true", I want the "onDrag" to be required.
To resolve this, I first excluded "onDrag" function from the interface and made a new type. Then I made a utility type which basically says "if that "isDraggable" thing is true, then return the interface combined with "TDraggableProps" otherwise just give me the interface back". Overall it looks like this:
type TDraggableProps = {
onDrag: () => void
// maybe some more props
}
type Test<I extends ITreeProps> = I['isDraggable'] extends true ? I & TDraggableProps : I
export class MyAwesomeComponent<T> extends React.Component<Test<IMyAwesomeComponentProps<T>>>
Aaaand, this kinda works except it's not. And I have no idea what to do with that generic that comes into the interface.
Upvotes: 0
Views: 663
Reputation: 1082
I would make the Props a type then move all interface for all the common properties and for different properties, I think this approach looks clean and readable:
interface Common {
x: number;
y: number;
z: number;
}
interface RequiredB {
a: true;
b: () => {}
}
interface OptionalB {
a: false;
b?: () => {}
}
type Props = Common & (RequiredB | OptionalB);
const obj: Props = {
x: 1,
y: 2,
z: 3,
//a: false, // b is not required now
//a: true, // b is required now
}
Upvotes: 1
Reputation: 2761
I would change your interface IMyAwesomeComponentProps signature by creating a IDraggable interface and include it in your IMyAwesomeComponentProps. Of course assuming here that having the situation where isDraggable = false
and the onDrag is defined
is useless...
Something like this :
interface ITest<T = {}> {
a: string;
b: IInner<T>;
isDraggable?: IIsDragble;
}
interface IIsDragble {
onDrag: () => void;
}
const a: ITest = {
a: "dsd",
b: {
valInner: "ssdds",
},
};
const b: ITest = {
a: "dsd",
b: {
valInner: "ssdds",
},
isDraggable: {
onDrag: () => true,
},
};
Upvotes: 1
Reputation: 3230
You can use Discriminated union
type Props = {
isDraggable: true;
onDrag: () => void;
} | {
isDraggable: false;
}
const Component = (props: Props) => {}
Component({ isDraggable: false }); // Ok
Component({ isDraggable: true }); // Error onDrag is missing
Component({ isDraggable: true, onDrag: () => {} }); // Ok
If you need to add some common props you can either make the types as interface and extend some base interface, or just extract them to a separate type and join to your props
type Props = {
isDraggable: true;
onDrag: () => void;
} | {
isDraggable: false;
}
type StaticProps = {
className: string;
}
const Component = (props: Props & StaticProps) => {}
Component({ isDraggable: false, className: "123" });
Component({ isDraggable: true, className: "123" }); // Error onDrag is missing
Component({ isDraggable: true, onDrag: () => {}, className: "123" }); // Ok
Upvotes: 1