cf.completefrontend
Open lab My progress
THE TYPESCRIPT FIELD GUIDE

implements & abstract classes

Make a TypeScript class promise a shape with implements, and write abstract base classes that subclasses must finish, using payment classes you can break.

By the end, you can
  • 01
    Make a class keep a promiseUse implements so TypeScript checks a class against an interface, and know what it doesn't change.
  • 02
    Write a base class to finishWrite an abstract class with shared code and abstract members that every subclass must fill in.
  • 03
    Pick the right toolChoose between an interface and an abstract class, and know what each leaves in the JavaScript.

A class that promises a shape

A checkout page lets the shopper pay by card or with a gift card. Each way to pay is a class, and the checkout calls the same method on each one: pay. Here it is in plain JavaScript (Class basics covers classes). One class got its method name wrong:

Two payment classes in plain JavaScriptPop out in the code editor (opens in a new tab)JavaScript
class CardPayment {
  pay(amount) {
    return `Paid $${amount} by card`;
  }
}

class GiftCardPayment {
  charge(amount) {
    return `Paid $${amount} with a gift card`;
  }
}

const methods = [new CardPayment(), new GiftCardPayment()];
for (const method of methods) {
  console.log(method.pay(7));
}

Lines 1 to 5 make CardPayment, with a pay method. Lines 7 to 11 make GiftCardPayment, but its method on line 8 is called charge. Line 13 puts one of each in a list, and line 15 calls pay(7) on each. The card prints Paid $7 by card. The gift card has no pay, so the code stops with TypeError: method.pay is not a function. The first person to find out is a shopper with a gift card.

In TypeScript, you can write down the shape every payment method must have, and make each class promise to have it. The shape is an interface: a named list of the properties and methods an object must have (the Type aliases & interfaces lesson introduced them). The promise is the word implements:

The same classes with a promise (an error on purpose)Pop out in the code editor (opens in a new tab)TypeScript
interface PaymentMethod {  pay(amount: number): string;} class CardPayment implements PaymentMethod {  pay(amount: number) {    return `Paid $${amount} by card`;  }} class GiftCardPayment implements PaymentMethod {  charge(amount: number) {    return `Paid $${amount} with a gift card`;  }}

Lines 1 to 3 are the interface. PaymentMethod says: anything that is a payment method has a pay method that takes a number and gives back a string. Line 5 says CardPayment implements PaymentMethod, and it does. Line 11 makes the same promise for GiftCardPayment, and TypeScript marks it, on purpose, because the class breaks its promise:

  • Class 'GiftCardPayment' incorrectly implements interface 'PaymentMethod' means "this class said it has the shape of PaymentMethod, and it doesn't".
  • The second line of the message says what's wrong: Property 'pay' is missing in type 'GiftCardPayment' but required in type 'PaymentMethod'.

Nobody has used the class yet, and the mistake is already marked, on the class itself. Try it: type a different name for the gift card's method and watch the list under the code.

Playground: name the gift card's method
Two payment classes and their promisePop out in the code editor (opens in a new tab)TypeScript
interface PaymentMethod {  pay(amount: number): string;} class CardPayment implements PaymentMethod {  pay(amount: number) {    return `Paid $${amount} by card`;  }} class GiftCardPayment implements PaymentMethod {  charge(amount: number) {    return `Paid $${amount} with a gift card`;  }}
Line 12 namescharge
What happens
The gift card hascharge, no pay
TypeScript1 error: line 11
Try it yourself

The gift card has a charge method and no pay. TypeScript marks line 11: Class 'GiftCardPayment' incorrectly implements interface 'PaymentMethod'. The second line of the message says why: Property 'pay' is missing in type 'GiftCardPayment'.

The real TypeScript compiler checks the code each time you type. Only line 12 changes. Reset puts charge back.

With pay, the class keeps its promise and the error disappears. Pay with a capital P and payNow keep the error: the checkout calls pay, so only that exact name counts.

Left, a window named PaymentMethod lists pay(amount: number). Right, two classes are checked against it: CardPayment has pay(amount: number) and keeps the promise; GiftCardPayment has charge(amount: number), has no pay, and gets an error on line 11.the interfacePaymentMethodpay(amount: number)what every payment needsCardPaymentpay(amount: number)keeps the promiseGiftCardPaymentcharge(amount: number)no pay: error on line 11
The interface lists what every payment method needs. Each class that says implements PaymentMethod is checked against that list. GiftCardPayment has no pay, so TypeScript marks line 11.
Real-life analogyA job advert and an applicant

A delivery company posts a job advert: "You need a driving licence." An applicant ticks a box that says "I meet every point". Before the interview, the recruiter checks the claim against the advert and finds the licence missing. Ticking the box was a promise; it didn't give the applicant a licence.

In real life: The job advert lists what the job needs: a driving licence
In TypeScript: The interface lists what a payment method needs: pay(amount: number)
In real life: An applicant writes "I meet every point" on the form
In TypeScript: A class writes implements PaymentMethod
In real life: The recruiter checks each point before the interview
In TypeScript: TypeScript checks each member before the code runs
In real life: Writing the claim doesn't give anyone a licence
In TypeScript: implements doesn't add pay to the class; you write it

Interfaces are one way to share a shape. The second half of this lesson covers the other: an abstract class, a base class with real code and some blanks that every class built on it must fill in.

The main idea

implements makes a class promise to have an interface's shape, and TypeScript checks the promise on the class itself. An abstract class is a base class you can't make objects from: it shares its code, and its abstract members are blanks that each subclass must fill in. Both checks happen before the code runs.

What implements checks, and what it doesn't

Without implements, TypeScript still catches the wrong method name, but later: only where the class is used as a PaymentMethod. Here the checkout function on lines 11 to 13 takes a PaymentMethod, and line 15 passes it a gift card. Press each button and watch where the errors appear:

Playground: where the mistake gets marked
A gift card class used at checkoutPop out in the code editor (opens in a new tab)TypeScript
interface PaymentMethod {  pay(amount: number): string;} class GiftCardPayment {  charge(amount: number) {    return `Paid $${amount} with a gift card`;  }} function checkout(method: PaymentMethod, total: number) {  console.log(method.pay(total));} checkout(new GiftCardPayment(), 7);
When it runsTypeError: method.pay is not a function
What happens
Line 5class GiftCardPayment {
TypeScript1 error: line 15
Try it yourself

The class makes no promise, so TypeScript has nothing to check it against. The mistake only shows up on line 15, where the class is used: Argument of type 'GiftCardPayment' is not assignable to parameter of type 'PaymentMethod'. In a real project, line 15 could be in another file, written by someone else.

TypeScript checks the code for real, and the output is what this code does when it runs. The buttons change only lines 5 and 6. Reset goes back to Without implements.

Without implements, only line 15 gets an error: Argument of type 'GiftCardPayment' is not assignable to parameter of type 'PaymentMethod'. An argument is a value you pass in a call. The message is right, but it points at the place the class is used, not the place the mistake is. With implements, line 5 gets its own error, right on the class. Both errors go when line 6 says pay.

A class can promise more than one shape. List the interfaces after implements, with commas:

A card payment that keeps two promisesPop out in the code editor (opens in a new tab)TypeScript
interface PaymentMethod {  pay(amount: number): string;} interface Refundable {  refund(amount: number): string;} class CardPayment implements PaymentMethod, Refundable {  lastFour = "4242";  pay(amount: number) {    return `Paid $${amount} by card`;  }  refund(amount: number) {    return `Refunded $${amount} to the card`;  }} const card = new CardPayment();console.log(card.pay(7));console.log(card.refund(3), card.lastFour);
  • Lines 1 to 3 and 5 to 7 are two interfaces: one for paying, one for giving money back.
  • Line 9 promises both. TypeScript checks for pay (lines 11 to 13) and refund (lines 14 to 16).
  • Line 10 adds lastFour, which neither interface asks for. That's fine: a class may have more than its interfaces list.
  • Line 20 prints Paid $7 by card, and line 21 prints Refunded $3 to the card 4242.

Now the part that surprises most people. implements is only a check. It doesn't change the class in any way, so it doesn't type the class's methods for you:

implements doesn't type the parameter (an error on purpose)Pop out in the code editor (opens in a new tab)TypeScript
interface PaymentMethod {  pay(amount: number): string;} class CashPayment implements PaymentMethod {  pay(amount) {    return `Pay $${amount.toFixed(2)} at the door`;  }} console.log(new CashPayment().pay(7));

The interface says pay takes a number, so you might expect amount on line 6 to be a number too. It isn't. TypeScript types the class first, on its own, and only then compares it with the interface. Line 6 gets Parameter 'amount' implicitly has an 'any' type: amount has no type, so TypeScript uses any, which means "anything, and don't check it", and strict mode won't do that silently. Point at amount to see (parameter) amount: any. Line 11 still prints Pay $7.00 at the door. Write pay(amount: number) and the error goes.

For the same reason, an optional property in the interface doesn't appear on the class:

An optional property the class never declared (an error on purpose)Pop out in the code editor (opens in a new tab)TypeScript
interface Profile {  name: string;  avatarUrl?: string;} class GuestProfile implements Profile {  name = "Guest";} const guest = new GuestProfile();guest.avatarUrl = "/images/guest.png";

Line 3 says a Profile may have an avatarUrl; the ? makes it optional. Lines 6 to 8 make a guest profile with only a name, and that keeps the promise. Line 11 sets guest.avatarUrl, and TypeScript says Property 'avatarUrl' does not exist on type 'GuestProfile'. The class is exactly what you wrote in it. To have an avatar, declare avatarUrl?: string; in the class.

The table sums it up. Read each row as: something you might expect from implements, does it do it, and why.

What implements does and doesn't do
Does implements…AnswerWhy
Check that the class has every member the interface listsYes.A missing member, or one with the wrong type, gets an error on the class line.
Add the interface's members to the classNo.You write every member yourself. implements only checks them.
Give the method's parameters their typesNo.pay(amount) still needs amount: number, or strict mode reports it.
Add the interface's optional propertiesNo.GuestProfile has no avatarUrl until the class declares one.
Stop the class from having extra membersNo.lastFour is fine. A class may have more than the interface asks for.
Leave anything in the JavaScriptNo.implements is removed with the other types.

Abstract classes: a base with a blank to fill in

A shop has discount codes. Every discount works the same way: work out how much to take off, then take it off the total. Only the first step differs: one code takes 10% off, another takes $5 off. In JavaScript, you'd write the shared part once in a parent class and let child classes build on it with extends (Class inheritance covers extends). TypeScript lets you mark the parent class abstract and leave the different part blank:

A discount base class and one discountPop out in the code editor (opens in a new tab)TypeScript
abstract class Discount {  constructor(public code: string) {}   abstract amountOff(total: number): number;   apply(total: number) {    return total - this.amountOff(total);  }} class PercentOff extends Discount {  amountOff(total: number) {    return total * 0.1;  }} const spring = new PercentOff("SPRING10");console.log(spring.code, spring.apply(20));
  • Line 1 makes Discount an abstract class. It's meant to be built on, not used on its own.
  • Line 2 stores the discount's code, like "SPRING10", with a parameter property.
  • Line 4 is an abstract method: a name, its parameters, and its return type, with no body. It says "every discount has an amountOff that takes a total and gives back a number, and each subclass writes its own".
  • Lines 6 to 8 are a normal method that every discount shares. apply calls this.amountOff(total) without knowing which discount it is.
  • Line 11 makes PercentOff, a subclass of Discount: a class that builds on it. Lines 12 to 14 fill in the blank: take off 10% of the total.
  • Line 17 makes a PercentOff. Line 18 prints SPRING10 18: the code, then 20 minus 2.
Left, an abstract class Discount holds apply(total), which has code, and abstract amountOff, which has none; new Discount is an error. An arrow labelled extends leads right to PercentOff, which writes amountOff(total) as total * 0.1; new PercentOff works.the abstract classDiscountapply(total)abstract amountOffnew Discount: errorextendsthe subclassPercentOffamountOff(total)total * 0.1apply comes from Discountnew PercentOff works
Discount writes apply once and leaves amountOff blank. PercentOff fills in the blank, so you can make a PercentOff with new, but not a Discount.

What you can't do is make a Discount itself. It has a blank, so there would be nothing to call:

Making an abstract class with new (an error on purpose)Pop out in the code editor (opens in a new tab)TypeScript
abstract class Discount {  constructor(public code: string) {}   abstract amountOff(total: number): number;   apply(total: number) {    return total - this.amountOff(total);  }} const mystery = new Discount("MYSTERY");console.log(mystery.apply(20));

Line 11 tries new Discount("MYSTERY"), and TypeScript says Cannot create an instance of an abstract class. An instance is an object made from a class with new. When the code runs anyway, line 12 calls apply, which calls this.amountOff, which doesn't exist: TypeError: this.amountOff is not a function.

Real-life analogyA recipe card with one blank

A café prints one recipe card for its muffins. The steps are the same for every batch, but one line is blank: "Flavour: ______". Each baker fills in a flavour on their own copy. Nobody bakes from a card with the blank still empty, because the steps would say "add the flavour" and there wouldn't be one.

In real life: A printed recipe card for flavoured muffins
In TypeScript: The abstract class Discount
In real life: The steps everyone follows: mix, bake for 20 minutes
In TypeScript: The shared method apply
In real life: The blank line: "Flavour: ______"
In TypeScript: The abstract method amountOff
In real life: A copy with "blueberry" written in
In TypeScript: A subclass, PercentOff, that writes amountOff
In real life: Nobody bakes from the card with the blank still empty
In TypeScript: new Discount() is an error

The rules for abstract members

A class that extends an abstract class must write every abstract member, unless it's abstract too. Try four ways of writing amountOff in a HalfOff discount:

Playground: how HalfOff writes amountOff
A discount subclass that fills in the blankPop out in the code editor (opens in a new tab)TypeScript
abstract class Discount {  constructor(public code: string) {}   abstract amountOff(total: number): number;   apply(total: number) {    return total - this.amountOff(total);  }} class HalfOff extends Discount {  amountOff(total: number) { return total / 2; }} const half = new HalfOff("HALF");console.log(half.apply(20));
When it runs10
What happens
TypeScriptNo errors
Try it yourself

HalfOff writes amountOff: it takes a number and gives back a number, as the abstract method says. No errors, and apply(20) prints 10.

TypeScript checks the code for real, and the output is what this code does when it runs. Each button changes only line 12. Reset goes back to Written.

Left out gets Non-abstract class 'HalfOff' does not implement inherited abstract member amountOff from class 'Discount'. A non-abstract class is one you can make objects from, so it can't have blanks. Returns text breaks the abstract method's promise to give back a number. No parameter is fine: a method may ignore values it's given, so it can take fewer parameters than the abstract method lists.

Two more rules, each broken here on purpose:

Two abstract methods in the wrong place (an error on purpose)Pop out in the code editor (opens in a new tab)TypeScript
class Banner {  abstract render(): string;} abstract class Popup {  abstract render(): string {    return "Sale!";  }}
  • Line 1 makes Banner without abstract, and line 2 gives it an abstract method. TypeScript says Abstract methods can only appear within an abstract class: a class you can make objects from can't have a blank.
  • Line 6 gives an abstract method a body. TypeScript says Method 'render' cannot have an implementation because it is marked abstract. An implementation is the code inside the braces. For a method with code that subclasses may replace, leave out abstract.

A property can be abstract too. The base class uses it, and each subclass gives it a value:

A notice with an abstract titlePop out in the code editor (opens in a new tab)TypeScript
abstract class Notice {  abstract title: string;   show() {    return `[${this.title}]`;  }} class SaleNotice extends Notice {  title = "Summer sale";} console.log(new SaleNotice().show());

Line 2 says every Notice has a title that is a string, without giving one. Lines 4 to 6 use it in show. Line 10 gives SaleNotice its title, and line 13 prints [Summer sale]. Leave out line 10 in Edit & run and line 9 gets the same "does not implement inherited abstract member" error.

Interfaces or abstract classes?

Both describe a shape that classes must match. The big difference is what's left when the types are removed. Here is an interface and an abstract class together:

An interface and an abstract class that keeps itPop out in the code editor (opens in a new tab)TypeScript
interface PaymentMethod {  pay(amount: number): string;} abstract class Payment implements PaymentMethod {  abstract pay(amount: number): string;   receipt(amount: number) {    return `Receipt: ${this.pay(amount)}`;  }}

Lines 1 to 3 are the interface. Line 5 makes an abstract class that promises to keep it. An abstract class can leave an interface's method blank, as line 6 does with pay; its subclasses will write it. Lines 8 to 10 are real code that every payment shares: a receipt. Here is what TypeScript gives the browser:

Left, a file payment.ts holds interface PaymentMethod, abstract class Payment, abstract pay, and receipt(amount). An arrow labelled types removed leads to payment.js, which holds only class Payment with receipt(amount).payment.tsinterface PaymentMethodabstract class Paymentabstract pay(…)receipt(amount)you write thistypesremovedpayment.jsclass Paymentreceipt(amount)the browser runs this
The interface, implements, and the abstract method are only types, so they're removed. The class and its receipt method are real code, so the browser gets them.
What the browser gets: only the class's real codePop out in the code editor (opens in a new tab)JavaScript
class Payment {
  receipt(amount) {
    return `Receipt: ${this.pay(amount)}`;
  }
}

The interface is gone. So are implements, the word abstract, and the abstract method on line 6: they were only types. Line 1 is a normal JavaScript class, and lines 2 to 4 are the receipt method, word for word.

Because the abstract class is still there when the code runs, you can test for it with instanceof, which asks "was this object made by this class, or a subclass of it?" (Checking types covers instanceof). You can't test for an interface:

instanceof works with a class, not an interface (an error on purpose)Pop out in the code editor (opens in a new tab)TypeScript
interface PaymentMethod {  pay(amount: number): string;} abstract class Payment implements PaymentMethod {  abstract pay(amount: number): string;} class CardPayment extends Payment {  pay(amount: number) {    return `Paid $${amount} by card`;  }} const card = new CardPayment();console.log(card instanceof Payment);console.log(card instanceof PaymentMethod);

Line 16 prints true: the card was made by CardPayment, a subclass of Payment. Line 17 gets 'PaymentMethod' only refers to a type, but is being used as a value here. In plain words: that name is only a type, and instanceof needs something that exists while the code runs. If you run it anyway, line 17 stops with ReferenceError: PaymentMethod is not defined.

The table compares the two. Read each row as a question, then the answer for an interface and for an abstract class.

Interfaces and abstract classes, side by side
QuestionAn interfaceAn abstract class
What does it hold?Only a list of members and their types.Real code, like receipt, plus abstract members for subclasses to fill in.
What is left in the JavaScript?Nothing. It's removed with the types.A normal class. Only the abstract members and the word abstract are removed.
Which word does a class use?implements, as in class CardPayment implements PaymentMethod.extends, as in class CardPayment extends Payment.
How many can one class use?Many, with commas: implements PaymentMethod, Refundable.One. A class can extend only one class.
Can you test for it with instanceof?No. It doesn't exist when the code runs.Yes. card instanceof Payment is true.
Can a plain object match it?Yes, like the voucher object at checkout.Its type, yes. But only a subclass gets its code, like receipt.

A simple way to choose:

  • Use an interface to say what something can do, when the classes or objects that match it share no code. Many unrelated classes, and plain objects, can match it.
  • Use an abstract class when the classes share real code, like apply or receipt, and each must fill in its own part.
  • Use both, as above: the interface for the shape the rest of your code uses, and the abstract class for the shared code.

Payment methods on a checkout page

Here is a checkout that uses both. The rest of the page only knows about PaymentMethod. An abstract class shares the code that works out a total with a fee, and a voucher is a plain object:

Three ways to pay at checkoutPop out in the code editor (opens in a new tab)TypeScript
interface PaymentMethod {  label: string;  pay(amount: number): string;} abstract class Payment implements PaymentMethod {  abstract label: string;  abstract fee(amount: number): number;   pay(amount: number) {    const total = amount + this.fee(amount);    return `${this.label}: $${total}`;  }} class CardPayment extends Payment {  label = "Card";  fee() {    return 0;  }} class CashOnDelivery extends Payment {  label = "Cash on delivery";  fee(amount: number) {    return amount < 20 ? 2 : 0;  }} const voucher: PaymentMethod = {  label: "Voucher",  pay: (amount) => `Voucher: $${amount} covered`,}; const methods: PaymentMethod[] = [new CardPayment(), new CashOnDelivery(), voucher];for (const method of methods) {  console.log(method.pay(12));}
  • Lines 1 to 4 are the interface: every payment method has a label to show on the page and a pay method.
  • Lines 6 to 14 are the abstract class. Lines 7 and 8 are blanks: each payment has its own label and its own fee. Lines 10 to 13 add the fee to the amount and build the text the page shows.
  • Lines 16 to 21 fill in the blanks for cards: no fee. Lines 23 to 28 do it for cash on delivery: $2 for orders under $20.
  • Lines 30 to 33 make a voucher as a plain object, with no class at all. The : PaymentMethod on line 30 is enough: TypeScript checks the object against the interface, and it even gives amount on line 32 its type, because the object is written right where a PaymentMethod is expected.
  • Line 35 puts all three in one list of PaymentMethod. Line 37 prints Card: $12, Cash on delivery: $14, and Voucher: $12 covered.

Now sort each card. Will TypeScript report an error, or is it fine?

Error or fine?
  • interface Item { id: number } class Row implements Item { id = 1; }
  • interface Item { id: number } class Row implements Item { id = "1"; }
  • interface Item { id: number } class Row implements Item { id = 1; name = "Tea"; }
  • interface Item { id: number } class Row implements Item {}
  • abstract class Widget { abstract render(): string } new Widget();
  • abstract class Widget { abstract render(): string } class Tip extends Widget {}
  • abstract class Widget { abstract render(): string } class Tip extends Widget { render() { return "Tip"; } }
  • abstract class Widget { render() { return "Widget"; } }
Try it yourself
0 of 8 correct

Each card is checked on its own with this lesson's settings. Place it in the group TypeScript would put it in.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Common mix-ups

  • "implements adds the interface's methods to my class." No. An interface has no code to add. You write every member, and implements checks them.
  • "implements gives my method's parameters their types." No. The class is typed on its own first. Write pay(amount: number), or amount is any.
  • "I can check instanceof PaymentMethod." No. An interface is removed with the types, so there's nothing to check against while the code runs. Use an abstract class, or check for the method.
  • "An abstract class can't have any code." No. It can have fields, a constructor, and normal methods like apply. Only its abstract members are blank.
  • "abstract stops the class from being made while the page runs." No. The word is removed with the types. It's TypeScript that reports new Discount(), before the code runs.
  • "A class can extend two abstract classes." No. It can extend one class, and implement as many interfaces as it likes.

Practice exercises

Each exercise is TypeScript. Press Edit & run on the starter code: the errors update as you type, and the console shows what it prints. Try a guess first, then use the hints.

Exercise 1 · Warm-upFind the line TypeScript marks

A theme class that promises a shape. Which line will TypeScript mark?

Starter codePop out in the code editor (opens in a new tab)TypeScript
interface Theme {
  name: string;
  apply(): string;
}

class DarkTheme implements Theme {
  name = "dark";
}

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · Warm-upFollow the shared code

    What does this code print? Guess first, then press Edit & run.

    Starter codePop out in the code editor (opens in a new tab)TypeScript
    abstract class Greeting {  abstract name(): string;   text() {    return `Hello, ${this.name()}!`;  }} class GuestGreeting extends Greeting {  name() {    return "guest";  }} console.log(new GuestGreeting().text());

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeFinish a discount

      Free shipping takes $5 off any order. Make FreeShipping fill in its blank. What does the fixed code print?

      Starter codePop out in the code editor (opens in a new tab)TypeScript
      abstract class Discount {  constructor(public code: string) {}   abstract amountOff(total: number): number;   apply(total: number) {    return total - this.amountOff(total);  }} class FreeShipping extends Discount {} const shipping = new FreeShipping("FREESHIP");console.log(shipping.code, shipping.apply(20));

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeMake the search box keep its promise

        The header's search box promises to be a SearchBox, and TypeScript marks two lines. Fix the class. What should the method on line 8 be called?

        Starter codePop out in the code editor (opens in a new tab)TypeScript
        interface SearchBox {  query: string;  clear(): void;} class HeaderSearch implements SearchBox {  query = "green tea";  reset() {    this.query = "";  }} const box = new HeaderSearch();box.clear();console.log(`[${box.query}]`);

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeType what implements doesn't

          TypeScript marks line 6, even though the interface says what price is. Fix it. What does the code print?

          Starter codePop out in the code editor (opens in a new tab)TypeScript
          interface PriceFormatter {  format(price: number): string;} class DollarFormatter implements PriceFormatter {  format(price) {    return `$${price.toFixed(2)}`;  }} console.log(new DollarFormatter().format(4.5));

          Answer, then press Check. Spacing and letter case don’t matter.

            Exercise 6 · ChallengeShare the toast code

            Two toast messages repeat the same show method. Move it into an abstract base class, so each toast only says its message. Which word goes in front of message() in the base class?

            Starter codePop out in the code editor (opens in a new tab)TypeScript
            class SavedToast {  message() {    return "Saved";  }  show() {    return `Toast: ${this.message()}`;  }} class ErrorToast {  message() {    return "Could not save";  }  show() {    return `Toast: ${this.message()}`;  }} console.log(new SavedToast().show());console.log(new ErrorToast().show());

            Answer, then press Check. Spacing and letter case don’t matter.

              Check your understanding

              For each question, think about two moments: what TypeScript says before the code runs, and what the code does when it runs. The quiz shows its code without the checker, so the error list doesn't give the answers away.

              The implements and abstract classes quiz · 7 questionsScore: first tries count
              1. Question 1 of 7What does implements PaymentMethod do to a class?

                Choose an answer to see the explanation.

              2. Question 2 of 7Which line does TypeScript flag in this cart item class?

                Read the code, then predictPop out in the code editor (opens in a new tab)TypeScript
                interface CartItem {
                  name: string;
                  price: number;
                }
                
                class TeaItem implements CartItem {
                  name = "Tea";
                }

                Choose an answer to see the explanation.

              3. Question 3 of 7This makes an abstract class with new. What does it print when it runs anyway?

                Read the code, then predictPop out in the code editor (opens in a new tab)TypeScript
                abstract class Banner {
                  text = "Summer sale";
                }
                
                const banner = new Banner();
                console.log(banner.text);

                Choose an answer to see the explanation.

              4. Question 4 of 7What type does TypeScript give width on line 6 of this image resizer?

                Read the code, then predictPop out in the code editor (opens in a new tab)TypeScript
                interface Resizer {
                  resize(width: number): string;
                }
                
                class ImageResizer implements Resizer {
                  resize(width) {
                    return `${width}px`;
                  }
                }

                Choose an answer to see the explanation.

              5. Question 5 of 7Which line does TypeScript flag in this widget subclass?

                Read the code, then predictPop out in the code editor (opens in a new tab)TypeScript
                abstract class Widget {
                  abstract render(): string;
                
                  mount() {
                    return `<div>${this.render()}</div>`;
                  }
                }
                
                class TipWidget extends Widget {
                  mount() {
                    return "<div>Tip</div>";
                  }
                }

                Choose an answer to see the explanation.

              6. Question 6 of 7A class needs shared code from a base class and must match two interfaces. What can it do?

                Choose an answer to see the explanation.

              7. Question 7 of 7What's left of an interface in the JavaScript the browser runs?

                Choose an answer to see the explanation.

              Key takeaways

              • class CardPayment implements PaymentMethod promises the class has the interface's shape, and TypeScript marks the class line if it doesn't.
              • A class can implement several interfaces, and may have more members than they list.
              • implements only checks. It doesn't add members, type parameters, or leave anything in the JavaScript.
              • An abstract class shares real code and leaves abstract members blank. You can't make one with new, and every non-abstract subclass must fill in every blank.
              • An interface disappears from the JavaScript; an abstract class stays as a normal class, so instanceof works with it.

              Remember the one-liner.
              An interface is a shape to promise; an abstract class is shared code with blanks to fill in.

              Coming next: Static members & class types. It shows what a class name means as a type, the type of the class itself, and members that belong to the class instead of its objects.

              CompleteFrontend Clear concepts. Working examples.