Remix.run Logo
bern4444 5 hours ago

It's pretty trivial to create derived and augmented types with Pick, Omit, Required, Partial. Combined with a few parsing functions that return an object typed to whatever specification you need and you are set IE:

    type User = { name: string; verified: boolean; email?: string; lastName: string; birthday?: string | { year: string; month: string; date: string; }}

    type Birthday = Required<Pick<User, 'birthday'>>;
    type UserWithBirthday = User & { birthday: Birthday } 
    type VerifiedUser = User & { verified: true; email: string; }
    type VerifiedUserWithBirthday = User & UserWithBirthday & VerifiedUser;


    const userHasBDayAndEmail = (user: User): user is VerifiedUserWithBirthday => {
        if (user.email === undefined || user.birthday === undefined) {
            return false
        }

        return true
    }
Any caller of userHasBDayAndEmail knows for the rest of its nested call stack if the provided user is a User object or a VerifiedUserWithBirthday.

The types are cheap to write (they're all derived) and have no runtime impact (types are erased at build/compile time) and these parsing functions are quite small to write

https://www.typescriptlang.org/play/?#code/FAFwngDgpgBAqgZyg...

throwaw12 5 hours ago | parent [-]

creation is not a problem, maintenance is.

Suppose you want to add one more property to VerifiedUserWithBirthday and UnverifiedUserWithBirthday, you might get 2 more new types, and somewhere at the higher layer call chains you need to know which enclosing type you should pass so that some method in the bottom chain will accept it.

I am sure there are more elegant ways, but I am struggling to generalize it to most enterprise SaaS CRUD apps, where you have one object with bunch of properties and can conditionally traverse the code logic

bern4444 5 hours ago | parent [-]

Yeah that's the engineering part in software engineer :)

If you have VerifiedUserWithBirthday, any value that fails the parsing function is implicitly UnverifiedUserOrUserWithoutBirthday... No need to define it separately. You get the inverse type for free IE a value that is of type User and not of type VerifiedUserWithBirthday.

A new property doesn't mean a new derived type. Only if that new property impacts what a VerifiedUserWithBirthday should represent should the VerifiedUserWithBirthday type be updated and even then, it's not a new type, just an update to an existing type. Again minimal updates needed.

The compiler handles all the validation and will tell you exactly where there are any issues - the compiler is what makes the maintenance cost quite low.