I am currently utilizing a library that offers an interface with a great deal of flexibility.
type Option = number | {
x?: number;
y?: number;
z?: number;
}
interface Options {
a?: Option;
b?: Option;
c?: Option;
d?: Option;
}
function initializeLibrary(options: Options) {
// ...
}
My goal is to define a limited set of options, manipulate the values myself, and then pass them into the library.
// 1. Define a sparse set of options.
const opts = {
a: 3,
b: 4,
c: { x: 3 },
};
// 2. Utilize the option values.
console.log("Sum:", opts.a + opts.c.x);
// 3. Pass the options into the library.
initializeLibrary(opts);
While the above works, it lacks type safety. I can introduce keys to opts
that are not part of Options
, without receiving any errors.
const opts = {
a: 3,
f: 3, // `f` is not part of Options.
};
initializeLibrary(opts); // No error!
If I declare opts
with type Options
, it becomes type-safe but I encounter an error when accessing opts.a
.
const opts : Options = {
a: 3,
b: 4,
c: { x: 3 },
};
console.log(opts.a + opts.c.x); // Error: Object is possibly 'undefined'
How can I declare my options in a type-safe manner while still having access to the values?
To address this issue, I have attempted to redeclare the specific components of the Options
interface that I require.
interface MyOptions extends Required<Pick<Options, "a" | "b" | "c">> {
a: number;
c: { x: number };
}
const opts: MyOptions = {
a: 3,
b: 4,
c: { x: 3 },
};
While this method does provide protection against adding properties that do not exist in Options
, my use case involves more complexity, so I aim to find a way for the system to handle this task for me.