2. Интерфейсы

Интерфейсы

Интерфейсы в TypeScript типичны для Си-подобных языков и реализованы в виде описания типа. Рассмотрим наглядно на следующем примере.
function printLabel(labelledObj: {label: string}) {
  console.log(labelledObj.label);
}
var myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj); // выведет Size 10 Object
Функция 'printLabel' принимает один объект со свойством 'label' типа string. Фактически объект имеет больше свойств, но компилятор проверяет, чтобы, как минимум, имелись требуемые свойства. Ниже представлен тот же самый пример, используя интерфейс.
interface LabelledValue {
  label: string;
}

function printLabel(labelledObj: LabelledValue) {
  console.log(labelledObj.label);
}

var myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);
В этом примере интерфейс описывает тип, а передаваемый в функцию объект должен удовлетворять интерфейсу. При этом порядок свойств не важен, главное - наличие необходимых свойств и их тип. Оба эти примера компилируются в одинаковый JavaScript код.

Необязательные свойства

Свойства интерфесов могут быть необязательными. Такие свойства обозначаются знаком вопроса. В примере ниже в функцию передается объект, содержащий только одно из описанных в интерфейсе свойство. А функция работает в соответствии с тем, какие из необязательных свойст в нее переданы. 
interface SquareConfig {
  color?: string;
  width?: number;
}

function createSquare(config: SquareConfig): {color: string; area: number} {
  var newSquare = {color: "white", area: 100};
  if (config.color) {
    newSquare.color = config.color;
  }
  if (config.width) {
    newSquare.area = config.width * config.width;
  }
  return newSquare;
}

var mySquare = createSquare({color: "black"});

Тип функция

В JavaScript можно передавать функции в качестве параметоров. Для этого свойство интерфейса TypeScript необходимо описать как функцию без названия и с возвращаемым значением.
interface SearchFunc {
  (source: string, subString: string): boolean;
}
Переменной типа функции можно присвоить значение в виде функции, удовлетворяющей этому интерфейсу.
var mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
  var result = source.search(subString);
  if (result == -1) {
    return false;
  }
  else {
    return true;
  }
}
При этом имена параметров функции не обязательно должны совпадать, но должны совпадать типы параметров и их порядок.

Тип массив

С помощтю интерфейсов также как и тип функцию можно описать тип массив. В этом случае свойство интерфейса описывается как массив, указывается тип индекса и тип элементов массива.
interface StringArray {
  [index: number]: string;
}
var myArray: StringArray;
myArray = ["Bob", "Fred"];
Тип индекса может быть string или number. Возможность поддержки обоих типов индекса ограничивается тем, что тип возвращаемый от числового индекса должен быть подтипом типа возвращаемого строковым индексом.
Описание индекса предоставляет хорошую возможность описывать словари, но при этом все свойства должны соответствовать возвращаемому значению. В следующем примере компилятор выдаст ошибку, потому что свойство length должен быть того же типа, который возвращает массив.
interface Dictionary {
  [index: string]: string;
  length: number;    // error, the type of 'length' is not a subtype of the indexer
} 

Тип класс

Реализация интерфейсов

Также как в языках С# и Java, интерфейсы TypeScript могут указывать на то, что класс должен следовать определенному контракту.
interface ClockInterface {
    currentTime: Date;
}
class Clock implements ClockInterface  {
    currentTime: Date;
    constructor(h: number, m: number) { }
}
При этом в интерфейсе также можно описывать методы.
interface ClockInterface {
    currentTime: Date;
    setTime(d: Date);
}
class Clock implements ClockInterface  {
    currentTime: Date;
    setTime(d: Date) {
        this.currentTime = d;
    }
    constructor(h: number, m: number) { }
}
Интерфейсы описывают публичную часть класса, но не приватную. 

Разница между статической частью класса и экземпляром класса

В этом разделе будет описана особенность работы с классами, которые реализуют интерфейс, в котором в свою очередь описан конструктор для создания экземпляров. Когда класс реализует интерфейс, то класс имеет два типа: тип статической части класса и тип экземпляра класса. При этом конструктор будет находится в статической части, а при проверке типов будет учавстовать экземплар класса. Поэтому если в интерфейсе описан конструктор для создания его экземпларов, то при попытке создать класс, который реализует этот интерфейс, компилятор выдаст ошибку.
interface ClockInterface {
    new (hour: number, minute: number);
}

class Clock implements ClockInterface  {
    currentTime: Date;
    constructor(h: number, m: number) { }
}
Это происходит, потому что в проверке участвует экземпляр класса, а конструктор находится в статической части, и не участвует в проверке. Т.е. компилятор будет видеть, что экземпляр реализует интерфейс и должен содержать конструктор, но не содержит его, т.к. конструктор содержится в статической части.
В общем нужно работать со статической частью напрямую, как в следующем примере.
interface ClockStatic {
    new (hour: number, minute: number);
}

class Clock  {
    currentTime: Date;
    constructor(h: number, m: number) { }
}

var cs: ClockStatic = Clock;
var newClock = new cs(7, 30);

Расширение интерфейсов

Интерфейсы могут расширять друг друга.
interface Shape {
    color: string;
}

interface Square extends Shape {
    sideLength: number;
}

var square = <Square>{};
square.color = "blue";
square.sideLength = 10;
Интерфейс может быть расширен с помощью нескольких интерфейсов.
interface Shape {
    color: string;
}

interface PenStroke {
    penWidth: number;
}

interface Square extends Shape, PenStroke {
    sideLength: number;
}

var square = <Square>{};
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;

Гибридные типы

Из-за гибкой основы JavaScript возможна работа с такими объектами, которые являются комбинацией нескольких типов. С такими объектами можно работать и как с объектом и как с функцией.
interface Counter {
    (start: number): string;
    interval: number;
    reset(): void;
}

var c: Counter;
c(10);
c.reset();
c.interval = 5.0;

Комментариев нет :

Отправить комментарий

Примечание. Отправлять комментарии могут только участники этого блога.