Подготовка к собеседованию по TypeScript
-
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. Расскажите об обобщённых типах в TypeScript.
Обобщённые типы (generics) позволяют создавать компоненты или функции, которые могут работать с различными типами, а не с каким-то одним. Рассмотрим пример:
/** Объявление класса с параметром обобщённого типа */ class Queue<t> { private data = []; push = (item: T) => this.data.push(item); pop = (): T => this.data.shift(); } const queue = new Queue<number>(); queue.push(0); queue.push("1"); // Ошибка : в такую очередь нельзя добавить строку, тут разрешено использовать лишь числа
-
3. Поддерживает ли TypeScript все принципы объектно-ориентированного программирования?
Да, поддерживает. Существуют четыре основных принципа объектно-ориентированного программирования:
- Инкапсуляция
- Наследование
- Абстракция
- Полиморфизм
Пользуясь простыми и понятными средствами TypeScript, можно реализовать все эти принципы.
-
4. Как в TypeScript проверять значения на равенство null и undefined?
Для выполнения подобных проверок достаточно воспользоваться следующей конструкцией:
if (value) { }
Выражение в скобках будет приведено к
true
в том случае, если оно не является чем-то из следующего списка:null
undefined
NaN
- Пустая строка
- 0
false
TypeScript поддерживает те же правила преобразования типов, что и JavaScript.
-
5. Как в 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. Что представляют собой .map-файлы в TypeScript?
Файлы с расширением .map хранят карты кода (source map), которые содержат данные о соответствии кода, написанного на TypeScript, JavaScript-коду, созданному на его основе. С этим файлами могут работать многие отладчики (например — Visual Studio и инструменты разработчика Chrome). Это позволяет, в ходе отладки, работать с исходным кодом программ на TypeScript, а не с их JS-эквивалентами.
-
7. Что такое геттеры и сеттеры в 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. Можно ли использовать 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. Расскажите об основных компонентах TypeScript.
TypeScript включает в себя три основных компонента:
- Язык. Это, с точки зрения разработчиков, самая важная часть TypeScript. «Язык» — это синтаксис, ключевые слова, всё то, что позволяет писать программы на TypeScript.
- Компилятор. TypeScript обладает компилятором с открытым исходным кодом, он является кросс-платформенным, с открытой спецификацией, и написан на TypeScript. Компилятор выполняет преобразование TypeScript-кода в JavaScript-код. Кроме того, если с программой что-то не так, он выдаёт сообщения об ошибках. Он позволяет объединять несколько TypeScript-файлов в один выходной JS-файл и умеет создавать карты кода.
- Вспомогательные инструменты. Вспомогательные инструменты TypeScript предназначены для облегчения процесса разработки с его использованием в различных IDE. Среди них — Visual Studio, VS Code, Sublime, различные средства для быстрого запуска TS-кода, и другие.
-
10. Есть ли в предоставленном вам TypeScript-коде ошибки?
Вот фрагмент кода:
class Point { x: number; y: number; } interface Point3d extends Point { z: number; } let point3d: Point3d = {x: 1, y: 2, z: 3};
Ошибок в этом коде нет. Объявление класса создаёт две сущности: это тип данных, используемый для создания экземпляров класса, и функция-конструктор. Так как классы создают типы данных, использовать их можно там же, где можно использовать интерфейсы.
-
11. Расскажите об использовании декораторов свойств в TypeScript.
Декораторы можно использовать для изменения поведения классов, при этом ещё больше пользы от них можно получить при их использовании с каким-либо фреймворком. Например, если в вашем фреймворке есть методы, доступ к которым ограничен (скажем, они предназначены только для администратора), несложно будет написать декоратор метода
@admin
, который будет запрещать доступ к соответствующим методам пользователям, не являющимся администраторами. Можно создать декоратор@owner
, который позволяет модифицировать объект только его владельцу. Вот как может выглядеть использование декораторов:class CRUD { get() { } post() { } @admin delete() { } @owner put() { } }
-
12. Можно ли в 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. Как сделать так, чтобы классы, объявленные в модуле, были бы доступны и за пределами этого модуля?
Классы, объявленные в модуле, доступны в пределах этого модуля. За его пределами доступ к ним получить нельзя.
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. Поддерживает ли 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. Что не так с предоставленным вам кодом?
Вот код, о котором идёт речь:
/* Неверно*/ interface Fetcher { getObject(done: (data: any, elapsedTime?: number) => void): void; }
Рекомендуется использовать необязательные параметры в коллбэках только в том случае, если вы абсолютно точно понимаете последствия такого шага. Этот код имеет весьма специфический смысл: коллбэк
done
может быть вызван или с 1 или 2 аргументами. Автор кода, вероятно, намеревался сообщить нам, что коллбэк может не обращать внимания на параметрelapsedTime
, но для того, чтобы этого достичь, всегда можно создать коллбэк, который принимает меньшее число аргументов. -
16. Как в 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. Чем различаются ключевые слова interface и type в TypeScript?
Вот примеры использования этих ключевых слов:
interface X { a: number b: string } type X = { a: number b: string };
В отличие от объявления интерфейса, которое всегда представляет именованный тип объекта, применение ключевого слова
type
позволяет задать псевдоним для любой разновидности типа, включая примитивные типы, типы-объединения и типы-пересечения.При использовании ключевого слова
type
вместо ключевого словаinterface
теряются следующие возможности:- Интерфейс может быть использован в выражении
extends
илиimplements
, а псевдоним для литерала объектного типа — нет. - Интерфейс может иметь несколько объединённых объявлений, а при использовании ключевого слова
type
эта возможность не доступна.
- Интерфейс может быть использован в выражении
-
18. Расскажите о том, когда в 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. Что такое внешние объявления переменных в TypeScript и когда их нужно использовать?
Внешнее объявление переменной (ambient declaration) — это механизм, который позволяет сообщать компилятору TypeScript о том, что некий исходный код существует где-то за пределами текущего файла. Внешние объявления помогают интегрировать в TS-программы сторонние JavaScript-библиотеки.
Эти объявления делают в файле объявления типов с расширением .d.ts. Внешние переменные или модули объявляют так:
declare module Module_Name { }
Файлы, в которых находится внешний код, должны быть подключены в TS-файле, использующем их, так:
/// <reference path=" Sample.d.ts"></reference>
-
20. Можно ли автоматически генерировать файлы объявлений TypeScript из JS-библиотек?
avaScript не всегда содержит достаточно информации, которая позволяет TypeScript автоматически выводить типы. Поэтому практически невозможно автоматически создавать объявления типов, основанные на JavaScript. Однако можно попытаться это сделать, воспользовавшись следующими инструментами:
- Microsoft/dts-gen — официальное средство, используемое Microsoft как отправная точка при создании объявлений типов.
- dtsmake — многообещающий инструмент для автоматического создания объявлений типов на основе JS-файлов, находящийся в процессе разработки. Он зависит от системы анализа кода Tern, которую используют некоторые редакторы для реализации механизма автозавершения при вводе JS-кода.