Classes Flashcards
What is the most basic class
?
Here’s the most basic class - an empty one:
class Point {}
“Class Members” (typescriptlang.org). Retrieved April 14, 2023.
How can you create field in a class
?
A field declaration creates a public writeable property on a class:
class Point { x: number; y: number; } const pt = new Point(); pt.x = 0; pt.y = 0;
As with other locations, the type annotation is optional, but will be an implicit any
if not specified.
“Fields” (typescriptlang.org). Retrieved April 14, 2023.
How can you initialize a field in a class
?
Fields can have initializers; these will run automatically when the class is instantiated:
class Point { x = 0; y = 0; } const pt = new Point(); // Prints 0, 0 console.log(`${pt.x}, ${pt.y}`);
Just like with const
, let
, and var
, the initializer of a class property will be used to infer its type.
Fields can also be initalized in the class constructor.
class Point { x: number; y: number; constructor() { this.x = 0; this.y = 0; } }
“Fields” (typescriptlang.org). Retrieved April 14, 2023.
What do you do if you need to initialize a field externally?
If you intend to definitely initialize a field through means other than the constructor (for example, maybe an external library is filling in part of your class for you), you can use the definite assignment assertion operator, !
:
class OKGreeter { // Not initialized, but no error name!: string; }
“Fields” (typescriptlang.org). Retrieved April 14, 2023.
How can you mark a class field as read-only?
With the readonly
modifier.
Example:
class Greeter { readonly name: string = "world"; . . . }
“readonly” (typescriptlang.org). Retrieved April 17, 2023.
What is a class constructor?
The constructor method is a special method of a class for creating and initializing an object instance of that class
.
“constructor - JavaScript | MDN” (developer.mozilla.org). Retrieved April 17, 2023.
Can you add default values to a class constructor?
Yes
Example:
class Point { x: number; y: number; // Normal signature with defaults constructor(x = 0, y = 0) { this.x = x; this.y = y; } }
“Constructors” (typescriptlang.org). Retrieved April 21, 2023.
Can you overload a class constructor?
Yes, you can overload it same way as you would overload a function signature.
Example:
class Point { // Overloads constructor(x: number, y: string); constructor(s: string); constructor(xs: any, y?: any) { // TBD } }
“Constructors” (typescriptlang.org). Retrieved April 21, 2023.
What are the differences between class constructor signatures and function signatures?
There are just a few differences between class constructor signatures and function signatures:
- Constructors can’t have type parameters - i.e. They can not declare generic types on the constructor signature, these belong on the outer class declaration.
- Constructors can’t have return type annotations - the class instance type is always what’s returned
“Constructors” (typescriptlang.org). Retrieved May 4, 2023.
What are super()
calls?
Just as in JavaScript, if your class is a derived class, you’ll need to call the parent constructor with super
passing along any arguments that were provided in your constructor body before using any this
members.
Example:
class Base { k; constructr(key: number) { this.k = key; } class Derived extends Base { constructor(key: number) { console.log(this.k); // Error 'super' must be called before accessing 'this' in the constructor of a derived class. super(key); } }
“Constructors” (typescriptlang.org). Retrieved April 21, 2023.
What is a method?
A function property on a class
is called a method. Methods can use all the same type annotations as functions and constructors:
class Point { x = 10; y = 10; scale(n: number): void { this.x *= n; this.y *= n; } }
“Methods” (typescriptlang.org). Retrieved April 17, 2023.
How can you add getter and setter accessors to a class?
With the get
and set
accessor keywords:
class C { private _length = 0; get length() { return this._length; } set length(value) { this._length = value; } }
“Getters / Setters” (typescriptlang.org). Retrieved April 18, 2023.
What happens if a class
field as a get
accessor but not set
accessor?
If get
exists but no set
, the property is automatically readonly
“Getters / Setters” (typescriptlang.org). Retrieved April 18, 2023.
Is it possible to have accessors with different types for getting and setting?
Yes, Since TypeScript 4.3, it is possible to have accessors with different types for getting and setting.
class Thing { _size = 0; get size(): number { return this._size; } set size(value: string | number | boolean) { let num = Number(value); // Don't allow NaN, Infinity, etc if (!Number.isFinite(num)) { this._size = 0; return; } this._size = num; } }
“Getters / Setters” (typescriptlang.org). Retrieved April 18, 2023.
Is it possible to declare index signatures inside a class?
Yes, Classes can declare index signatures; these work the same as Index Signatures for other object types:
class MyClass { [s: string]: boolean | ((s: string) => boolean); check(s: string) { return this[s] as boolean; } }
NOTE: Because the index signature type needs to also capture the types of methods, it’s not easy to usefully use these types. Generally it’s better to store indexed data in another place instead of on the class instance itself.
“Index Signatures” (typescriptlang.org). Retrieved April 18, 2023.
How can a class
implement an interface
?
You can use an implements
clause to check that a class satisfies a particular interface. An error will be issued if a class fails to correctly implement it:
interface Pingable { ping(): void; } class Sonar implements Pingable { ping() { console.log("ping!"); } }
“implements Clauses” (typescriptlang.org). Retrieved April 18, 2023.
How can you extend a class
?
Classes may extend from a base class using the extends
clause. A derived class has all the properties and methods of its base class, and also define additional members.
class Animal { move() { console.log("Moving along!"); } } class Dog extends Animal { woof(times: number) { for (let i = 0; i < times; i++) { console.log("woof!"); } } }
“extends Clauses” (typescriptlang.org). Retrieved April 18, 2023.
How can you overwride a parent class method?
You can override a class method by first extending the base class with a derived class and then redefining the method in the derived class. This is done using the extends
keyword.
// Define the base class class Animal { speak(): void { console.log("The animal makes a sound"); } } // Define the derived class that extends the base class class Dog extends Animal { // Override the speak method in the derived class speak(): void { console.log("The dog barks"); } } // Create a new instance of the derived class and call the overridden method const myDog = new Dog(); myDog.speak(); // Output: "The dog barks" // Create a new instance of the base class and call the original method const myAnimal = new Animal(); myAnimal.speak(); // Output: "The animal makes a sound"
“Overriding Methods” (typescriptlang.org). Retrieved April 18, 2023.
What are type-only Field Declarations?
When target >= ES2022
or useDefineForClassFields
is true
, class fields are initialized after the parent class constructor completes, overwriting any value set by the parent class. This can be a problem when you only want to re-declare a more accurate type for an inherited field. To handle these cases, you can write declare
to indicate to TypeScript that there should be no runtime effect for this field declaration.
interface Animal { dateOfBirth: any; } interface Dog extends Animal { breed: any; } class AnimalHouse { resident: Animal; constructor(animal: Animal) { this.resident = animal; } } class DogHouse extends AnimalHouse { // Does not emit JavaScript code, // only ensures the types are correct declare resident: Dog; constructor(dog: Dog) { super(dog); } }
“Type-only Field Declarations” (typescriptlang.org). Retrieved April 19, 2023.
Explain the initialization order of a class in JavaScript
In JavaScript, the initialization order of a class is determined by the order in which its constructor function is executed, and the order in which its properties and methods are defined. Here’s a step-by-step explanation of the initialization order in JavaScript:
1.- Class definition: When the JavaScript engine encounters a class definition, it creates a constructor function for the class.
2.- Property and method definitions: After creating the constructor function, the engine adds the methods and properties defined in the class body to the prototype of the constructor function. This ensures that all instances of the class inherit the properties and methods.
3.- Constructor function execution: When a new instance of the class is created using the new
keyword, the constructor function is executed. The this
keyword inside the constructor refers to the newly created instance.
4.- Property initialization: Inside the constructor, you can initialize instance properties with default values or with the values passed as arguments to the constructor function.
5.- Superclass constructor: If the class extends
another class, the constructor of the superclass (parent class) is called using the super()
function. This should be done before any other statements that reference this
in the derived class constructor, as it ensures that the parent class constructor has been executed and the instance has been properly set up.
“Classes - JavaScript | MDN” (developer.mozilla.org). Retrieved April 19, 2023.
What is the problem with inheriting built-in types in TypeScript if the target is ES6/ES2015
?
Inheriting built-in types (like Array
, Map
, Set
, Error
, etc.) in TypeScript can be problematic when the target is set to ES6/ES2015
. This is because there are certain issues with subclassing built-in types in ES6, particularly when it comes to the constructors and how instances are created.
The main issue arises from how ES6 handles the construction of built-in objects internally. When a built-in object constructor is called with new.target
being a derived class, ES6 expects the derived constructor to return an object with the correct internal slots (low-level data structures) for the built-in object. However, TypeScript does not provide a way to create objects with the correct internal slots for the built-in object.
When you try to subclass a built-in type, you may run into issues like:
1- Incorrect prototype chain: The prototype chain of the instance may not be set up correctly, leading to incorrect behavior when interacting with built-in methods or properties. i.e. instanceof
won’t work.
2.- Inability to create instances: When calling the superclass constructor, it may throw an error or fail to create an instance of the subclass correctly.
For example for a subclass like the following:
class MsgError extends Error { constructor(m: string) { super(m); } sayHello() { return "hello " + this.message; } }
you may find that:
methods may be undefined on objects returned by constructing these subclasses, so calling sayHello
will result in an error.instanceof
will be broken between instances of the subclass and their instances, so (new MsgError()) instanceof MsgError
will return false
.
“Inheriting Built-in Types” (typescriptlang.org). Retrieved April 19, 2023.
How can you “fix” the problem with inheriting built-in types in TypeScript if the target is ES6/ES2015?
You need to fix the prototype chain.
For example in a subclass like the following:
class MsgError extends Error { constructor(m: string) { super(m); } sayHello() { return "hello " + this.message; } }
You need to manually adjust the prototype immediately after any super(...)
calls.
class MsgError extends Error { constructor(m: string) { super(m); // Set the prototype explicitly. Object.setPrototypeOf(this, MsgError.prototype); } sayHello() { return "hello " + this.message; } }
However, any subclass of MsgError
will have to manually set the prototype as well. For runtimes that don’t support Object.setPrototypeOf
, you may instead be able to use \_\_proto\_\_
.
Unfortunately, these workarounds will not work on Internet Explorer 10 and prior. One can manually copy methods from the prototype onto the instance itself (i.e. MsgError.prototype onto this), but the prototype chain itself cannot be fixed.
“Inheriting Built-in Types” (typescriptlang.org). Retrieved April 19, 2023.
What is the default visibility of a class member (field or method)
The default visibility of class members is public
. A public member can be accessed anywhere:
class Greeter { public greet() { console.log("hi!"); } } const g = new Greeter(); g.greet();
Because public
is already the default visibility modifier, you don’t ever need to write it on a class member, but might choose to do so for style/readability reasons.
“Member Visibility” (typescriptlang.org). Retrieved April 20, 2023.
Explain class
Member Visibility
In TypeScript, class member visibility determines the access levels for properties and methods within a class. There are three visibility modifiers: public
, private
, and protected
.
“Member Visibility” (typescriptlang.org). Retrieved April 20, 2023.