TypeScript: проблема полиморфизма интерфейса - PullRequest
0 голосов
/ 09 февраля 2019

У меня есть базовая учетная запись интерфейс:

interface Account {
  id: number;
  email: string;
  password: string;
  type: AccountType;
}

, где AccountType :

enum AccountType {
  Foo = 'foo',
  Bar = 'bar'
}

и два подтипа учетных записей ( FooAccount и BarAccount ), расширяющие интерфейс Account :

interface FooAccount extends Account {
  foo: Foo;
}
interface BarAccount extends Account {
  bar: Bar;
}

Account - это совокупность, которая содержитОсновная информация об учетной записи и, в зависимости от типа, владеет объектом Foo или Bar .

Действия над этими объектами могут выполнять только их владельцы (учетная запись).

Я определил AccountRepository :

export interface AccountRepository {
  findById(accountId: number): Account;
}

где findById(accountId: number) возвращает учетную запись , но эта учетная запись может быть любой FooAccount или BarAccount .

Я хочу использовать эту findById перед выполнением каких-либо действий с Foo или Bar.Например, допустим, я хочу обновить Foo:

  • будет использовать findById(accountId: number) для извлечения учетной записи
  • проверить AccountType учетной записи, в данном случае account.type === AccountType.Foo
  • , если проверка AccountType верна, то получит доступ к account.foo.id и будет использовать fooId для выполнения требуемого обновления

Проблема здесь в том, что эта последняя точкане удается: findById(accountId: number): Account возвращает Account , и в его интерфейсе не определено свойство foo: Foo.

Я также попытался выполнить следующее, но также не может быть выполнено:

const fooAccount: FooAccount = findById(accountId);

потому что функция возвращает Account .

Я пытаюсь выяснить, как этого можно достичь, что я упускаю?Есть ли что-то, что я могу делать неправильно?

Ответы [ 2 ]

0 голосов
/ 16 февраля 2019

Решил это, используя Тип утверждения , просто добавив as FooAccount, например:

const fooAccount: FooAccount = findById(accountId) as FooAccount;

Нет необходимости изменять существующий дизайн, чтобы достичь его.

По существу, утверждение от типа S к T успешно выполняется, если либо S является подтипом T, либо T является подтипом S.

Подробнее: https://basarat.gitbooks.io/typescript/docs/types/type-assertion.html

0 голосов
/ 09 февраля 2019

Лучшее решение, вероятно, состоит в том, чтобы использовать дискриминационный союз.

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
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...