Skip to content

Latest commit

 

History

History
220 lines (193 loc) · 8.17 KB

TypeScript.md

File metadata and controls

220 lines (193 loc) · 8.17 KB

Типы данных

Примитивные типы

  • booleanлогическое значение (true, false).
  • numberчисловое значение (-1, 3.9).
  • stringстроковое значение ("notes", '123').
  • nullспециальное значение null.
  • undefinedспециальное значение undefined.
  • symbolсимвол.
const symbol = Symbol('key');
const obj = {
    [symbol]: 'value'
};
console.log(obj[sym]); // "value"
console.log(Symbol('key') === symbol); // false
console.log(obj[Symbol('key')]) // undefined

Остальные типы

  • Arrayмассив (number[]).
let foo: number[];
foo = [1, 2, 3];
let bar: Array<string>;
bar = ['n', 'o', 't', 'e', 's'];
  • anyпроизвольный тип (используется по умолчанию, если тип не указан).
let foo: any;
foo = 1;
foo = '';
  • voidотсутствие конкретного значения (обычно возвращаемый тип функции).
const fn = (param: string): void => {
  console.log(param);
  // return отсутствует
}
  • neverзначение, которое никогда не наступит (обычно функции, возвращающие ошибку)
const createError = (message: string): never {
  throw new Error(message);
};
  • Typleкортеж ([string, number]).
let foo: [string, number, boolean];
foo = ['notes', 17, true];
/* порядок важен */
foo = [17, 'notes', true]; // error
  • Enumперечисление (более дружелюбные имена для множества числовых значений).
enum Visibility { Visible, Hidden }
const state: Visibility = Visibility.Visible;

object vs Object vs {}

  • objectнепримитивный тип (non-primitive); любой тип, кроме примитивных.
let foo: object;
foo = { prop: 'value' };
foo = ['value'];
foo = ():void => console.log('notes');
  • Object — любой JavaScript-объект (соответствует интерфейсу Object, включающему в себя toString(), valueOf(), hasOwnProperty() и другие методы).
let obj: Object;
obj = {};
interface Object {
  toString(): string;
  hasOwnProperty(v: string): boolean;
  /* ... */
}
  • {}пустой тип, пустой объект. Обращение к его свойствам приведёт к ошибке, но остаётся возможность использовать все методы Object.
const foo = {};
foo.a = 'notes'; // Error: Property 'a' does not exist on type '{}'
console.log(foo.toString()); // '[object Object]'

Класс

Интерфейс

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

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

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

interface IAuthor {
  id: string;
  username: string;
}

interface IArticle {
  id: string;
  title: string;
  description?: string;
  getAuthor: () => Author;
}

Использование интерфейсов похоже на утиную типизацию.

interface IDuck {
  quack(): void;
}

const obj: IDuck = {
  /* может квакать, значит утка */
  quack(): void {
    console.log('quack!');
  },
};

Абстрактные классы и интерфейсы

Абстрактный класс (Abstract class) содержит некоторые абстрактные методы, которые должны быть реализованы его наследниками. Помимо абстрактных методов, в нём могут также содержаться и обычные методы. Они характеризуют поведение по умолчанию и их реализовывать не обязательно.

abstract class Duck {
  abstract eat(): void; // утки в разных странах могут есть разную еду
  makeSound(): void {
    console.log('quack!'); // но все они издают похожий звук
  }
}

class Mallard extends Duck {
  eat() { /* ... */ }
  // дикая утка наследует метод quack от абстрактного родительского класса
}

Интерфейс, в отличие от любого класса, вообще не может содержать реализации. Поэтому от него не наследуют (extends), а его реализуют (implements).

interface IDuck {
  eat(): void;
  makeSound(): void;
}

class Mallard implements IDuck {
  eat() { /* ... */ }
  makeSound() { /* ... */ }
};

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

В TypeScript есть возможность наследовать интерфейс от класса.

class Fish {
  private age(): string;
  swim(): void;
}
interface IFlyingFish extends Fish {
  fly: () => void;
};

Такая возможность связана с тем, что в TypeScript (как и в JavaScript) можно создать объект без класса.

class Human {
  sex: string;
  constructor(sex: string) {
    this.sex = sex;
  }

  run():void {
    console.log('run');
  }
}
/* объекты human1 и human2 реализуют один и тот же интерфейс */
const human1 = new Human('male');
const human2 = {
  sex: 'male',
  run():void {
    console.log('run');
  }
};

В этом и есть смысл: можно взять интерфейс класса и использовать его.

interface IHuman extends Human {}

let human3: IHuman;
human3 = { ...human2 };

Более того, мы можем расширить этот интерфейс. И таким образом заменить наследование композицией (Composite Reuse Principle), реализуя расширенный интерфейс вместо переопределения методов родительского класса.

class Bird { /* ... */ };

interface IFlyingBird extends Bird {
  fly: () => void;
}

class FlyingBird implements IFlyingBird { /* ... */ }

Можно также найти применение наследованию интерфейса от класса при использовании Generics.

class Translator<From, To> { /* ... */ }
interface EngRusTranslator extends Translator<Russian, English> {}

const translate = (translator: EngRusTranslator) => { /* ... */ }

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

Если базовый класс содержит приватные или защищённые свойства и методы, то наследующий от него интерфейс может быть реализован только базовым классом или его наследником.