Capítulo 13 de 36
this, and Abstract ClassesBeyond instance members, classes carry a second, separate type surface for static members, a dynamic this type that tracks subclasses automatically, and abstract as the mechanism for base classes that can never be instantiated directly.
static members: attached to the constructor object itself (MyClass.x), accessed without new. Support the same public/protected/private modifiers and are inherited by subclasses. A few names (name, length, call, …) are reserved because classes are themselves callable Function objects and would collide with the Function prototype.static blocks: a static { ... } block runs once at class-definition time with its own scope, and can read/write the class's private/#-prefixed static fields — useful for initialization logic (e.g. reading from an external source) that's too complex for a single field initializer expression.class Box<Type> { contents: Type } — type parameters infer from new Box("hello") the same way they infer in a generic function call. A generic class's static members can never reference the class's type parameter — there's only one underlying static property slot at runtime, shared across every instantiation (Box<string> and Box<number> would otherwise silently share and clobber the same static field).this at runtime is call-site-dependent, same as plain JS — extracting a method off an instance (const g = c.getName) and calling it loses the original this binding. Two TS-level mitigations:
getName = () => this.name): guarantees correct this even from un-checked JS callers, at the cost of one function allocation per instance and losing access to super.getName in a subclass (arrow functions have no entry in the prototype chain to look it up from).this parameter (getName(this: MyClass) { ... }): a compile-time-only declared parameter (erased on emit) that makes calling the method with a wrong this a type error at the call site — one function per class (not per instance), and super calls still work, but only protects TS-checked callers.this as a return/parameter type: a method returning this (inferred automatically for fluent/chainable APIs like .set(value) { ...; return this }) automatically returns the subclass's type when called on a subclass instance — more precise than hardcoding the base class name as the return type. Using this as a parameter type (sameAs(other: this)) similarly restricts the argument to instances of the same (sub)class, not just the base.this is Type type guards: a class method can narrow this the same way a free function narrows a parameter with x is Type — isFile(): this is FileRep. A common use is lazy-validated optional fields: hasValue(): this is { value: T } strips undefined from value's type inside the narrowed branch.public, private, protected, or readonly) both declares a field of that name/type/visibility and assigns it from the argument — constructor(public readonly x: number, private y: number) needs no body at all for that assignment.const C = class<Type> { ... }), generic-capable just like class declarations.InstanceType<typeof SomeClass>: the utility type for "the type of an instance produced by this class," derived from the class's own constructor type via typeof.abstract classes/members: an abstract class can't be instantiated with new directly; abstract methods/fields declare a signature with no implementation, and every concrete subclass must implement them (a missing implementation is a compile error). To accept "a constructor that produces a (subclass of) some abstract class" as a parameter — a common factory pattern — type the parameter as a construct signature (new () => Base), not typeof Base: typeof Base would also (wrongly) accept the abstract class itself, which can't actually be new'd.extends. An empty class (class Empty {}) has no members to check, so — in a structural system — it becomes a supertype of nearly everything, meaning almost any value satisfies it. This is presented as a cautionary "don't do this," not a recommended pattern.class Box {
contents = "";
set(value: string) {
this.contents = value;
return this; // inferred return type: `this`, not `Box`
}
}
class ClearableBox extends Box {
clear() { this.contents = ""; }
}
const b = new ClearableBox().set("hi"); // b: ClearableBox, not Box
this return type automatically tracking the actual subclass, enabling type-safe method chaining across an inheritance hierarchy.abstract class Base {
abstract getName(): string;
printName() { console.log("Hello, " + this.getName()); }
}
class Derived extends Base {
getName() { return "world"; }
}
function greet(ctor: new () => Base) { // construct signature, NOT `typeof Base`
new ctor().printName();
}
greet(Derived); // OK
greet(Base); // Error — Base is abstract, can't be `new`'d
this-safety technique | this correctness | Memory | super access |
|---|---|---|---|
| Arrow function property | guaranteed, even for untyped JS callers | one closure per instance | not available |
this: T parameter | enforced only for TS-checked call sites | one function per class | available |
static members a type depending on the class's own type parameter — there's exactly one shared static slot at runtime across every instantiation.new () => T), not typeof AbstractClass, when a function needs "a constructor for T" but must reject abstract bases.this as a chainable method's return type over hardcoding the base class name — it keeps fluent APIs correct across subclasses for free.this-parameter trade-off deliberately: arrow properties are safer against untyped callers but cost memory-per-instance and lose super; this parameters are cheaper and keep super but only protect TS-checked call sites.this is Type is the class-method form of the free-function type predicate.InstanceType<T> is the general-purpose version of the class-to-instance-type mapping shown here.