Всем привет! В этой статье (части) рассмотрим вопросы, связанные с великим и могучим TypeScript'ом, которые задают на собеседованиях. Если вы не читали прошлую статью, где описаны вопросы по JS, вот ссылочка. Пожалуйста, не заучивайте вопросы, это вредно для вашего здоровья!)

Стартуем ????

1. Что такое TypeScript и чем он отличается от JavaScript?

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

Основное отличие TypeScript от JavaScript заключается в следующем:

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

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

  3. Дополнительные возможности: TypeScript предоставляет дополнительные возможности, такие как перечисления (enums), кортежи (tuples), типы-алиасы (type aliases) и другие. Это позволяет разработчикам писать более выразительный и понятный код.

  4. Компиляция: TypeScript код компилируется в JavaScript, поэтому он может быть исполнен в любом совместимом с JavaScript окружении. Компилятор TypeScript проверяет синтаксис и типы на этапе компиляции, что помогает выявить ошибки до запуска программы.

2. Какие есть недостатки TypeScript?

Несмотря на множество преимуществ, TypeScript также имеет некоторые недостатки:

  1. Кривая обучения: TypeScript вводит новые концепции и синтаксис, которые могут быть незнакомыми для разработчиков, привыкших к JavaScript. Это может потребовать времени и усилий для изучения и приспособления к новому языку.

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

  3. Дополнительные ресурсы: TypeScript требует наличия компилятора TypeScript для преобразования кода в JavaScript. Это означает, что вам нужно установить и поддерживать компилятор TypeScript, чтобы использовать язык. Это может потребовать дополнительных ресурсов и увеличить сложность проекта.

3. Расскажите про компилятор TypeScript.

Компилятор TypeScript (tsc) преобразует код TypeScript в JavaScript, который может быть выполнен в любой среде, поддерживающей JavaScript. Это позволяет использовать преимущества TypeScript на этапе разработки, а затем получить JavaScript-код, который может работать на любой платформе, поддерживающей JavaScript.

4. Перечислите встроенные типы в TypeScript.

В TypeScript существует несколько встроенных типов. Вот некоторые из них:

  1. number - числовой тип переменной;

  2. string - тип переменной, представляющей строку текста;

  3. boolean - логический тип переменной, принимающий значения true или false;

  4. array - тип переменной, представляющей массив значений. Может быть записан как тип[] или Array<тип>;

  5. tuple - тип переменной, представляющей кортеж (упорядоченный набор элементов разных типов);

  6. enum - тип-перечисление, позволяющий определять набор именованных значений;

  7. any - тип переменной, который может принимать значения любого типа;

  8. void - тип, используемый для объявления функций, которые не возвращают значения;

  9. null и undefined - типы переменных, которые могут принимать значения null или undefined;

5. Поддерживаются ли все принципы ООП в TypeScript?

TypeScript полностью совместим с принципами объектно-ориентированного программирования (ООП).

  1. Инкапсуляция: TypeScript поддерживает инкапсуляцию путем использования модификаторов доступа, таких как publicprivate и protected, для ограничения доступа к членам класса.

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

  3. Полиморфизм: TypeScript поддерживает полиморфизм, что означает, что объекты одного класса могут быть использованы вместо объектов другого класса, если они наследуются от одного и того же базового класса или реализуют один и тот же интерфейс.

  4. Абстракция: TypeScript позволяет создавать абстрактные классы и методы с помощью ключевых слов abstract. Абстрактные классы не могут быть инстанциированы напрямую, а их методы должны быть реализованы в производных классах.

  5. Полноценные классы: TypeScript поддерживает создание классов с конструкторами, методами, свойствами и статическими членами.

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

  7. Перегрузка методов: TypeScript позволяет определять несколько версий метода с разными параметрами и типами входных и выходных данных.

Хотя TypeScript поддерживает принципы ООП, важно отметить, что TypeScript все еще является расширением JavaScript, и некоторые концепции ООП могут иметь некоторые отличия от традиционных языков, таких как Java или C++.

6. Что такое интерфейс в TypeScript?

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

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

Пример:

interface Person {
  name: string;
  age: number;
  greet: () => void;
}

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

7. Различие между классами и интерфейсами в TypeScript?

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

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

Классы:

  • Классы используются для определения объектов с состоянием (переменные-члены) и поведением (методы).

  • Они могут иметь конструкторы для инициализации объектов.

  • Классы поддерживают наследование, что позволяет создавать иерархию наследования и расширять функциональность.

  • Они могут содержать методы доступа (геттеры и сеттеры) для обеспечения контроля доступа к переменным-членам.

  • Классы могут быть инициализированы с использованием оператора new, что позволяет создавать экземпляры объектов.

Интерфейсы:

  • Интерфейсы используются в основном для определения типов данных или контрактов.

  • Они определяют только структуру объекта, но не содержат реализацию методов или инициализацию переменных-членов.

  • Интерфейсы не поддерживают наследование. Они могут наследовать другие интерфейсы, но не классы.

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

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

8. Чем различаются ключевые слова interface и type в TypeScript?

Основные различия между interface и type в TypeScript:

  1. Расширение: Интерфейсы могут быть расширены другими интерфейсами, в то время как типы могут быть объединены с помощью оператора &.

  2. Реализация: Интерфейсы могут быть реализованы классами, в то время как типы не могут быть непосредственно реализованы.

  3. Объединение типов: Типы могут быть объединены с помощью оператора |, что позволяет создавать объединение типов.

  4. Возможности: Интерфейсы обладают некоторыми дополнительными возможностями, такими как объявление методов и наследование интерфейсов.

9. Что такое модули в TypeScript?

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

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

Пример использования модулей в TypeScript:

// Модуль A
export const greeting = "Hello, world!";
export function sayHello() {
  console.log(greeting);
}
// Модуль B
import { greeting, sayHello } from './moduleA';
console.log(greeting); // Выводит "Hello, world!"
sayHello(); // Выводит "Hello, world!"

Модули также могут иметь директиву export default, которая позволяет экспортировать только одно значение по умолчанию из модуля. Это полезно, когда вы хотите экспортировать только одну сущность из модуля.

10. Как вызвать конструктор базового класса из дочернего класса в TypeScript?

В TypeScript вы можете вызвать конструктор базового класса из дочернего класса с помощью ключевого слова super(). Это позволяет инициализировать унаследованные свойства и выполнить любую логику, присутствующую в конструкторе базового класса.

Пример:

class BaseClass {
  constructor(baseProp: string) {
    console.log("Конструктор базового класса вызван с параметром", baseProp);
  }
}

class ChildClass extends BaseClass {
  constructor(baseProp: string, childProp: number) {
    super(baseProp); // Вызов конструктора базового класса
    console.log("Конструктор дочернего класса вызван с параметром", childProp);
  }
}

const childObj = new ChildClass("Значение базового свойства", 42);

11. Что такое геттеры и сеттеры?

В TypeScript геттеры (getters) и сеттеры (setters) - это специальные методы, которые позволяют получать и устанавливать значения свойств класса соответственно. Они предоставляют контроль над доступом к свойствам и позволяют выполнять дополнительные действия при получении или установке значений.

Геттеры и сеттеры определяются с использованием ключевых слов get и set перед именем метода.

Пример:

class Person {
  private _name: string;

  get name(): string {
    return this._name;
  }

  set name(value: string) {
    this._name = value;
  }
}

let person = new Person();
person.name = "John"; // Установка значения с помощью сеттера
console.log(person.name); // Получение значения с помощью геттера

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

class Circle {
  private _radius: number;

  get radius(): number {
    return this._radius;
  }

  set radius(value: number) {
    if (value >= 0) {
      this._radius = value;
    } else {
      throw new Error("Радиус не может быть отрицательным");
    }
  }
}

let circle = new Circle();
circle.radius = 10; // Установка корректного значения
console.log(circle.radius); // Получение значения
circle.radius = -5; // Вызовет ошибку

12. Поддерживает ли TypeScript перегрузку функций?

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

Пример:

function combine(a: string, b: string): string;
function combine(a: number, b: number): number;
function combine(a: any, b: any): any {
  return a + b;
}

let result1 = combine("Hello", "World");// result1 будет иметь тип string
let result2 = combine(5, 10);// result2 будет иметь тип number

13. Можно ли создать только для чтения (read-only) массивы в TypeScript?

В TypeScript можно создать только для чтения (read-only) массивы с помощью типа ReadonlyArray<T>. Этот тип предоставляет гарантию, что массив не может быть изменен после его инициализации.

Использование типа ReadonlyArray<T>:

let readonlyArr: ReadonlyArray<number> = [1, 2, 3];

Использование типа readonly T[]:

let readonlyArr: readonly number[] = [1, 2, 3];

14. В чем разница между типами объединения и пересечения?

В TypeScript есть два основных способа для комбинирования типов: тип объединения (union type) и тип пересечения (intersection type).

Тип объединения (Union Type):

  • Обозначается символом |.

  • Позволяет объединять несколько типов в один.

  • Значение переменной с типом объединения может быть одним из указанных типов.

  • Можно применять операции и методы, общие для всех типов в объединении.

  • Применяется, когда значение может быть одним из нескольких типов.

Пример использования типа объединения:

let value: string | number;
value = "Hello"; // Допустимо
value = 10; // Допустимо
value = true; // Ошибка компиляции

Тип пересечения (Intersection Type):

  • Обозначается символом &.

  • Позволяет создавать новый тип, который объединяет свойства и методы из нескольких типов.

  • Значение переменной с типом пересечения должно удовлетворять всем указанным типам.

  • Можно использовать свойства и методы, объединенные из различных типов.

  • Применяется, когда значение должно удовлетворять нескольким типам одновременно.

Пример использования типа пересечения:

type Person = {
  name: string;
  age: number;
};

type Employee = {
  company: string;
  position: string;
};

type EmployeePerson = Person & Employee;

let employee: EmployeePerson = {
  name: "John",
  age: 30,
  company: "ABC Corp",
  position: "Manager"
};

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

15. В чем разница между extends и implements в TypeScript?

В TypeScript ключевые слова extends и implements используются для определения отношений между классами и интерфейсами.

extends:

  • Используется для создания наследования между классами.

  • Класс может расширять только один другой класс.

  • Позволяет наследовать свойства и методы базового класса.

  • Можно переопределить методы базового класса в производном классе.

  • Можно добавить новые свойства и методы в производный класс.

class Animal {
  name: string;

  constructor(name: string) {
    this.name = name;
  }

  makeSound() {
    console.log("Animal makes sound");
  }
}

class Dog extends Animal {
  makeSound() {
    console.log("Dog barks");
  }
}

let dog = new Dog("Buddy");
dog.makeSound(); // Выводит "Dog barks"

implements:

  • Используется для определения, что класс или объект должен реализовывать определенный интерфейс.

  • Класс может реализовывать несколько интерфейсов.

  • Обязывает класс реализовать все методы и свойства, указанные в интерфейсе.

  • Не поддерживает наследование свойств и методов.

interface Printable {
  print(): void;
}

class Book implements Printable {
  print() {
    console.log("Printing book");
  }
}

let book = new Book();
book.print(); // Выводит "Printing book"

16. Что такое абстрактные классы?

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

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

abstract class Animal {
  name: string;

  constructor(name: string) {
    this.name = name;
  }

  abstract makeSound(): void;
}

class Dog extends Animal {
  makeSound() {
    console.log("Dog barks");
  }
}

let dog = new Dog("Buddy");
dog.makeSound(); // Выводит "Dog barks"

17. Что такое Type Assertions в TypeScript?

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

С помощью угловых скобок (<тип>):

let someValue: any = "Hello, world!";
let strLength: number = (<string>someValue).length;

С помощью ключевого слова as:

let someValue: any = "Hello, world!";
let strLength: number = (someValue as string).length;

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

18. Для чего используется оператор keyof?

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

Синтаксис использования оператора keyof выглядит следующим образом:

type Keys = keyof SomeType;

type Person = {
  name: string;
  age: number;
  email: string;
};

type PersonKeys = keyof Person; // 'name' | 'age' | 'email'

19. Какие Модификаторы Доступа Поддерживает TypeScript?

TypeScript поддерживает следующие модификаторы доступа для классов и их членов:

public: Это является модификатором доступа по умолчанию и означает, что член класса доступен из любого места в коде.

class MyClass {
  public myProperty: string;

  public myMethod() {
    // ...
  }
}

private: Этот модификатор ограничивает доступ к члену класса только внутри самого класса. Члены, объявленные как private, не могут быть доступны извне класса.

class MyClass {
  private myProperty: string;

  private myMethod() {
    // ...
  }
}

protected: Этот модификатор позволяет доступ к члену класса внутри самого класса и его подклассов. Члены, объявленные как protected, не могут быть доступны извне класса или его экземпляров.

class MyBaseClass {
  protected myProperty: string;

  protected myMethod() {
    // ...
  }
}

class MyDerivedClass extends MyBaseClass {
  constructor() {
    super();
    this.myProperty = 'value'; // Доступно в подклассе
  }
}

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

class MyClass {
  readonly myProperty: string;

  constructor() {
    this.myProperty = 'value'; // Можно установить только здесь
  }

  updateProperty() {
    this.myProperty = 'new value'; // Ошибка компиляции: свойство только для чтения
  }
}

static: Этот модификатор позволяет создавать статические члены класса, которые принадлежат самому классу, а не его экземплярам. Статические члены могут быть доступны без создания экземпляра класса.

class MyClass {
  static myStaticProperty: string;

  static myStaticMethod() {
    // ...
  }
}

MyClass.myStaticProperty = 'value'; // Доступно без создания экземпляра класса
MyClass.myStaticMethod(); // Доступно без создания экземпляра класса

20. Что такое Generics?

В TypeScript generics позволяют создавать параметризованные типы, которые могут быть использованы для создания переиспользуемых компонентов (функции, классы).

function identity<T>(arg: T): T {
  return arg;
}

let result = identity<string>("Hello, TypeScript!");
console.log(result); // Output: Hello, TypeScript!

// Тип возвращаемого значения выводится автоматически
let result2 = identity(42);
console.log(result2); // Output: 42

Еще немного примеров.

Функция для вывода длины массива:

function getArrayLength<T>(arr: T[]): number {
  return arr.length;
}

let numbers = [1, 2, 3, 4];
let length = getArrayLength(numbers); 
console.log(length); // Output: 4

let strings = ["Hello", "World"];
length = getArrayLength(strings);
console.log(length); // Output: 2

класс для создания стека

class Stack<T> {
  private items: T[] = [];

  push(item: T) {
    this.items.push(item);
  }

  pop(): T | undefined {
    return this.items.pop();
  }
}

let stack = new Stack<number>();
stack.push(1);
stack.push(2);
stack.push(3);

console.log(stack.pop()); // Output: 3
console.log(stack.pop()); // Output: 2

let stringStack = new Stack<string>();
stringStack.push("Hello");
stringStack.push("World");

console.log(stringStack.pop()); // Output: World
console.log(stringStack.pop()); // Output: Hello

интерфейс для сравнения объектов:

interface Comparable<T> {
  compareTo(other: T): number;
}

class Person implements Comparable<Person> {
  constructor(public name: string, public age: number) {}

  compareTo(other: Person): number {
    return this.age - other.age;
  }
}

let person1 = new Person("Alice", 25);
let person2 = new Person("Bob", 30);
console.log(person1.compareTo(person2)); // Output: -5

21. Ограничения и наследования generics.

В TypeScript можно применять ограничения (constraints) и использовать наследование для более точного управления доступными типами в generics. Использование ограничений позволяет указать, что тип, переданный в generics, должен удовлетворять определенным условиям.

Ограничение по типу через extends:

interface Animal {
  name: string;
}

function getAnimalName<T extends Animal>(animal: T): string {
  return animal.name;
}

let cat = { name: "Whiskers", color: "gray" };
let catName = getAnimalName(cat); // ОК, так как cat имеет свойство `name`
console.log(catName); // Output: Whiskers

let car = { brand: "Tesla", model: "Model 3" };
let carName = getAnimalName(car); // Ошибка, так как car не имеет свойства `name`

Ограничение по типу с использованием операции keyof:

interface Person {
  name: string;
  age: number;
}

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

let person: Person = { name: "Alice", age: 25 };
let name = getProperty(person, "name"); // ОК, так как у person есть свойство "name"
console.log(name); // Output: Alice

let age = getProperty(person, "age"); // ОК, так как у person есть свойство "age"
console.log(age); // Output: 25

let job = getProperty(person, "job"); // Ошибка, так как у person нет свойства "job"

Наследование generics:

class Animal {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}

class Cat extends Animal {
  meow() {
    console.log(`${this.name} says meow!`);
  }
}

function printAnimalName<T extends Animal>(animal: T) {
  console.log(animal.name);
}

let cat = new Cat("Whiskers");
printAnimalName(cat); // ОК, так как Cat является подтипом Animal
cat.meow(); // Output: Whiskers says meow!

22. Что такое Mixins?

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

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

Пример:

// Миксиновая функция
function Printable(target: any) {
  target.prototype.print = function() {
    console.log(this);
  };
}

// Базовый класс
class Person {
  constructor(public name: string) {}
}

// Применение миксина к классу
const PrintablePerson = Printable(Person);

// Создание экземпляра класса с добавленным поведением миксина
const person = new PrintablePerson("John");
person.print(); // Выводит объект person в консоль

23. Для чего используются Enum?

Enum (перечисление) в TypeScript - это специальный тип данных, который позволяет определить набор именованных констант. Перечисления представляют собой удобный способ задания ограниченного набора значений, которые могут быть использованы в коде.

Вот пример определения и использования перечисления в TypeScript:

enum Color {
  Red,
  Green,
  Blue,
}

let myColor: Color = Color.Green;
console.log(myColor); // Выводит 1

if (myColor === Color.Green) {
  console.log("The color is green");
}

24. Расскажите про условные типы (conditional types) в TypeScript.

Условные типы (conditional types) в TypeScript - это мощный механизм, который позволяет создавать типы, зависящие от условий и других типов. Они позволяют нам задавать условия и определять типы на основе этих условий.

Условные типы используют ключевое слово infer, которое позволяет нам "выводить" типы на основе заданных условий. Они часто используются в комбинации с условными операторами extends и ?.

Пример:

type TypeName<T> =
  T extends string ? "string" :
  T extends number ? "number" :
  T extends boolean ? "boolean" :
  T extends undefined ? "undefined" :
  "object";

let name1: TypeName<string> = "string";
let name2: TypeName<number> = "number";
let name3: TypeName<boolean> = "boolean";
let name4: TypeName<undefined> = "undefined";
let name5: TypeName<object> = "object";

25. Чем отличается any never unknown

anynever и unknown - это три разных типа в TypeScript, каждый из которых имеет свои особенности и использование.

any: Тип any представляет отсутствие статической типизации. Переменные с типом any могут принимать значения любого типа, и компилятор TypeScript не будет проверять типы во время компиляции. Использование any позволяет обойти проверку типов, но также уменьшает безопасность и надежность кода.

let variable: any = 5;
variable = "Hello";
variable = true;

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

function throwError(message: string): never {
  throw new Error(message);
}

function infiniteLoop(): never {
  while (true) {
    // Бесконечный цикл
  }
}

unknown: Тип unknown представляет значение, о типе которого мы ничего не знаем. Переменные с типом unknown могут хранить значения любого типа, но мы не можем выполнять над ними операции без явного приведения типов или проверки типов. unknown является более безопасной альтернативой типу any, так как требует явного приведения типов перед использованием.

function processValue(value: unknown): string {
  if (typeof value === "string") {
    return value.toUpperCase();
  } else {
    return "Unknown value";
  }
}

let result1 = processValue("hello"); // Возвращает "HELLO"
let result2 = processValue(10); // Возвращает "Unknown value"

В целом, any предоставляет полную гибкость, но уменьшает безопасность типов. never используется для недостижимых значений или ситуаций, когда программа не может продолжить выполнение. unknown представляет неизвестные типы, требует явного приведения типов и обеспечивает большую безопасность типов, чем any.

26. Что такое Mapped types?

В TypeScript вы можете использовать отображаемые типы (mapped types) для создания новых типов на основе существующих типов. Они позволяют вам применять определенные преобразования к свойствам существующего типа и создавать новый тип с обновленными свойствами.

Основной синтаксис для создания отображаемых типов выглядит следующим образом:

type NewType = { [Property in ExistingType]: NewValueType };

type Person = {
  name: string;
  age: number;
};

type UpdatedPerson = {
  [Property in keyof Person]: Property extends 'name' ? string : number;
};

const person: UpdatedPerson = {
  name: 'John',
  age: 25,
};

27. Что такое декораторы?

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

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

Декоратор класса:

function logClass(target: any) {
  console.log('Class decorator');
}

@logClass
class MyClass {
  // ...
}

Декоратор метода:

function logMethod(target: any, key: string, descriptor: PropertyDescriptor) {
  console.log('Method decorator');
}

class MyClass {
  @logMethod
  myMethod() {
    // ...
  }
}

Декоратор свойства:

function logProperty(target: any, key: string) {
  console.log('Property decorator');
}

class MyClass {
  @logProperty
  myProperty: string;
}

Декоратор параметра функции:

function logParameter(target: any, key: string, index: number) {
  console.log('Parameter decorator');
}

class MyClass {
  myMethod(@logParameter param: string) {
    // ...
  }
}

28. Что такое утилиты в TypeScript

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

Некоторые из наиболее распространенных утилит в TypeScript включают:

Partial<T>: Создает тип, который делает все свойства типа T необязательными. Это позволяет создавать новые типы, частично определенные на основе существующих типов.

interface Person {
  name: string;
  age: number;
}

const partialPerson: Partial<Person> = {
  name: 'John',
};

Required<T>: Создает тип, который делает все свойства типа T обязательными. Это полезно, когда вы хотите указать, что все свойства должны быть определены.

interface Person {
  name?: string;
  age?: number;
}

const requiredPerson: Required<Person> = {
  name: 'John',
  age: 25,
};

Readonly<T>: Создает тип, который делает все свойства типа T доступными только для чтения. Это предотвращает изменение свойств объекта после его инициализации.

interface Person {
  name: string;
  age: number;
}

const readonlyPerson: Readonly<Person> = {
  name: 'John',
  age: 25,
};

readonlyPerson.name = 'Jane'; // Ошибка компиляции: свойство доступно только для чтения

Record<K, T>: Создает тип, который представляет объект с ключами типа K и значениями типа T. Это полезно, когда вы хотите создать объект с определенными ключами и значениями.

const fruits: Record<string, number> = {
  apple: 5,
  banana: 3,
};

Pick<T, K>: Создает тип, который выбирает только указанные свойства K из типа T. Это позволяет создавать новые типы, содержащие только необходимые свойства.

interface Person {
  name: string;
  age: number;
  email: string;
}

const pickedPerson: Pick<Person, 'name' | 'email'> = {
  name: 'John',
  email: 'john@example.com',
};

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

29. Что представляют собой .map-файлы в TypeScript?

Файлы с расширением .map в TypeScript представляют собой файлы карты (source map files), которые создаются вместе с скомпилированным JavaScript кодом. Они содержат информацию о соответствии между исходным кодом TypeScript и соответствующим скомпилированным JavaScript кодом.

Карты (source maps) являются инструментом отладки, который позволяет разработчикам отслеживать и отлаживать исходный код TypeScript в инструментах разработчика браузера или других инструментах, которые работают с JavaScript кодом.

Когда TypeScript компилируется в JavaScript, вместе с скомпилированным кодом генерируется файл карты .map. Этот файл содержит информацию о соответствии между строками исходного кода TypeScript и строками скомпилированного JavaScript кода. Это включает информацию о файлах, именах функций, позициях исходного кода и других сопутствующих данных.

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

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


На этом все. Надеюсь, эти вопросы и ответы помогут вам в подготовке к техническому собеседованию. Еще у TypeScript есть хорошая дока, почитать можно здесь.

Подписывайтесь на мой канал, там тоже стараюсь выкладывать интересную информацию.

Удачи ✨

Комментарии (7)


  1. Spaceoddity
    11.01.2024 05:25
    +2

    В недостатки внесите так же возню с проставлением типов (порой совсем не тривиальную).


  1. mclander
    11.01.2024 05:25

    Огонь! Всё по полочкам.

    Но меня бы сильно насторожил работодатель, который начал бы такой формальный опрос)


    1. altrapeznikov Автор
      11.01.2024 05:25

      Я обычно по ts спрашиваю три любых вопроса из этого списка )


  1. persii
    11.01.2024 05:25

    Спасибо за хороший обзорный материал;

    однако же кое в чём есть сомнения.

    Ещё в предыдущей статье встречались конструкции, образованные калькой с английского, но "изучение кривой" в этой части уже никак нельзя списать на то, что так случайно прочитали и запомнили как запомнили, ибо в таком виде невозможно *сделать смысл* из высказанного, *если вы понимаете, о чём я*.

    К чему веду:

    в тексте есть свидетельства составления на английском и неотсмотренного перевода, возможно, размещение переводного материала без тега (или нейротекста) — по коням!


  1. MarineEngineer
    11.01.2024 05:25

    Отличная подборка, автору благодарность.

    В 7ом пункте задвоилось начало.


  1. Safort
    11.01.2024 05:25
    +6

    Не очень. Если материал именно про TS, то половину статьи можно удалить. Всё перемешано, вы часто тут представляете фичи JS как фичи TS. Да и иногда старый синтаксис используете (private vs #).

    Вот с ходу в том, что описано в

    9. Что такое модули в TypeScript?

    10. Как вызвать конструктор базового класса из дочернего класса в TypeScript?

    11. Что такое геттеры и сеттеры?

    Это же вы по сути JS и описываете? Только в геттерах зачем-то пример с ключевым словом private, хотя пожно было обойтись более коротким #.

    19. Какие Модификаторы Доступа Поддерживает TypeScript?

    public, private, protected, readonly - ок, это именно из TS, но static тут причём? И почему тогда забыли про модификатор # ? Раз уж всё равно смешиваете оба языка.

    28. Что такое декараторы?

    Вы используете у себя депрекейтнутые декораторы TS? Иначе зачем про это вообще страшивать. Или тут всё таки про JS-декораторы говорится?


  1. movl
    11.01.2024 05:25
    +3

    Про пункт отличия типов и интерфейсов

    Реализация: Интерфейсы могут быть реализованы классами, в то время как типы не могут быть непосредственно реализованы.

    Если речь про implements, то типы могут быть реализованы в виде класса с некоторыми ограничениями.

    Объединение типов: Типы могут быть объединены с помощью оператора |, что позволяет создавать объединение типов.

    Объединение интерфейсов ничем не отличается от объединения типов, но на выходе, да, получится тип, вместо интерфейса.

    Возможности: Интерфейсы обладают некоторыми дополнительными возможностями, такими как объявление методов и наследование интерфейсов.

    Ничто не мешает объявить метод внутри типа.

    Касательно наследования и расширения, из данного описания как-будто это суть одного и того же:

    interface Foo {}
    interface Bar extends Foo {}
    
    • Foo расширяет Bar

    • Bar наследует Foo

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

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