[Перевод] Собеседование по TypeScript: 20 вопросов и ответов

Язык TypeScript основан на том же синтаксисе и семантике, которые хорошо знакомы миллионам JavaScript-разработчиков. TypeScript даёт возможность работать с самыми свежими и ещё только появляющимися возможностями JS, включая те, которые имеются в ECMAScript 2015, и те, которые пока существуют лишь в виде предложений. Среди таких возможностей, например, асинхронные функции и декораторы. Всё это направлено на то, чтобы помочь разработчику в создании надёжных и современных приложений.

TypeScript-программа компилируется в обычный JavaScript-код, который может выполняться в любом браузере или в среде Node.js. Этот код будет понятен любому JS-движку, который поддерживает стандарт ECMAScript 3 или более новый.

a09exqcgbx58lh0etyzgbtexcnw.jpeg

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

Вопрос №1 (1). Что такое TypeScript и зачем использовать его вместо JavaScript?


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

TypeScript (TS) — это надмножество JavaScript (JS), среди основных особенностей которого можно отметить возможность явного статического назначения типов, поддержку классов и интерфейсов. Одним из серьёзных преимуществ TS перед JS является возможность создания, в различных IDE, такой среды разработки, которая позволяет, прямо в процессе ввода кода, выявлять распространённые ошибки. Применение TypeScript в больших проектах может вести к повышению надёжности программ, которые, при этом, можно разворачивать в тех же средах, где работают обычные JS-приложения.

Вот некоторые подробности о TypeScript:

  • TypeScript поддерживает современные редакции стандартов ECMAScript, код, написанный с использованием которых, компилируется с учётом возможности его выполнения на платформах, поддерживающих более старые версии стандартов. Это означает, что TS-программист может использовать возможности ES2015 и более новых стандартов, наподобие модулей, стрелочных функций, классов, оператора spread, деструктурирования, и выполнять то, что у него получается, в существующих средах, которые пока этих стандартов не поддерживают.
  • TypeScript — это надстройка над JavaScript. Код, написанный на чистом JavaScript, является действительным TypeScript-кодом.
  • TypeScript расширяет JavaScript возможностью статического назначения типов. Система типов TS отличается довольно обширными возможностями. А именно, она включает в себя интерфейсы, перечисления, гибридные типы, обобщённые типы (generics), типы-объединения и типы-пересечения, модификаторы доступа и многое другое. Применение TypeScript, кроме того, немного упрощает работу за счёт использования вывода типов.
  • Применение TypeScript, в сравнении с JavaScript, значительно улучшает процесс разработки. Дело в том, что IDE, в реальном времени, получает сведения о типах от TS-компилятора.
  • При использовании режима строгой проверки на null (для этого применяется флаг компилятора --strictNullChecks), компилятор TypeScript не разрешает присвоение null и undefined переменным тех типов, в которых, в таком режиме, использование этих значений не допускается.
  • Для использования TypeScript нужно организовать процесс сборки проекта, включающий в себя этап компиляции TS-кода в JavaScript. Компилятор может встроить карту кода (source map) в сгенерированные им JS-файлы, или создавать отдельные .map-файлы. Это позволяет устанавливать точки останова и исследовать значения переменных во время выполнения программ, работая непосредственно с TypeScript-кодом.
  • TypeScript — это опенсорсный проект Microsoft, выпущенный под лицензией Apache 2. Инициатором разработки TypeScript является Андерс Хейлсберг. Он причастен к созданию Turbo Pascal, Delphi и C#.


→ Источник

Вопрос №2 (1). Расскажите об обобщённых типах в TypeScript.


Обобщённые типы (generics) позволяют создавать компоненты или функции, которые могут работать с различными типами, а не с каким-то одним. Рассмотрим пример:

/** Объявление класса с параметром обобщённого типа */
class Queue {
  private data = [];
  push = (item: T) => this.data.push(item);
  pop = (): T => this.data.shift();
}

const queue = new Queue();
queue.push(0);
queue.push("1"); // Ошибка : в такую очередь нельзя добавить строку, тут разрешено использовать лишь числа


→ Источник

Вопрос №3 (2). Поддерживает ли TypeScript все принципы объектно-ориентированного программирования?


Да, поддерживает. Существуют четыре основных принципа объектно-ориентированного программирования:

  • Инкапсуляция
  • Наследование
  • Абстракция
  • Полиморфизм


Пользуясь простыми и понятными средствами TypeScript, можно реализовать все эти принципы.

→ Источник

Вопрос №4 (2). Как в TypeScript проверять значения на равенство null и undefined?


Для выполнения подобных проверок достаточно воспользоваться следующей конструкцией:

if (value) {
}


Выражение в скобках будет приведено к true в том случае, если оно не является чем-то из следующего списка:

  • null
  • undefined
  • NaN
  • Пустая строка
  • 0
  • false


TypeScript поддерживает те же правила преобразования типов, что и JavaScript.

→ Источник

Вопрос №5 (2). Как в TypeScript реализовать свойства класса, являющиеся константами?


В TypeScript, при объявлении свойств классов, нельзя использовать ключевое слово const. При попытке использования этого ключевого слова выводится следующее сообщение об ошибке: A class member cannot have the ‘const’ keyword. В TypeScript 2.0 имеется модификатор readonly, позволяющий создавать свойства класса, предназначенные только для чтения:

class MyClass {
    readonly myReadonlyProperty = 1;

    myMethod() {
        console.log(this.myReadonlyProperty);
    }
}

new MyClass().myReadonlyProperty = 5; // ошибка, так как свойство предназначено только для чтения


→ Источник

Вопрос №6 (2). Что представляют собой .map-файлы в TypeScript?


Файлы с расширением .map хранят карты кода (source map), которые содержат данные о соответствии кода, написанного на TypeScript, JavaScript-коду, созданному на его основе. С этим файлами могут работать многие отладчики (например — Visual Studio и инструменты разработчика Chrome). Это позволяет, в ходе отладки, работать с исходным кодом программ на TypeScript, а не с их JS-эквивалентами.

→ Источник

Вопрос №7 (2). Что такое геттеры и сеттеры в TypeScript?


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

class foo {
  private _bar:boolean = false;

  get bar():boolean {
    return this._bar;
  }
  set bar(theBar:boolean) {
    this._bar = theBar;
  }
}

var myBar = myFoo.bar;  // здесь вызывается геттер
myFoo.bar = true;  // здесь вызывается сеттер


→ Источник

Вопрос №8 (2). Можно ли использовать TypeScript в серверной разработке, и если да — то как?


Программы, написанные на TypeScript, подходят не только для фронтенд-разработки, но и для создания серверных приложений. Например, на TS можно писать программы для платформы Node.js. Это даёт программисту дополнительные средства по контролю типов и позволяет использовать другие возможности языка. Для создания серверных приложений на TS нужно лишь наладить правильный процесс обработки кода, на вход которого поступают TypeScript-файлы, а на выходе получаются JavaScript-файлы, подходящие для выполнения их в Node.js. Для того чтобы организовать такую среду, сначала надо установить компилятор TypeScript:

npm i -g typescript


Параметры компилятора задают с помощью файла tsconfig.json, который определяет, кроме прочего, цель компиляции и место, в которое нужно поместиться готовые JS-файлы. В целом, этот файл очень похож на конфигурационные файлы babel или webpack:

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "declaration": true,
    "outDir": "build"
  }
}


Теперь, при условии, что компилятору есть что обрабатывать, нужно его запустить:

tsc


И, наконец, учитывая то, что JS-файлы, пригодные для выполнения в среде Node.js, находятся в папке build, надо выполнить такую команду, находясь в корневой директории проекта:

node build/index.js


→ Источник

Вопрос №9 (3). Расскажите об основных компонентах TypeScript.


TypeScript включает в себя три основных компонента:

  • Язык. Это, с точки зрения разработчиков, самая важная часть TypeScript. «Язык» — это синтаксис, ключевые слова, всё то, что позволяет писать программы на TypeScript.
  • Компилятор. TypeScript обладает компилятором с открытым исходным кодом, он является кросс-платформенным, с открытой спецификацией, и написан на TypeScript. Компилятор выполняет преобразование TypeScript-кода в JavaScript-код. Кроме того, если с программой что-то не так, он выдаёт сообщения об ошибках. Он позволяет объединять несколько TypeScript-файлов в один выходной JS-файл и умеет создавать карты кода.
  • Вспомогательные инструменты. Вспомогательные инструменты TypeScript предназначены для облегчения процесса разработки с его использованием в различных IDE. Среди них — Visual Studio, VS Code, Sublime, различные средства для быстрого запуска TS-кода, и другие.


→ Источник

Вопрос №10 (3). Есть ли в предоставленном вам TypeScript-коде ошибки? Объясните свой ответ.


Вот фрагмент кода:

class Point {
    x: number;
    y: number;
}

interface Point3d extends Point {
    z: number;
}

let point3d: Point3d = {x: 1, y: 2, z: 3};


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

→ Источник

qrnn7-sznof1sueb9oqf0uhxjie.jpeg
Просто очень любим этот фильм)

Вопрос №11 (3). Расскажите об использовании декораторов свойств в TypeScript.


Декораторы можно использовать для изменения поведения классов, при этом ещё больше пользы от них можно получить при их использовании с каким-либо фреймворком. Например, если в вашем фреймворке есть методы, доступ к которым ограничен (скажем, они предназначены только для администратора), несложно будет написать декоратор метода @admin, который будет запрещать доступ к соответствующим методам пользователям, не являющимся администраторами. Можно создать декоратор @owner, который позволяет модифицировать объект только его владельцу. Вот как может выглядеть использование декораторов:

class CRUD {
    get() { }
    post() { }

    @admin
    delete() { }

    @owner
    put() { }
}


→ Источник

Вопрос №12 (3). Можно ли в TypeScript использовать строго типизированные функции в качестве параметров?


Рассмотрим следующий пример:

class Foo {
    save(callback: Function) : void {
        //Выполняем сохранение
        var result : number = 42; //Получаем в ходе операции сохранения некое число
        //Можно ли во время выполнения программы как-то обеспечить то, чтобы коллбэк принимал лишь один параметр типа number?
        callback(result);
    }
}

var foo = new Foo();
var callback = (result: string) : void => {
    alert(result);
}
foo.save(callback);


Можно ли в методе save организовать работу с типизированным коллбэком? Перепишите код для того, чтобы это продемонстрировать.

В TypeScript можно объявить тип коллбэка, после чего переписать код:

type NumberCallback = (n: number) => any;

class Foo {
    // Эквивалент
    save(callback: NumberCallback): void {
        console.log(1)
        callback(42);
    }
}

var numCallback: NumberCallback = (result: number) : void => {
    console.log("numCallback: ", result.toString());
}

var foo = new Foo();
foo.save(numCallback)


→ Источник

Вопрос №13 (3). Как сделать так, чтобы классы, объявленные в модуле, были бы доступны и за пределами этого модуля?


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

module Vehicle {
    class Car {
        constructor (
            public make: string, 
            public model: string) { }
    }
    var audiCar = new Car("Audi", "Q7");
}
// Это работать не будет
var fordCar = Vehicle.Car("Ford", "Figo");


В коде, приведённом выше, при попытке инициализации переменной fordCar произойдёт ошибка. Для того чтобы сделать класс, объявленный в модуле, доступным за пределами этого модуля, нужно воспользоваться ключевым словом export:

module Vehicle {
    export class Car {
        constructor (
            public make: string, 
            public model: string) { }
    }
    var audiCar = new Car("Audi", "Q7");
}
// Теперь этот фрагмент кода работает нормально
var fordCar = Vehicle.Car("Ford", "Figo");


→ Источник

Вопрос №14 (3). Поддерживает ли TypeScript перегрузку функций?


TypeScript поддерживает перегрузку функций, но реализация этого механизма отличается от той, которую можно видеть в других объектно-ориентированных языках. А именно, в TS создают лишь одну функцию и некоторое количество объявлений. Когда такой код компилируется в JavaScript, видимой оказывается лишь одна конкретная функция. Этот механизм работает из-за того, что JS-функции можно вызывать, передавая им разное количество параметров.

class Foo {
    myMethod(a: string);
    myMethod(a: number);
    myMethod(a: number, b: string);
    myMethod(a: any, b?: string) {
        alert(a.toString());
    }
}


→ Источник

Вопрос №15 (4). Что не так с предоставленным вам кодом?


Вот код, о котором идёт речь:

/* Неверно*/
interface Fetcher {
    getObject(done: (data: any, elapsedTime?: number) => void): void;
}


Рекомендуется использовать необязательные параметры в коллбэках только в том случае, если вы абсолютно точно понимаете последствия такого шага. Этот код имеет весьма специфический смысл: коллбэк done может быть вызван или с 1 или 2 аргументами. Автор кода, вероятно, намеревался сообщить нам, что коллбэк может не обращать внимания на параметр elapsedTime, но для того, чтобы этого достичь, всегда можно создать коллбэк, который принимает меньшее число аргументов.

→ Источник

Вопрос №16 (4). Как в TypeScript перегрузить конструктор класса?


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

  • Можно воспользоваться необязательным параметром:
    class Box {
        public x: number;
        public y: number;
        public height: number;
        public width: number;
    
        constructor();
        constructor(obj: IBox); 
        constructor(obj?: any) {    
            this.x = obj && obj.x || 0
            this.y = obj && obj.y || 0
            this.height = obj && obj.height || 0
            this.width = obj && obj.width || 0;
        }   
    }
    
  • Можно воспользоваться параметрами по умолчанию:
    class Box {
        public x: number;
        public y: number;
        public height: number;
        public width: number;
    
        constructor(obj : IBox = {x:0,y:0, height:0, width:0}) {    
            this.x = obj.x;
            this.y = obj.y;
            this.height = obj.height;
            this.width = obj.width;
        }   
    }
    
  • Можно использовать дополнительные перегрузки в виде методов статической фабрики:
    class Person {
        static fromData(data: PersonData) {
            let { first, last, birthday, gender = 'M' } = data 
            return new this(
                `${last}, ${first}`,
                calculateAge(birthday),
                gender
            )
        }
    
        constructor(
            public fullName: string,
            public age: number,
            public gender: 'M' | 'F'
        ) {}
    }
    
    interface PersonData {
        first: string
        last: string
        birthday: string
        gender?: 'M' | 'F'
    }
    
    
    let personA = new Person('Doe, John', 31, 'M')
    let personB = Person.fromData({
        first: 'John',
        last: 'Doe',
        birthday: '10-09-1986'
    })
    
  • Можно использовать тип-объединение:
    class foo {
        private _name: any;
        constructor(name: string | number) {
            this._name = name;
        }
    }
    var f1 = new foo("bar");
    var f2 = new foo(1);
    


→ Источник

Вопрос №17 (4). Чем различаются ключевые слова interface и type в TypeScript?


Вот примеры использования этих ключевых слов:

interface X {
    a: number
    b: string
}

type X = {
    a: number
    b: string
};


В отличие от объявления интерфейса, которое всегда представляет именованный тип объекта, применение ключевого слова type позволяет задать псевдоним для любой разновидности типа, включая примитивные типы, типы-объединения и типы-пересечения.

При использовании ключевого слова type вместо ключевого слова interface теряются следующие возможности:

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


→ Источник

Вопрос №18 (5). Расскажите о том, когда в TypeScript используют ключевое слово declare.


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

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

declare var myLibrary;


TypeScript назначит переменной myLibrary тип any. Проблема тут заключается в том, что у вас не будет, во время разработки, интеллектуальных подсказок по этой библиотеке, хотя использовать её в своём коде вы сможете. В этой ситуации можно воспользоваться и другим подходом, ведущим к тому же результату. Речь идёт об использовании переменной типа any:

var myLibrary: any;


И в том и в другом случае при компиляции TS-кода в JavaScript, получится одно и то же, но вариант с использованием ключевого слова declare отличается лучшей читабельностью. Применение этого ключевого слова приводит к созданию так называемого внешнего объявления переменной (ambient declaration).

Вопрос №19 (5). Что такое внешние объявления переменных в TypeScript и когда их нужно использовать?


Внешнее объявление переменной (ambient declaration) — это механизм, который позволяет сообщать компилятору TypeScript о том, что некий исходный код существует где-то за пределами текущего файла. Внешние объявления помогают интегрировать в TS-программы сторонние JavaScript-библиотеки.

Эти объявления делают в файле объявления типов с расширением .d.ts. Внешние переменные или модули объявляют так:

declare module Module_Name {
}


Файлы, в которых находится внешний код, должны быть подключены в TS-файле, использующем их, так:

/// 


→ Источник

Вопрос №20 (5). Можно ли автоматически генерировать файлы объявлений TypeScript из JS-библиотек?


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

  • Microsoft/dts-gen — официальное средство, используемое Microsoft как отправная точка при создании объявлений типов.
  • dtsmake — многообещающий инструмент для автоматического создания объявлений типов на основе JS-файлов, находящийся в процессе разработки. Он зависит от системы анализа кода Tern, которую используют некоторые редакторы для реализации механизма автозавершения при вводе JS-кода.


→ Источник

Итоги


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

Уважаемые читатели! Какие вопросы вы задали бы тому, кто проходит собеседование, претендуя на должность, требующую знания TypeScript?

kvczxistpznrxhfv91vhwme31e4.jpeg
— И какая скидка по этому промо-коду?!
— Кажется, 7. Проверю, если хотите…
— Да уж, вы проверьте. По-моему, скидка чуть завышена!

— Простите, я немного ошиблась насчет скидки. 10% на все виртуальные сервера.

© Habrahabr.ru