В чем разница между "расширяет" и "реализует" в TypeScript

Я хотел бы знать, что общего между мужчиной и ребенком и чем они отличаются.

class Person {
  name: string;
  age: number;
}
class child extends Person {}
class man implements Person {}

7 ответов

Решение

Укороченная версия

  • extends средства:

Новый класс - ребенок. Это получает выгоду от наследства. У него есть все свойства, методы в качестве его родителя. Он может переопределить некоторые из них и реализовать новое, но родительский материал уже включен.

  • implements средства:

Новый класс можно рассматривать как одну и ту же "форму", пока он не дочерний. Это может быть передано любому методу, где Person требуется, независимо от наличия другого родителя, чем Person

Больше...

В ООП (языки как C#, Java) мы использовали бы

extends получить прибыль от наследства (см. вики). Небольшая ссылка:

... Наследование в большинстве объектно-ориентированных языков на основе классов - это механизм, при котором один объект приобретает все свойства и поведение родительского объекта. Наследование позволяет программистам: создавать классы, основанные на существующих классах...

implements будет больше для полиморфизма (см. вики). Небольшая ссылка:

... полиморфизм - это предоставление единого интерфейса сущностям разных типов...

Таким образом, мы можем иметь действительно другое дерево наследования нашего class Man,

class Man extends Human ...

но если мы также объявим, что мы можем притвориться, что другой тип - Person:

class Man extends Human 
          implements Person ...

.. тогда мы можем использовать его где угодно, где Person необходимо. Мы просто должны выполнить Персонал "interface" (т.е. реализовать все свои публичные вещи).

implement другой класс? Это действительно классная штука

Javascript "приятное лицо" (одно из преимуществ) - встроенная поддержка печати по утке ( см. Вики). Небольшая ссылка:

"Если он ходит как утка и крякает как утка, значит, это утка".

Так, в Javascript, если два разных объекта... будет иметь один похожий метод (например, render() ) они могут быть переданы функции, которая ожидает этого:

function(engine){
  engine.render() // any type implementing render() can be passed
}

Чтобы не потерять это - мы можем в Typescript сделать то же самое - с более типизированной поддержкой. И это где

class implements class

имеет свою роль, где это имеет смысл

На ООП языках как C# ... нет способа сделать это...

Также документация должна помочь здесь:

Интерфейсы, расширяющие классы

Когда тип интерфейса расширяет тип класса, он наследует членов класса, но не их реализации. Это как если бы интерфейс объявил всех членов класса без предоставления реализации. Интерфейсы наследуют даже частные и защищенные члены базового класса. Это означает, что когда вы создаете интерфейс, который расширяет класс частными или защищенными членами, этот тип интерфейса может быть реализован только этим классом или его подклассом.

Это полезно, когда у вас большая иерархия наследования, но вы хотите указать, что ваш код работает только с подклассами, которые имеют определенные свойства. Подклассы не должны быть связаны кроме наследования от базового класса. Например:

class Control {
    private state: any;
}

interface SelectableControl extends Control {
    select(): void;
}

class Button extends Control {
    select() { }
}

class TextBox extends Control {
    select() { }
}

class Image extends Control {
}

class Location {
    select() { }
}

Так что пока

  • extends значит - он получает все от своего родителя
  • implements в этом случае это почти как реализация интерфейса. Дочерний объект может притвориться, что он является родителем... но он не получает никакой реализации

В машинописи (и некоторых других языках OO) у вас есть классы и интерфейсы.

Интерфейс не имеет реализации, это просто "контракт" на то, какие члены / методы имеют этот тип.
Например:

interface Point {
    x: number;
    y: number;
    distance(other: Point): number;
}

Экземпляры, которые реализуют это Point Интерфейс должен иметь два члена типа номер: x а также yи один метод distance который получает другой Point экземпляр и возвращает number,
Интерфейс не реализует ничего из этого.

Классы являются реализациями:

class PointImplementation implements Point {
    public x: number;
    public y: number;

    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }

    public distance(other: Point): number {
        return Math.sqrt(Math.pow(this.x - other.x, 2) + Math.pow(this.y - other.y, 2));
    }
}

( код на детской площадке)

В вашем примере вы относитесь к Person Класс один раз как класс, когда вы расширяете его, и один раз как интерфейс, когда вы реализуете его.
Ваш код:

class Person {
    name: string;
    age: number;
}
class Child  extends Person {}

class Man implements Person {}

Ошибка компиляции:

Класс "Человек" неправильно реализует интерфейс "Человек".
Свойство name отсутствует в типе Man.

И это потому, что интерфейсы не имеют реализации.
Так что если вы implement класс, тогда вы только берете его "контракт" без реализации, поэтому вам нужно сделать это:

class NoErrorMan implements Person {
    name: string;
    age: number;
}

( код на детской площадке)

Суть в том, что в большинстве случаев вы хотите extend другой класс и не implement Это.

Расширяет инструменты VS

  • extends: Дочерний класс (который расширяется) наследует все свойства и методы класса, который расширяется.
  • implements: Класс, который использует implementsключевое слово потребуется для реализации всех свойств и методов класса, который онimplements

Проще говоря:

  • extends: Здесь вы получаете все эти методы / свойства из родительского класса, поэтому вам не нужно реализовывать это самостоятельно
  • implements: Вот договор, которому должен следовать класс. Класс должен реализовывать как минимум следующие методы / свойства

Пример:

class Person {
  name: string;
  age: number;

  walk(): void {
    console.log('Walking (person Class)')
  }

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}
class child extends Person { }

// Man has to implements at least all the properties
// and methods of the Person class
class man implements Person {
  name: string;
  age: number

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  walk(): void {
    console.log('Walking (man class)')
  }

}

(new child('Mike', 12)).walk();
// logs: Walking(person Class)

(new man('Tom', 12)).walk();
// logs: Walking(man class)

В этом примере мы можем заметить, что дочерний класс наследует все от Person, тогда как класс man должен реализовать все от самого Person.

Если бы мы удалили что-то из класса man, например метод walk, мы получили бы следующую ошибку времени компиляции:

Класс man неправильно реализует класс Person. Вы хотели расширить "Person" и унаследовать его члены как подкласс? Свойство walk отсутствует в типе man, но обязательно в типе Person.(2720)

Отличный ответ от @nitzan-tomer! Мне очень помогло... Я немного расширил его демо:

IPoint interface;
Point implements IPoint;
Point3D extends Point;

И как они ведут себя в функциях, ожидающих тип IPoint.

Итак, что я выучил до сих пор и использовал как правило большого пальца: если вы используете классы и методы, ожидающие универсальные типы, используйте интерфейсы в качестве ожидаемых типов. И убедитесь, что родительский или базовый класс использует этот интерфейс. Таким образом, вы можете использовать все подклассы в тех, которые реализуют интерфейс.

Hier расширенная демонстрация

По сути:

  • extends получит все свойства и методы родительского класса.
  • implements обяжет нас реализовать все свойства и методы, определенные в интерфейсе.
  1. Интерфейс расширяет интерфейс с помощью формы
  2. Интерфейс расширяет класс формой
  3. Класс реализует интерфейс должен реализовывать все поля, предоставляемые интерфейсом
  4. Класс реализует класс с формой
  5. Класс расширяет класс всеми полями

extends сосредоточиться на наследовать и implements сосредоточиться на ограничении, будь то интерфейсы или классы.

Интерфейсы помогают уменьшить связь между классами, предоставляя контракт или общий язык, который определяет, как разные классы могут взаимодействовать друг с другом, обеспечивая слабую связь и облегчая обслуживание и расширяемость (программирование для интерфейса). Использование интерфейсов вместо наследования сводит к минимуму зависимости между классами, что приводит к уменьшению связанности кода. Такое разделение повышает возможность повторного использования кода, тестируемость и общую гибкость системы.

Другие вопросы по тегам