I have a base Account interface:
interface Account {
id: number;
email: string;
password: string;
type: AccountType;
}
where AccountType:
enum AccountType {
Foo = 'foo',
Bar = 'bar'
}
and two account subtypes (FooAccount and BarAccount) that extend the Account interface:
interface FooAccount extends Account {
foo: Foo;
}
interface BarAccount extends Account {
bar: Bar;
}
Account is an aggregate that holds basic account info and, depending on the type, owns a Foo or a Bar object.
Actions on these objects, can only be performed by their owners (the account).
I have defined an AccountRepository:
export interface AccountRepository {
findById(accountId: number): Account;
}
where the findById(accountId: number) returns an Account, but this account could be any FooAccount or BarAccount.
I want to use this findById function before performing any action on a Foo or Bar. For example, let's say I want to update an account's Foo:
findById(accountId: number) to retrieve the accountaccount.type === AccountType.Fooaccount.foo.id and use that fooId to perform the desired updateThe problem here is, that this last point is failing: as findById(accountId: number): Account returns an Account and there is no foo: Foo property defined in its interface.
I have also tried the following, but cannot be done either:
const fooAccount: FooAccount = findById(accountId);
because the function returns an Account.
I am trying to figure out how can this be achieved, what am I missing out? Is there anything I could be doing wrong?
The best solution is probably to use a discriminated union.
export class Bar { public idBar: number; }
class Foo { public idFoo: number; }
interface AccountCommon {
id: number;
email: string;
password: string;
}
enum AccountType {
Foo = 'foo',
Bar = 'bar'
}
interface FooAccount extends AccountCommon {
type: AccountType.Foo; // type can only be Foo
foo: Foo;
}
interface BarAccount extends AccountCommon {
type: AccountType.Bar; // type can only be Bar
bar: Bar;
}
// The discriminated union
type Account = BarAccount | FooAccount //type is common so type can be either Foo or Bar
export interface AccountRepository {
findById(accountId: number): Account;
}
let r: AccountRepository;
let a = r.findById(0);
if (a.type === AccountType.Bar) { // type guard
a.bar.idBar // a is now BarAccount
} else {
a.foo.idFoo // a is now FooAccount
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With