TypeScript Classes
With TypeScript, class objects can use type annotation to describe the relationship between class members and their static types. Classes are a core part of the JavaScript language that TypeScript respects.
In addition to type annotations, TypeScript adds a few new syntaxes to help write classes in a type-safe manner.
Members
Class member types work similarly to parameter and variable types.
- If an initial value exists, the member’s type is inferred to be that type.
- If no initial value exists, the member is implicitly of type
anyunless a type annotation exists on its name.
In the following Box class, count is inferred to be of type number because its initial value is 0, while value is explicitly declared as type string.
class Box {count = 0;value: string;constructor(value: string) {this.value = value;}}
Therefore, asking for the count of a Box instance is of type number, and the value is of type string:
const myBox = new Box('stuff');myBox.count; // Type: numbermyBox.value; // Type: string
Member Visibility
JavaScript has a # syntax to mark a member as private, meaning it can only be accessed inside its class:
class Secret {#value: string;constructor(value: string) {this.#value = value;}getValue() {return this.#value;}}const mySecret = new Secret('shhh');mySecret.getValue(); // OkmySecret.#value;/*Error: Property '#value' is not accessible outsideclass 'Secret' because it has a private identifier.*/
Public, Private, or Protected
Separately, TypeScript also supports adding public, protected, or private in front of a class member to indicate whether the member may be used outside that class.
public(default): anybody may access that member, anywhere.protected: only the class or its derived classes may access that member.private: only the class itself may access that member.
The key difference between JavaScript’s # syntax and TypeScript’s privacy keywords are that:
- JavaScript’s
#syntax changes the name of the member. - TypeScript’s privacy keywords exist only in the type system; the member’s name is not changed.
In this example, the Base class sets up five variables:
- The first two,
firstandsecond, are publicly accessible. - The third,
third, is protected and only accessible in the class or a derived class. - The fourth and fifth,
fourthandfifth, are set to private withprivateand#, respectively.
class Base {// These two are functionally equivalentfirst = '';public second = '';protected third = '';private fourth = '';#fifth = '';}
The Derived class uses the extends keyword to become a child of the Base class. In the example below, a .test() method is used to borrow from members of the parent Base class:
class Derived extends Base {test() {this.first; // Ok: publicthis.second; // Ok: publicthis.third; // Ok: protectedthis.fourth;/*Error: Property 'fourth' is privateand only accessible within class 'Base'.*/this.fifth;/*Error: Property '#fifth' is not accessible outsideclass 'Base' because it has a private identifier.*/}}
The members first, second, and even the protected third, are all accessible within the Derived class definition. With fourth and fifth, however, they are both private members of Base. Therefore, they are not accessible inside of Derived.
The example below shows how some members of Derived can be invoked through an instance variable, derived, while others are not as accessible. While third can be used within the actual Derived class, as a protected member, it cannot be used outside of the class with an instance variable.
const derived = new Derived();derived.first; // Ok: publicderived.second; // Ok: publicderived.third;/*Error: Property 'third' is protectedand only accessible within class 'Base'.*/derived.fourth;/*Error: Property 'fourth' is privateand only accessible within class 'Base'.*/derived.fifth;/*Error: Property '#fifth' is not accessible outsideclass 'Base' because it has a private identifier.*/
Implementing Interfaces
Classes can use the TypeScript interface keyword to further define the “shape” of the class object. This means that instances of the class are assignable to a given interface type.
To use interfaces with classes, we use the implements keyword between the class name and name of the interface followed by the opening curly bracket {.
In this example, the Speaker interface has a single speak() method, so the SpeechGiver class may be marked as implementing it:
interface Speaker {speak(): string;}class SpeechGiver implements Speaker {constructor(speech: string) {this.speech = speech;}increaseEmphasis() {this.speech += '!';}speak() {return this.speech;}}
Classes can implement more than one interface, as well, by a comma-separated (,) list of any number of interfaces.
Marking a class as implementing an interface doesn’t change anything about the class itself; it won’t change the types of any members or implicitly add them to the class. It’s purely a way to make sure the class implements the interface properly where it’s declared, rather than when instances of the class happen to not match up with an interface.
Contribute to Docs
- Learn more about how to get involved.
- Edit this page on GitHub to fix an error or make an improvement.
- Submit feedback to let us know how we can improve Docs.
Learn TypeScript on Codecademy
- A full-stack engineer can get a project done from start to finish, back-end to front-end.
- Includes 51 Courses
- With Professional Certification
- Beginner Friendly.150 hours
- Learn TypeScript, a superset of JavaScript that adds types to make the language scale!
- Intermediate.10 hours