Простое руководство по интерфейсам Typescript: объявление и варианты использования

TypeScript Программирование и разработка

TypeScript

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

В TypeScript интерфейс — это абстрактный тип, который сообщает компилятору, какие имена свойств могут иметь данный объект. TypeScript создаёт неявные интерфейсы, когда вы определяете объект со свойствами. Он начинается с просмотра имени свойства объекта и типа данных с использованием возможностей вывода типов TypeScript.

Сегодня вы узнаете, как создавать интерфейсы и работать с ними в TypeScript, чтобы улучшить функциональность ваших проектов. Мы подробно познакомим вас с концепцией и примерами использования.

Объявление интерфейсов

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

Интерфейсы гарантируют, что всё реализовано должным образом.

Интерфейсы могут быть описаны явно или они могут быть неявно созданы компилятором, поскольку он считывает объекты в коде. Мы объявляем интерфейс, используя interface ключевое слово в.ts файле. В следующем примере показан пример синтаксиса:

interface Dimension {
    width: string;
    height: string;
}

Здесь мы определили интерфейс со свойствами width и height, которые являются строками. Теперь мы можем реализовать интерфейс:

interface Dimension {
    width: string;
    height: string;
}

let _imagedim: Dimension = {
    width: "200px",
    height: "300px"
};

Посмотрим на другой пример. Здесь мы объявляем интерфейс с именем Userс interface ключевым словом. Свойства и методы, составляющие интерфейс, добавляются в фигурные скобки в виде key:valueпар, точно так же, как мы добавляем члены к простому объекту JavaScript.

interface User {
  id: number;
  firstName: string;
  lastName: string;
  getFullName(): string;
}

Мы можем использовать наш недавно созданный интерфейс в коде, добавив аннотацию к соответствующим объектам. Например, вот как создать новый объект пользователя с пользовательским интерфейсом:

const user: User = {
  id: 12,
  firstName: "Josh",
  lastName: "",
  getFullName: () => `${firstName} ${lastName}`
};

Благодаря этому наш компилятор знает, какими свойствами должен обладать пользовательский объект. Если свойство отсутствует или если его значение не того типа, который указан в интерфейсе, компилятор выдаст ошибку.

Интерфейс реализации класса

Мы можем использовать интерфейс с классом, используя ключевое слово implements, например:

class NameofClass implements InterfaceName {
}

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

interface Size {
    width : string,
    height: string,
    getWidth(): string; 
}

class Shapes implements Size {
    width: string;
    height: string;
    constructor (width:string, height:string) {
        this.width = width;
        this.height = height;
    }
    getWidth() {
        return this.width;
    }
}

Работа с интерфейсами

Теперь, когда мы знаем, как объявлять интерфейсы, давайте посмотрим на них в действии для нескольких распространённых случаев использования.

Указание дополнительных свойств

В предыдущем примере кода все свойства интерфейса являются обязательными. Если мы создадим новый объект с этим интерфейсом и оставим свойство, компилятор TypeScript выдаст ошибку.

Однако есть определённые случаи, когда мы ожидаем, что наши объекты будут иметь необязательные свойства. Этого можно добиться, поместив вопросительный знак ( ?) непосредственно перед аннотацией типа свойства при объявлении интерфейса:

interface Post {
  title: string;
  content: string;
  tags?: string[];
}

В этом конкретном сценарии мы сообщаем компилятору, что можно иметь объекты Post без тегов.

Такое описание интерфейсов очень полезно, особенно если вы хотите предотвратить использование свойств, не включённых в интерфейс.

const validPostObj: Post {
  title: 'Post title',
  content: 'Some content for our post',
};

const invalidPostObj: Post = {
  title: 'Invalid post',
  content: 'Hello',
  meta: 'post description', // this will throw an error
  /* Type '{ title: string; content: string; meta: string; }' is not assignable to type 'Post'.

    Object literal may only specify known properties, and 'meta' does not exist in type 'Post'.
  */
};

Указание свойств только для чтения

А также можно пометить свойство в интерфейсе как доступное только для чтения. Мы можем сделать это, добавив readonlyключевое слово перед ключом свойства:

interface FulfilledOrder {
  itemsInOrder: number;
  totalCost: number;
  readonly dateCreated: Date;
}

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

const order: FulfilledOrder = {
 itemsInOrder: 1,
 totalCost: 199.99,
 dateCreated: new Date(),
};

order.dateCreated = new Date(2021, 10, 29);

Это поведение аналогично тому, что происходит, когда вы пытаетесь переназначить значение переменной, объявленной с constключевым словом в JavaScript.

Типы функций и классов

Интерфейсы также можно использовать для описания типов функций и для проверки структуры классов JavaScript.

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

interface SumFunc {
  (a: number, b: number): number;
}

const add: SumFunc = (a, b) => {
  return a + b;
}

Примечание: нам не нужно было указывать тип параметра при создании нашей функции.

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

Мы также можем использовать интерфейсы для правильного ввода классов, созданных в JavaScript. Для этого мы создаём интерфейс, который содержит свойства и методы класса, а затем используем ключевое слово реализации при создании нашего класса.

interface CarInterface {
  model: string;
  year: string;
  getUnitsSold(): number;
}

class Car implements CarInterface {
  model: string;
  year: string;
  getUnitsSold() {
    // logic to return number of units sold
    return 100;
  }

  constructor(model: string, year: string) {
    this.model = model;
    this.year = year;
  }
}

Примечание. Наши интерфейсы описывают публичную сторону класса, а не публичную и частную стороны.

Общие интерфейсы

Иногда вы можете не знать, какой тип данных будет содержать каждое свойство в вашем интерфейсе. Простым решением было бы просто добавить к таким свойствам любой тип. Однако это сделает наш код менее типобезопасным.

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

Эти параметры могут быть заменены типом или другим интерфейсом позже в вашем коде.

Мы можем создать общий интерфейс, передав параметры типа с помощью угловых скобок ( <>) в наш интерфейс.

interface PaginatedResponse<T> {
 data: T[];
 nextPageUrl?: string;
 previousPageUrl?: string;
}

interface Post {
 title: string;
 content: string;
 tags?: string[];
}

function loadDataFromApi() {
 fetch('/some/api/endpoint')
   .then((response) => response.json())
   .then((data: PaginatedResponse<Post>) => console.log(data));
}

В приведённом выше примере создаётся PaginatedResponseинтерфейс, который мы можем повторно использовать для ввода данных, возвращаемых из источника API. Это можно повторно использовать в вашем коде для разных ресурсов API с той же базовой структурой.

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

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

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

interface PetsInterface {
  name: string;  
}
interface DogInterface extends PetsInterface {
  breed: string;
}
interface CatInterface extends PetsInterface {
  breed: string;
}

Чтобы реализовать как наш, так Dogи Catинтерфейсы, мы также реализуем Petsинтерфейс.

interface PetsInterface {
  name: string;  
}
interface DogInterface extends PetsInterface {
  breed: string;
}
interface CatInterface extends PetsInterface {
  breed: string;
}
class Cat implements CatInterface {
  name: string = 'Garfield';
  breed: string = 'Tabby';
}
class Dog implements DogInterface {
  name: string = 'Rover';
  breed: string = 'Poodle';
}

Заключение

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

Некоторые из этих функций, которые вам следует изучить дальше, включают:

  • Utility Types.
  • Union and Intersection Types.
  • Enums.
  • Type Guards.
  • Type Aliases.
Читайте также:  PHP или Python: сравнение, что лучше
Оцените статью
bestprogrammer.ru
Добавить комментарий