Изучение TypeScript — полное руководство для начинающих. Часть 3 — Классы и интерфейсы

—

от автора

Всем привет! Меня зовут Лихопой Кирилл, я работаю fullstack-разработчиком. Это — уже третья часть руководства по TypeScript для начинающих, в которой мы разберем более сложные темы, такие как классы, модули и интерфейсы.

Предыдущие части:
Часть 1 — введение и примитивные типы данных
Часть 2 — ссылочные типы данных

Классы в TypeScript

Мы можем определить типы для любых данных, которые будут в классе:

class Person { name: string; isCool: boolean;     friends: number;      constructor(n: string, c: boolean, f: number) {     this.name = n;     this.isCool = c;     this.friends = f;     }      sayHello() {         return `Привет, меня зовут ${this.name}, у меня есть ${this.friends} друзей`     } }  const person1 = new Person('Денис', false, 10); const person2 = new Person('Вова', 'да', 6); // ОШИБКА: Аргумент типа 'string' не может быть присвоен параметру с типом 'boolean' console.log(person1.sayHello()); // Привет, меня зовут Денис, у меня есть 10 друзей 

Затем мы можем создать массив people , в котором содержаться экземпляры класса Person :

let People: Person[] = [person1, person2];

Мы можем добавить модификаторы доступа к свойствам класса. TypeScript также предоставляет новый модификатора доступа readonly (мы говорили о нем в прошлой части):

class Person { readonly name: string; // это свойство неизменно - его можно только прочитать private isCool: boolean; // можно прочитать и изменять только в пределах этого класса protected email: string; // можно прочитать и изменить только из класса и наследуемых от него public friends: number; // можно прочитать и изменить откуда угодно, даже вне класса        constructor(n: string, c: boolean, e: string, f: number) {     this.name = n;     this.isCool = c;     this.email = e;     this.friends = f;     }      sayMyName() {     console.log(`Ты не Хайзенберг, ты ${this.name}`);     } }  const person1 = new Person('Менделеев', false, 'men@de.ru', 118); console.log(person.name); // все в порядке person1.name = 'Хайзенберг'; // ОШИБКА: только для чтения console.log(person1.isCool); // ОШИБКА: private свойство - доступ есть только в пределах класса Person console.log(person1.email); // ОШИБКА: protected свойство - доступ есть только в пределах класса Person и его наследниках console.log(person1.friends); // public свойство - никаких проблем

Мы можем сделать наш код более лаконичным, если опишем свойства класс следующим образом:

class Person { constructor( readonly name: string, private isCool: boolean, protected email: string, public friends: number ){}        sayMyName() {     console.log(`Ты не Хайзенберг, ты ${this.name}`);     } }  const person1 = new Person('Менделеев', false, 'men@de.ru', 118); console.log(person.name); // Менделеев

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

Обратите внимание — когда мы пропускаем модификатор доступа, то по умолчанию свойство будет public.

Классы могут наследоваться, так же, как и в обычном JavaScript:

class Programmer extends Person { programmingLanguages: string[];        constructor(     name: string,     isCool: boolean,     email: string,     friends: number,     pL: string[]     ){     // Вызов super должен содержать все параметры базового класса (Person), т.к. конструктор не наследуется      super(name, isCool, email, friends);     this.programmingLanguages = pl;     } }

Больше о классах вы можете узнать на официальном сайте TypeScript.

Модули в TypeScript

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

TypeScript так же имеет поддержку модулей. Файлы TypeScript будут компилироваться в отдельные JavaScript файлы.

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

"target": "es2016", "module": "es2015"

(Несмотря на то, что для Node-проекта вам, вероятно, очень хочется добавить "module":"CommonJS" , это не сработает, т.к. Node еще не поддерживает современные экспорт и импорт.)

Теперь, добавьте атрибут type="module" в тег скрипта в вашем HTML-файле:

<script type="module" src="/public/script.js"></script>

Теперь мы можем импортировать и экспортировать файлы с помощью ES6:

// src/hello.ts export function sayHi() { console.log('Всем привет!'); }  // src/script.ts import { sayHi } from './hello.js'; syaHi(); // Всем привет!

Обратите внимание: всегда импортируйте файл с разрешением .js, даже если это TypeScript-файл.

Интерфейсы в TypeScript

Интерфейсы объявляются как объекты и выглядят следующим образом:

interface Person {   name: string;   age: number; }  function sayHi(person: Person) {   console.log(Привет, ${person.name}); }  sayHi({   name: 'Джон',   age: 33, }); // Привет, Джон

Вы также можете объявлять их как объекты, используя type(псевдоним типа):

type Person = { name: string; age: number; }  function sayHi(person: Person) {     console.log(Привет, ${person.name}); }  sayHi({     name: 'Джон',     age: 33, }); // Привет, Джон

А еще тип объекта может быть указан анонимно, прямо в параметрах функции:

function sayHi(person: { name: string; age: number }) {     console.log(`Привет, ${person.name}`); }  sayHi({     name: 'Джон',     age: 33, }); // Привет, Джон

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

Следующие примеры взяты из документации TypeScript.

Наследование интерфейса:

interface Animal { name: string }  interface Bear extends Animal {   honey: boolean }  const bear: Bear = {   name: 'Винни',   honey: false, }

Изучение TypeScript — полное руководство для начинающих. Часть 3 — Классы и интерфейсы. типов:

type Animal = {   name: string }  type Bear = Animal & {   honey: boolean }  const bear: Bear = {   name: 'Винни',   honey: true, }

Добавление новых полей к существующему интерфейсу:

interface Animal {   name: string }  // Добавление поля к интерфейсу interface Animal {   tail: boolean }  const dog: Animal = {   name: 'Хатико',   tail: true, }

А вот и разница: типы не могут изменены после объявления:

type Animal = {   name: string }  type Animal = {   tail: boolean } // ОШИБКА: Дублирующийся идентификатор 'Animal'.

Документация TypeScript советует использовать интерфейсы для объявления объектов, если вам не требуется использовать возможности типов.

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

interface Person { name: string age: number speak(sentence: string): void }  const person1: Person = {   name: 'Джон',   age: 33,   speak: sentence => console.log(sentence), }

У вас мог возникнуть вопрос: почему мы используем интерфейсы вместо классов в примере выше?

Во-первых, интерфейсы используются только в TypeScript, не в JavaScript. Это значит, что они не будут скомпилированы, и соответственно не будут раздувать наш JavaScript-файл. А так как классы используются в JavaScript, то они будут скомпилированы.

Во-вторых, класс это, по сути своей, фабрика объектов (то есть это “план” того, как должен выглядеть объект), в то время как интерфейсы используются исключительно для проверки типов.

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

Интерфейсы с классами

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

interface HasFormatter { form(): string; }  class Person implements HasFormatter {   constructor(public username: string, protected password: string) {}      format() {   return  this.username.toLocaleLowerCase();   } }  // Должно быть объектом, который реализует интерфейс HasFormatter let person1: HasFormatter; let person2: HasFormatter; person1 = new Person('Денис', 'password123'); person2 = new Person('Женя', 'TypeScripter999'); console.log(person1.format()); // Денис

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

P.S. Автор искренне извиняется за большой перерыв между второй и третью частями, который связан с огромными переменами в жизни (в т.ч. релокейт в Казахстан)


ссылка на оригинал статьи https://habr.com/ru/post/707990/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *