The combinatorial explosion you're picturing only shows up if you make a separate type per combination of present fields, but you don't need to. An independent optional field stays one `T | null`. You only reach for distinct types when fields are correlated and present together because they represent a state, and then it's a discriminated union on a status field, which is N states, not 2^N.
Using types like this also means you can more easily avoid assignment errors, as everything will have a very specific type (e.g. Age instead of int).
The short version is: the shape of a type is inherent to the type itself, but the optionality of its members is dependent on the situation. A type system that solves this problem separates these concepts to allow for this distinction.
I _suspect_ it's possible to implement something like that in typescript but I haven't tried it myself (and I doubt it's very ergonomic).
class User{phone: ?PhoneNumber}
over class User{phone: ?string}.It's more about writing
struct User {phone: MaybePhoneNumber} // give or take, it's a monoid
over struct User {phone: Option<String>}> monoid
nullables with `??` and `?.` are also give-or-take monoids. is it common though to `or` two MaybePhoneNumbers together or to apply a PhoneNumber->MaybePhoneNumber function to it? if not then why mention it?
let's see something meaningfully different like a database schema.
[1] https://esolangs.org/wiki/Trivial_brainfuck_substitution