阿里云主机折上折
  • 微信号
Current Site:Index > Interface implementation and class type

Interface implementation and class type

Author:Chuan Chen 阅读数:35668人阅读 分类: TypeScript

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

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.