I'm trying to determine how to handle optional fields in an options argument for a TypeScript class whilst only requiring the user of the class to provide only the arguments they need so all other options are defaulted (use case is allowing the user to customize the class via the options)
For example, if there's a class (e.g., Logger
) that accepts an options argument (e.g., IOptions
) where all options are optional & will be defaulted in the constructor. Then all IOptions
fields can be nullable so the user only provides the arguments they need (e.g., customizing the Logger
). This is fine since all nullable values will be defaulted in the constructor, however, if the options argument is used throughout the class, the compiler will error since the fields are marked as nullable in IOptions
. However, the constructor has defaulted all options so they should be safe but to avoid the error, either repeated null assertions are used (e.g., this.options.logTag!!
) or the defaulting is repeated (e.g., this.options.logTag ?? 'NONE
), both of these attempts to avoid errors are not ideal
Please explain how a defaulted options object can be used whilst only requiring the user to provide the fields they need?
Please also share any resources that might be helpful in answering this questin.
Example Logger
class:
interface IOptions {
logTag?: string;
// ...
}
class Logger {
private options: IOptions; // Assume options are always contained & accessed from this object.
constructor(options: IOptions = {}) {
this.options = {
...options,
logTag: options.logTag ?? 'NONE',
// ...
}
}
private saveTag(tag: string, message: string) { // Example demonstrating requiring non-null option value (logTag could be used directly, but passed here for example).
// ... (e.g., formatting, saving, etc.)
}
log(message: string) {
this.saveTag(this.options.logTag); // ERROR: Argument of type 'string | undefined' is not assignable to parameter of type 'string'.
// ...
}
}
const logger = new Logger();
logger.log('message');
CodePudding user response:
Based on @jonrsharpe's suggestion of Partial
, I found Required
solves the problem by letting the user provide any of the optional fields whilst avoiding the errors of potentially null options without using repeated null assertions or repeated defaulting:
private options: Required<IChangeloggerOptions>;
This means IChangeloggerOptions has fields that all remain nullable whilst the options in the class will all be required (these options won't error as they're all defaulted in the constructor)