【问题标题】:TypeScript: interface polymorphism issueTypeScript:接口多态性问题
【发布时间】:2019-07-03 14:57:20
【问题描述】:

我有一个基本的帐户界面:

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

其中账户类型

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

以及扩展 Account 接口的两个帐户子类型(FooAccountBarAccount):

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) 返回一个 Account,但此帐户可以是任何 FooAccountBarAccount

我想在对FooBar 执行任何操作之前使用这个findById 函数。例如,假设我要更新帐户的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);

因为该函数返回一个帐户

我试图弄清楚如何实现这一点,我错过了什么?有什么我可能做错了吗?

【问题讨论】:

    标签: javascript typescript oop


    【解决方案1】:

    最好的解决方案可能是使用有区别的联合。

    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
    }
    

    【讨论】:

      【解决方案2】:

      使用 Type Assertion 解决了这个问题,只需像这样添加 as FooAccount

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

      无需修改现有设计即可实现。

      基本上,如果 S 是 T 的子类型或 T 是 S 的子类型,则从类型 S 到 T 的断言成功。

      更多信息: https://basarat.gitbooks.io/typescript/docs/types/type-assertion.html

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-11-20
        • 2019-11-25
        • 2018-12-12
        • 2021-10-30
        • 2011-02-20
        • 1970-01-01
        • 1970-01-01
        • 2019-04-25
        相关资源
        最近更新 更多