This proposal is opinionated, as there is no “good” or “wrong” way of doing this. But I still wanted to tackle on that subject.
A lot of statically-typed languages don’t allow optional properties. When a property is “optional”, it is marked as being able to be assigned a null (or equivalent) value.
In TypeScript, we can do this through the T | null type, or with T | undefined.
I personally think optional properties should almost never be used in TypeScript and instead replaced by nullable or undefined-able types. For instance:
// With optional properties
type T = { prop?: string }
// Without
type T = { prop: string | undefined }
The immediate benefit is having a very explicit type. Everywhere that type is used, you need to write all the properties as they are required.
Now, this may seem very heavy as you need to specify additional properties whereas you didn’t it that before, but it makes the code much more explicit.
When using optional properties, these don’t appear in your code. This means someone reading it needs to know there are optional, “hidden” properties in the object, otherwise they may miss out some options that they’re not aware of.
Also, explicitly stating a property hasn’t been assigned a value and will need to be handled by the recipient function is clearer than not specifying anything at all. When you provide a null or undefined, you know that the recipient function will have some behavior to take care of it because you didn’t explicitly specify something.
I understand the motivation of seeing everything being explicit everywhere.
But, in my experience, it is also pushing the burden on the user of your API, and makes backward compatibility impossible.
Let’s say you expose type T on version 1.0.0 of your application.
type T = {
key1: string;
}
At runtime, users of T can confidently use key trusting that the value will be available (thank you typechecker).
But now version 2.0.0 is released with a new key on type T
type T = {
key1: string;
key2: string;
}
If key2 is mandatory:
all the places where T is constructed now have to be updated which can be a lot of work (and pushing work on others if T is used for code we don’t control)
code now using key2 internally cannot be safely used with library calling it from v1.0.0.
If key2 is optional:
as a maintainer, you are forced to handle the case where key2 might be missing
but the change has much less impact of the rest of the codebase, and the codebase of other which makes it possible to upgade seamlessly
it’s also safer at runtime since users of v1.0.0 can call code using v2.0.0 safely
Therefore, I don’t think this is a practice we want to promote.
Of course this does mean we can’t do it sporadically, but this is a breaking change that needs to be discussed case by case. https://www.xwiki.org/xwiki/bin/view/Blog/HowToBreakAPI by @vmassol is a good longer form explanation of XWiki’s approach to backward compatibility.