I have a function being called:
const accountDetails: IAccount | ISmallError = getAccount()
It might return IAccount or ISmallError.
On the next line I want to do this:
if (accountDetails.error) return response.json({ error: error.error });
But under .error
it says
Property 'error' does not exist on type 'IAccount | ISmallError'.
Property 'error' does not exist on type 'IAccount'.ts(2339)
what do I do about this?
One solution that does work is:
const err = accountDetails as ISmallError;
if (err.error) return response.json({ accountDetails: "No account found" });
but is this the best way?
CodePudding user response:
Assuming the property is meant to be a discriminant property something as simple as 'error' in accountDetails
will be sufficient:
type IAccount = { payload: 2 }
type ISmallError = { error: Error }
declare const getAccount: () => IAccount | ISmallError;
const accountDetails: IAccount | ISmallError = getAccount()
if ('error' in accountDetails) {
accountDetails.error // No longer an error
}
Using a property existence check is better (in my opinion) because it uses the type system instead of overriding it (as the cast does), ensuring we will get an error in more circumstances in future.
Even better would be to introduce a proper discriminant property to IAccount
and ISmallError
so that you can switch on the tag. That helps avoid the case where IAccount
grows its own error
field in the future (fewer places for collisions makes for an easier-to-maintain codebase):
type IAccount = { type: 'accountDetails', payload: 2 }
type ISmallError = { type: 'prodError', error: Error }
// ... snip ...
if (accountDetails.type === 'prodError') {
// Full narrowing here and no potential overlapping fields later
}