Interface implementation and class type
Interface Implementation and Class Types
In TypeScript, interfaces and class types are core tools for building complex type systems. Interfaces define contracts, and classes implement these contracts. The combination of the two enables the creation of strongly typed and extensible code structures.
Basic Interface Implementation
Classes can implement one or more interfaces using the implements
keyword. When a class implements an interface, it must include all the properties and methods defined in the interface:
interface Animal {
name: string;
makeSound(): void;
}
class Dog implements Animal {
name: string;
constructor(name: string) {
this.name = name;
}
makeSound() {
console.log("Woof!");
}
}
Failing to implement all interface members results in a compilation error:
class Cat implements Animal {
// Error: Property 'makeSound' is missing
name: string;
}
Optional and Readonly Properties
Interfaces can define optional and readonly properties, and implementing classes must adhere to these constraints:
interface User {
readonly id: number;
username: string;
age?: number;
}
class RegisteredUser implements User {
readonly id: number;
username: string;
constructor(id: number, username: string) {
this.id = id;
this.username = username;
}
// age is optional and can be omitted
}
Implementing Multiple Interfaces
A class can implement multiple interfaces, separated by commas:
interface Loggable {
log(): void;
}
interface Serializable {
serialize(): string;
}
class Document implements Loggable, Serializable {
log() {
console.log("Logging document");
}
serialize() {
return "Serialized document";
}
}
Interface Inheritance and Class Implementation
Interfaces can inherit from other interfaces, and implementing classes must satisfy all requirements in the inheritance chain:
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
class MySquare implements Square {
color: string = "red";
sideLength: number = 10;
}
Class Types and Constructor Signatures
Interfaces can describe not only instance structures but also class constructors:
interface ClockConstructor {
new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
tick(): void;
}
function createClock(
ctor: ClockConstructor,
hour: number,
minute: number
): ClockInterface {
return new ctor(hour, minute);
}
class DigitalClock implements ClockInterface {
constructor(h: number, m: number) {}
tick() {
console.log("beep beep");
}
}
class AnalogClock implements ClockInterface {
constructor(h: number, m: number) {}
tick() {
console.log("tick tock");
}
}
let digital = createClock(DigitalClock, 12, 17);
let analog = createClock(AnalogClock, 7, 32);
Static and Instance Parts
In TypeScript, classes have static and instance parts. When implementing an interface, only the instance part is checked:
interface Person {
name: string;
greet(): void;
}
class Employee implements Person {
static company = "ACME";
constructor(public name: string) {}
greet() {
console.log(`Hello, I'm ${this.name}`);
}
}
Abstract Classes and Interfaces
Abstract classes can implement interfaces but don't have to implement all members, leaving some for derived classes to complete:
interface Vehicle {
startEngine(): void;
stopEngine(): void;
}
abstract class Car implements Vehicle {
abstract startEngine(): void;
stopEngine() {
console.log("Engine stopped");
}
}
class SportsCar extends Car {
startEngine() {
console.log("Vroom!");
}
}
Index Signatures in Interfaces
When a class implements an interface with an index signature, it must satisfy the index signature constraints:
interface StringArray {
[index: number]: string;
}
class MyArray implements StringArray {
[index: number]: string;
constructor() {
this[0] = "first";
this[1] = "second";
}
}
Hybrid Type Interfaces
Interfaces can describe types that function as both functions and objects:
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
function getCounter(): Counter {
let counter = <Counter>function (start: number) {
return "Started at " + start;
};
counter.interval = 123;
counter.reset = () => {
console.log("Reset counter");
};
return counter;
}
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
Type Checking for Interfaces and Class Types
TypeScript uses structural subtyping for type checking, considering types compatible as long as their shapes match:
interface Point {
x: number;
y: number;
}
class Point2D {
constructor(public x: number, public y: number) {}
}
let p: Point = new Point2D(1, 2); // Valid
Generic Classes Implementing Generic Interfaces
Generic classes can implement generic interfaces, and type parameters can differ:
interface Pair<T, U> {
first: T;
second: U;
}
class StringNumberPair implements Pair<string, number> {
constructor(public first: string, public second: number) {}
}
class GenericPair<T, U> implements Pair<T, U> {
constructor(public first: T, public second: U) {}
}
Private Members in Interfaces
Starting with TypeScript 3.8, interfaces can declare private fields, but implementing classes must be in the same file:
interface PrivateExample {
#secret: string;
reveal(): string;
}
class SecretKeeper implements PrivateExample {
#secret: string;
constructor(secret: string) {
this.#secret = secret;
}
reveal() {
return this.#secret;
}
}
Advanced Patterns with Interfaces and Class Types
Combining interfaces and class types enables more complex patterns, such as the factory pattern:
interface Product {
operation(): string;
}
abstract class Creator {
public abstract factoryMethod(): Product;
public someOperation(): string {
const product = this.factoryMethod();
return `Creator: ${product.operation()}`;
}
}
class ConcreteProduct1 implements Product {
operation(): string {
return "Result of ConcreteProduct1";
}
}
class ConcreteCreator1 extends Creator {
public factoryMethod(): Product {
return new ConcreteProduct1();
}
}
Interface Merging and Class Implementation
When interfaces merge, classes must implement all members from the merged interface:
interface Box {
height: number;
}
interface Box {
width: number;
}
class CardboardBox implements Box {
height: number = 0;
width: number = 0;
}
Class Expressions Implementing Interfaces
Class expressions can also implement interfaces:
interface Runnable {
run(): void;
}
const MyRunner = class implements Runnable {
run() {
console.log("Running");
}
};
const runner = new MyRunner();
runner.run();
Call and Construct Signatures in Interfaces
Interfaces can include both call and construct signatures:
interface CallOrConstruct {
new (s: string): Date;
(n?: number): number;
}
function getDate(): CallOrConstruct {
function buildDate(s: string) {
return new Date(s);
}
buildDate.now = function(n?: number) {
return n || Date.now();
};
return buildDate as CallOrConstruct;
}
const MyDate = getDate();
const d = new MyDate("2023-01-01");
const timestamp = MyDate();
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn