Write Object-Oriented TypeScript: Abstraction
This is part 2 of a 4 part article on how to program in TypeScript using object-oriented techniques. If you are just starting with TypeScript and WebStorm, see our blog post on Getting Started with TypeScript.
Implement abstraction in TypeScript
Abstraction is a way to model objects in a system that creates a separation of duties between class or type and the code that inherits it. It’s also a way to enforce a concept called contract based development, as you’ll see in this post. A developer creates a type, i.e., a class or interface, and that type specifies what the calling code should implement, but not how. So it’s the job of the abstract type to define what needs to be done, but up to the consuming types to actually do those things. To enforce abstraction, inherit or implement from abstract classes and interfaces.
For example, some bank accounts have fees. You can create a Fee interface that defines a method for charging a fee. Fees don’t apply to all types of accounts, so it’s best to create an interface that can be applied to specific classes anywhere in the inheritance hierarchy. A checking account might charge fees, where its parent and sibling, the generic bank account and savings accounts might not.
interface Fee { chargeFee(amount: number ); } // parent BankAccount and sibling SavingsAccount do not implement Fee interface class BankAccount { ... } class SavingsAccount extends BankAccount { ... } // checking implements Fee class CheckingAccount extends BankAccount implements Fee { chargeFee(amount: number) {} }
Children classes inherit interface members that have been implemented in their parent, so if a BusinessChecking account has inherited from the CheckingAccount class, then it inherits that implementation.
// BusinessChecking inherits CheckingAccount and therefore Fee class BusinessChecking extends CheckingAccount { … } // Code that uses BusinessChecking can call chargeFee function CalculateMonthlyStatements() { let businessChecking = new BusinessChecking(); businessChecking.chargeFee(100); }
If you implement an interface, but haven’t implemented all the members yet, WebStorm notifies you and offers a quick fix (Alt-Enter) that inserts the method signatures into the class. From there, you can fill in the implementation code as you normally would.
Because abstraction means separating duties that can have blurry boundaries, sometimes, you find that you’ve added a member in the wrong level of the class hierarchy. Perhaps you have created a member that is better suited to be in the parent class for the children to inherit. In our banking scenario, this might be a minimum balance that was applied to a savings account, though a minimum balance makes sense for all types of bank accounts – not just savings. No need to worry, WebStorm contains a feature to pull members up so you can design better abstract classes. Use the Pull Members Up refactoring that you can find in the Refactor menu, or in the Refactor this… (Ctrl+Alt+Shift+T on Windows and Linux or Ctrl-T on Mac) popup.
Summary
In this article we’ve discussed what abstraction is and how to implement it in TypeScript through both abstract classes and interfaces using WebStorm.
Part 1: Write Object-Oriented TypeScript: Inheritance
[This article] Part 2: Write Object-Oriented TypeScript: Abstraction