I'm new to Typescript and I couldn't find any solutions for below scenario. Any suggestion will be greatly helpful.
I would like to make the property options optional if type is SHORT_TEXT or LONG_TEXT.
export interface Question {
[keyName: string]: any;
type: QuestionType;
id: string;
label: string;
helpText?: string;
placeholder?: string;
value?: string;
options?: Option[];
validations?: any;
required?: boolean;
priority?: number;
intent?: Intent[];
}
export type QuestionType =
| "DROPDOWN"
| "CHECKBOX"
| "RADIO"
| "SHORT_TEXT"
| "LONG_TEXT";
export interface Option {
id: string;
label: string;
value?: string;
}
I believe what you're looking for is a "Discriminated Union". Essentially as you described it: the value of a particular property can be used to narrow the type in a union, and in this case you want one (or more) types where the options property is required, and one type where it is optional.
export type QuestionType =
| "DROPDOWN"
| "CHECKBOX"
| "RADIO"
| "SHORT_TEXT"
| "LONG_TEXT";
export interface Option {
id: string;
label: string;
value?: string;
}
export interface Intent {};
export interface BaseQuestion {
[keyName: string]: any;
type: QuestionType;
id: string;
label: string;
helpText?: string;
placeholder?: string;
value?: string;
validations?: any;
required?: boolean;
priority?: number;
intent?: Intent[];
}
export interface TextQuestion extends BaseQuestion {
type: "SHORT_TEXT" | "LONG_TEXT";
options?: Option[];
}
export interface OptionQuestion extends BaseQuestion {
type: "DROPDOWN" | "CHECKBOX" | "RADIO";
options: Option[];
}
export type Question = TextQuestion | OptionQuestion;
const q: Question = {
type: "SHORT_TEXT",
id: "id_foo",
label: "Test Question",
};
You can try this out in the Typescript playground: you'll see that the validator doesn't have an issue with the q object, even though it doesn't have an options property. But if you change the type property on q to be "DROPDOWN" or something other than "SHORT_TEXT" or "LONG_TEXT", the validator will raise an error.
You'll also notice that if you type q in the interactive playground, the hint will have it cast as a TextQuestion rather than a Question, since the type has been narrowed. At least in this example, that actually prevents you from changing the q.type property later, but keep in mind that guard will only in blocks where the type of that union has already been narrowed. If you're looping over a list of questions, for example, and you reassign the type:
(questions as Question[]).forEach(q => {
q.type = "DROPDOWN";
});
Typescript hasn't narrowed each object's type in this case, and won't guard against you reassigning the type.
Hope this helps! ✌️
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With