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

Ответы:

156

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

  • extends средства:

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

  • implements средства:

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

Больше ...

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

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

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

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

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

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

class Man extends Human ...

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

class Man extends Human 
          implements Person ...

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

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

Красивое лицо Javascript (одно из преимуществ) - это встроенная поддержка Duck typing ( см. Вики ). Небольшой цитата:

«Если он ходит как утка и крякает как утка, значит, это должна быть утка».

Итак, в 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 implements SelectableControl {
    select() { }
}

class TextBox extends Control {
    select() { }
}

// Error: Property 'state' is missing in type 'Image'.
class Image implements SelectableControl {
    private state: any;
    select() { }
}

class Location {

}

Так что пока

  • extends означает - он получает все от своего родителя
  • implementsв этом случае почти как реализация интерфейса. Дочерний объект может притвориться родительским ... но не получает никакой реализации
Радим Кёлер
источник
когда вы говорите " extends- он получает все от своего родителя", применяется ли это к закрытым членам? Например class Person {private name: string} class man extends Person{gender: string;}, manесть название свойства?
davejoem
Частные тоже есть. Просто недоступно для TS. Сделайте их защищенными, и вы сможете их использовать. В случае «орудий» имеет смысл только публичная часть. Надеюсь, это немного поможет
Радим Кёлер
Отличный ответ. Просто не уверен, что ваш комментарий к "частному есть, но не доступен TS". Вы имеете в виду, что частные свойства копируются во вновь созданный дочерний объект? А в случае с орудиями копируются только общедоступные свойства?
kushalvm
Кроме того, я нашел еще один момент. Если это определение extends. Тогда, пожалуйста, не могли бы вы объяснить это stackoverflow.com/questions/60390454/…
kushalvm
99

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

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

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

Экземпляры, реализующие этот Pointинтерфейс, должны иметь два члена типа number: 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 {}

Имеется ошибка компиляции:

Класс Man неправильно реализует интерфейс Person.
В типе Man отсутствует свойство name.

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

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

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

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

Ницан Томер
источник
6

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

IPoint interface;
Point implements IPoint;
Point3D extends Point;

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

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

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

Andzep
источник
Это не дает ответа на вопрос. Чтобы критиковать или запросить разъяснения у автора, оставьте комментарий под его сообщением. - Из
отзыва
1
@aronisstav Я только опубликовал расширенную демонстрацию того, что нашел хороший ответ, который мне уже помог. Но, возможно, кто-то другой сочтет мою работу по расширению демо полезной. Вот и все. Комментарии на самом деле не предназначены для размещения блока кода, поэтому я считаю его более понятным в ответном сообщении. Так в чем тут проблема?
andzep 07
Ваш ответ был (автоматически?) Помечен из-за длины и содержания, появился в моей очереди на рассмотрение, и я оценил причины, указанные во флаге. Его основной вклад (объяснение того, что вы расширили демоверсию) был бы лучше в качестве комментария. С добавленным абзацем, возможно, это действительно более полезно.
aronisstav 07
@andzep ваш расширенный демонстрационный пример действительно полезен.
Намить
3
  1. Интерфейс расширяет интерфейс с помощью формы
  2. Интерфейс расширяет класс с помощью формы
  3. Интерфейс реализации класса должен реализовывать все поля, предоставляемые интерфейсом
  4. Класс реализует класс с формой
  5. Класс расширяет класс всеми полями

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

лей ли
источник
0

Расширяет инструменты 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)

Виллем ван дер Вин
источник