The relationship between classes and interfaces
In TypeScript, classes and interfaces are core tools for building complex type systems. Classes provide concrete implementations, while interfaces define behavioral contracts. The two collaborate in various ways to achieve code abstraction and reuse.
Class Definition and Implementation
Classes are the foundation of object-oriented programming, used to describe the properties and methods of objects. TypeScript classes support features like inheritance, encapsulation, and polymorphism. For example:
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
move(distance: number = 0) {
console.log(`${this.name} moved ${distance}m.`);
}
}
Basic Concepts of Interfaces
Interfaces are purely tools for type definitions and contain no concrete implementations. They describe the shape an object should have:
interface Movable {
move(distance: number): void;
}
Classes Implementing Interfaces
Classes can implement one or more interfaces using the implements
keyword, enforcing that the class must satisfy the contract defined by the interface:
class Car implements Movable {
move(distance: number) {
console.log(`Car moved ${distance}km.`);
}
}
Interfaces Extending Classes
TypeScript allows interfaces to inherit member definitions (excluding implementations) from classes, which is useful for reusing class structures:
class Point {
x: number;
y: number;
}
interface Point3D extends Point {
z: number;
}
const point: Point3D = { x: 1, y: 2, z: 3 };
Similarities and Differences Between Classes and Interfaces
Feature | Classes | Interfaces |
---|---|---|
Implementation | Contains concrete implementations | Only type definitions |
Runtime presence | Yes | No |
Instantiable | Yes | No |
Inheritance | Single inheritance | Multiple inheritance |
Special Cases When Interfaces Extend Classes
When an interface extends a class with private or protected members, only subclasses of that class can implement the interface:
class Control {
private state: any;
}
interface SelectableControl extends Control {
select(): void;
}
class Button extends Control implements SelectableControl {
select() {}
}
// Error: Missing private member 'state'
class TextBox implements SelectableControl {
select() {}
}
Hybrid Type Interfaces
Interfaces can describe combinations of functions, objects, and index signatures, offering flexibility for complex scenarios:
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
function getCounter(): Counter {
let counter = function(start: number) {} as Counter;
counter.interval = 123;
counter.reset = function() {};
return counter;
}
Comparison Between Abstract Classes and Interfaces
Abstract classes sit between regular classes and interfaces, allowing partial implementations:
abstract class Department {
constructor(public name: string) {}
abstract printMeeting(): void;
}
class AccountingDepartment extends Department {
printMeeting() {
console.log('Accounting meeting');
}
}
Interface Merging Feature
Interfaces with the same name automatically merge, which is common when extending third-party types:
interface Box {
height: number;
}
interface Box {
width: number;
}
// The final Box interface includes both height and width
const box: Box = { height: 1, width: 2 };
Difference Between Static and Instance Parts of Classes
Classes have static and instance parts, while interfaces can only constrain the instance part:
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);
}
Using Interfaces to Describe Function Types
Interfaces can describe function signatures, which is particularly useful for defining callbacks:
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc = function(src, sub) {
return src.search(sub) > -1;
};
Indexable Type Interfaces
Interfaces can describe indexable types like arrays and dictionaries:
interface StringArray {
[index: number]: string;
}
let myArray: StringArray = ['Bob', 'Fred'];
Differences Between Interfaces and Type Aliases
Although interfaces and type aliases can sometimes be interchangeable, they have fundamental differences:
type Alias = { num: number }
interface Interface {
num: number;
}
// Type aliases cannot be extended or implemented
class A implements Interface {
num = 1;
}
Interfaces Describing Constructors
Using special constructor signatures, interfaces can describe constructors:
interface SomeConstructor {
new (s: string): SomeInterface;
}
interface SomeInterface {
doSomething(): void;
}
function createInstance(ctor: SomeConstructor, s: string): SomeInterface {
return new ctor(s);
}
Readonly Properties in Interfaces
Interfaces can define readonly properties to enhance type safety:
interface Point {
readonly x: number;
readonly y: number;
}
let p1: Point = { x: 10, y: 20 };
p1.x = 5; // Error!
Classes Implementing Multiple Interfaces
TypeScript supports a class implementing multiple interfaces, offering more flexibility than multiple inheritance:
interface Flyable {
fly(): void;
}
interface Swimmable {
swim(): void;
}
class Duck implements Flyable, Swimmable {
fly() { console.log('Flying'); }
swim() { console.log('Swimming'); }
}
Optional Properties in Interfaces
Interface properties can be marked as optional for added flexibility:
interface SquareConfig {
color?: string;
width?: number;
}
function createSquare(config: SquareConfig) {
// ...
}
Interfaces Describing Complex Objects
Interfaces can describe complex object structures with multiple types:
interface NestedObject {
id: string;
metadata: {
createdAt: Date;
updatedAt?: Date;
tags: string[];
};
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn