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
any
unless 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,
first
andsecond
, are publicly accessible. - The third,
third
, is protected and only accessible in the class or a derived class. - The fourth and fifth,
fourth
andfifth
, are set to private withprivate
and#
, 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.