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

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



Материал, перевод которого мы сегодня публикуем, содержит разбор двадцати вопросов, которые вполне могут задать тому, кто собирается пройти собеседование, претендуя на позицию 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<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 (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};

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

> Источник


Просто очень любим этот фильм )

Вопрос №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-файле, использующем их, так:

/// <reference path=" Sample.d.ts"></reference>

> Источник

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


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

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

> Источник

Итоги


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

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


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

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

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


  1. k12th
    13.08.2018 11:44
    +2

    module Vehicle {

    ...


    /// <reference path=" Sample.d.ts"></reference>

    Шел 2018 год...


  1. Chamie
    13.08.2018 12:43

    Вас не смущает, что в разъяснении вашего ответа на 4-й вопрос вы, фактически, объясняете, что он неправильный?


  1. Smokin
    13.08.2018 12:47

    Имею малый опыт с TS, но неужели это и правда является ошибкой?

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


    1. k12th
      13.08.2018 12:57

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


      1. Smokin
        13.08.2018 13:33

        Красивей, это так?

        interface Fetcher {
            getObject(done: (data: any) => void): void;
            getObject(done: (data: any, elapsedTime: number) => void): void;
        }
        


        1. k12th
          13.08.2018 13:39
          +1

          Не, скорее так:


          interface IGetObjectCallback {
              (data: any): void;
              (data: any, elapsedTime: number): void;
          }
          
          interface Fetcher {
              getObject(done: IGetObjectCallback): void;
          }


          1. Smokin
            13.08.2018 15:07

            Понятно, спасибо.


          1. printercu
            14.08.2018 15:46

            Что-то не работает.


            class A implements Fetcher {
                getObject(done: IGetObjectCallback) {}
            }
            
            const a = new A
            a.getObject((x: any, y: number) => {})
            // TS2345: Argument of type '(x: any, y: number) => void' is not assignable to parameter of type 'IGetObjectCallback'.

            Официальные доки советуют использовать опциональные параметры вместо перегрузок: https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html#use-optional-parameters


            1. Chamie
              14.08.2018 15:51

              Там явное приведение типа не нужно?

              a.getObject(<IGetObjectCallback>(x: any, y: number) => {})


              1. printercu
                14.08.2018 16:02
                -1

                Вы хотите, чтобы я за вас проверил?)


                1. Chamie
                  14.08.2018 16:28
                  +1

                  Ну, если вам интересно, почему ошибка, то проверяйте. У вас под рукой, вроде бы, есть развёрнутое TS-окружение, откуда вы ошибку привели.
                  У меня нет задачи разобраться в этом примере, так что это вы не за меня, а за себя проверите.


            1. printercu
              14.08.2018 16:00

              Пример с Fetcher тоже из этой документации. В статью почему-то не добавили совет и пример:


              Пишите параметры колбэков, как не-опциональные:
              /* OK */
              interface Fetcher {
              getObject(done: (data: any, elapsedTime: number) => void): void;
              }

              "… всегда можно передать коллбэк, который принимает меньшее число аргументов" будет правильнее.


          1. k12th
            14.08.2018 17:08
            +1

            Поправка: красивее по мнению автора оригинала.


  1. VolCh
    13.08.2018 12:49

    Несколько для собеседования, сколько для использования, ничего внятного нагуглить не смог
    1) Какие есть способы задать тип значения равным классу, внутри которого тип задаётся без использования его имени (по типу this.constructor в JS или self в PHP?
    2) Какие есть способы задать тип значения, обозначающих конкретный декорируемый конкретным декоратор класс? Ну есть что-то вроде @loggable class Entity {}. просто где-то использовать let obj: Entity = new Entity() не рабоатет.


    1. arvitaly
      14.08.2018 01:20

      1) Можно с помощью this, хотя лучше писать с использованием имени явно.

      class A {
          public b: this;
          public c: string;
      }
      
      new A().b.b.c.toLowerCase();
      


      2) Нет таких способов. И в целом, я думаю, что использовать декораторы, как множественное наследование — bad practice. Если нужно добавить в класс функциональность с публичным интерфейсом, лучше сделать это явно, без магии. А декораторы как раз предназначены для того, чтобы изменить поведение класса, не меняя интерфейс.


      1. VolCh
        14.08.2018 06:37

        2 ReturnType<decorator<A>> не вариант разве?


        1. arvitaly
          14.08.2018 07:11

          Не понимаю, как это поможет? Приведите полный пример задачи.


  1. ooki2day
    13.08.2018 15:08

    я, конечно, очень далек от JS и TS, но неужели вопрос о ключевом слове является сложным на 5 баллов по пятибальной шкале?


  1. Delagen
    13.08.2018 15:08

    Половина вопросов вытекает из знания JS.
    2. Русское наименование дженериков меня вообще в ступор ввело, хотя знаю их еще с C# когда писал в 2005 году
    3. Это не реализация, а описание. В принципе таким же методом можно и геттер объявить.
    6. Спрашивать про .map в TS? Реально? Если он о них не знает, значит и остальное просто 0.
    13. В 2018 кто то вообще пользуется module?
    15. Всё так. Никогда не думали что все мыслят по разному и то что принятно в одной команде, легко не принято в другой?
    19. Если вы до сих пор используете reference path, я вам сочуствую.
    20. Возможность может быть и есть, но нежизнеспособно.


    1. inoyakaigor
      13.08.2018 15:38

      А объясните кто-нибудь ньюфагу от мира TS, что не так с module и reference path?


      1. VolCh
        13.08.2018 15:56

        Документация советует использовать namespace, а не module, чтобы избежать когнитивных коллизий с ES Modules.


    1. VolCh
      13.08.2018 15:59

      > Спрашивать про .map в TS? Реально? Если он о них не знает, значит и остальное просто 0.

      Я вот только догадываюсь, что TS тоже может .map генерить как babel и, может быть, браузеры что-то покажут внятное. Но в обозримой перспективе даже уверенность в этом мне ничего мне не даст, весь маппинг в голове.


      1. Delagen
        13.08.2018 17:02

        Ну тут тема вопросы к собеседованию, а не ответы начинающим ;)


        1. VolCh
          13.08.2018 17:19

          Я уже давно прошёл собеседование по TS и активно использую его и на фронте, и на бэке :) Может быть когда-то и читал про map в TS и по горячим следам ответил бы, но на практике не используется у нас на проекте. И обычно это значит, что такие чисто теоретические знания быстро улетучиваются, а значит на собеседовании вполне можно и не ответить даже имея кучу актуального опыта по стэку. Думаю, у почти любого спеца можно найти пробелы в оперативных знаниях.


          1. Delagen
            13.08.2018 18:06

            Возможно вы просто пишете на нем, но никогда не лезли глубже, вдобавок наверное никогда не настраивали какую то свою сборку на нем… Это не беда, в нормальных конторах разделение труда. У нас народу немного, поэтому приходится во все тонкости вникать самому. Может быть я просто параноик и хочу знать как что работает ;)


            1. VolCh
              13.08.2018 18:23

              И это не беда, по крайней мере если это не делается за счёт работодателя без его ведома ;)

              А вообще я к тому, что заявлять «Если он о них не знает, значит и остальное просто 0.» в отношении какого-то малозначимого и не всеми используемого параметра трансляции, как-то очень близко к ложноотрицательному выводу. Тем более в наше время, когда правит, как вы заметили, разделение труда, и настройками сборок нередко занимаются специально обученные люди, которым этим мапы даром не сдались и сделают они их, в лучшем случае, если моё начальство обоснует их необходимость перед их начальством, плюс докажет, например, третьим людям, что создание этих мап в процессе сборки не являтся угрозой безопасности или является, но вторые люди исключат их утечку.


              1. Delagen
                13.08.2018 18:42

                Ну тут сильно не согласен. Без карт отладка любого транспилированного модуля превращается в нечто нетривиальное. Да и зачем сидеть разбирать транспилированный код, если можно прекрасно дебажить исходный? Я как раз против того, чтобы этим занимались необученные люди. В нашей конторе несколько проектов пишущих фронт и я бы не сказал, что настройка сборки может производиться необученными людьми. Это создает кучу проблем и неудобств.
                А насчет угроз безопасности, это смешно… Никто не заставляет эти мапы отдавать заказчику и т.п. Да и сами должны понимать, что JS код не защищен от слова НИКАК


                1. VolCh
                  13.08.2018 18:49

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


                1. faiwer
                  13.08.2018 19:49
                  +2

                  Да и зачем сидеть разбирать транспилированный код, если можно прекрасно дебажить исходный?

                  Не могу говорить за TS, но в случае JS и babel-я я давно отказался от source-map-ов. Побудило меня к этому:


                  • я натыкался на баги транспайлера, когда оный совершенно по непонятным причинам срезал куски кода совсем
                  • различные _this2 вместо this приводят к тому, что это невозможно дебажить в консоли в runtime
                  • различные (0, _flatten3.default)(...) вместо flatten(...) приводят к тому, что невозможно дебажить в консоли в runtime
                  • многие другие неочевидные моменты, закрытые от глаз
                  • многочисленные баги уже самого отладчика chrome

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


                  Возможно в TS картина точно такая же.


                  1. Delagen
                    13.08.2018 20:28

                    Ну картина везде одинаковая, но в случаях если все работает, то зачем лезть в эту муть? Понятно что, когда что-то перестает работать то надо. Сам находил пару ошибок в транспиляторе TS, но как говорится не обновляйся раньше времени и проблем не будет. Проблема в Babel это то что все размыто по куче плагинов, не всегда поддерживаемых самим сообществом. В TS хотя бы оно если заявлено, то в 99% работает.


                    1. faiwer
                      13.08.2018 20:33
                      +1

                      Я ведь не администратор, я ? разработчик. У меня перманентно что-нибудь не работает. Или новый функционал в процессе реализации, или сторонние библиотеки работают не так как ожидаешь, добавим различный отлов багов, и многое другое. Как тут без дебага? Можно даже пару дней в дебагере провести. По сути я что-нибудь отлаживаю практически каждый день. Ну может раз в два дня. Сам процесс отладки это… не одну книгу по трюкам и подходам можно написать.


                      Проблема в Babel я думаю не в плагинах и сообществе. С этим всё ок, баг в babel я пока встретил лишь единожды (кажется). Проблема скорее в том, что уж больно любит babel переименовывать сущности во всякие (0, _name4) и прочие трюки. Да, наверняка, у каждого такого трюка есть своя причина, но в отладке это сильно мешает.


                  1. F0iL
                    14.08.2018 12:12

                    многочисленные баги уже самого отладчика chrome

                    А можете рассказать, с какими конкретно багами столкнулись?
                    Репортили их? Каков результат?


                    1. faiwer
                      14.08.2018 12:25

                      Самое простое — ломалась подсветка синтаксиса не транспилированного кода. Это ломало и просмотр значений при наведении. Из того, что посложнее, хм, был какой-то мутный баг с Object.entries. Правда это скорее к v8 баг, не к тулзам. Многократно наблюдал вылеты devtool-ов. Бывало по 30-40 раз в день умирали они. Тоже касается и браузерного таба (то самое "опаньки"). Многократно наблюдал недоступность каких-либо переменных, объявленных вот прямо под носом строкой выше (видимо какие-то внутренние оптимизации). Очень странное поведение трассировки, проскакивающее какие-нибудь куски (без blackbox). Да честно говоря всего не упомнишь. Обычно я или привыкал к какому-нибудь поведению, или оно "само" исправлялось с очередным релизом. Не репортил, каюсь, грешен. Репортил по v8, репортил по рендер-движку браузера, а вот по дев-тулзам ещё не репортил. Мне кажется это нетривиальным. Вот с полгода назад у меня гарантировано девтулзы мёрли в ряде обстоятельств. Но как это можно было воспроизвести без открытия кода — я не знал. А кому нужен мутный багрепорт без примера.


                      1. k12th
                        14.08.2018 13:09

                        Я думал я один такой криворукий.


                    1. faiwer
                      14.08.2018 12:45

                      Не прошло и получаса. Дебагер остановился в пустоте. Попал я тоже туда невесть как (нажал F10). Дальнейшая отладка тоже вся "поехала".


  1. kaljan
    13.08.2018 15:28

    Не советую пользоваться этим опросником HR-ам

    Вопрос №1 (1) — Что такое TypeScript

    Что? Какой смысл задавать вопрос и ожидать услышать ответ на 40 строк?
    А если человек ответит «Не знаю» — тогда что?

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

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


    Да кому это вообще нужно

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


    ANY — Лучше не использовать any, тем самым вы полностью гасите смысл использования TypeScript

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


    if(value) — если в числе 0 — вернет false, хотя там число. Не пишите на Javascript, если пишете на Typescript

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


    в спеке все описано гораздо лучше — github.com/Microsoft/TypeScript/blob/master/doc/spec.md#3.10

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

    Копипаст детектед
    Пример использования declare — когда у вас импортирован js-файл, и вы хотите работать с jquery (хаха)

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


    ///

    Вот что говорит по этому поводу TsLint

    reference is not allowed, use imports


  1. mwizard
    13.08.2018 18:54
    +5

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


  1. arvitaly
    14.08.2018 00:56

    if (value) {
    }

    const a = {};
    if (a){
    // Это ведь JavaScript!
    }

    Такие проверки даже в JS чаще всего плохой тон, всегда нужно писать полную версию без приведения типа: a === null.


    1. TheShock
      14.08.2018 09:38

      Лучше a == null, с двойным сравнением, а не тройным, тогда он проверит сразу на null и undefined, что в большинстве архитектур будет аналогичным значениями. Нету никакого смысла их разделять и потому двойное сравнение — самое правильное


      1. arvitaly
        14.08.2018 11:12

        Настаиваю на тождественном сравнении.

        Значения null и undefined не аналогичны, null мы можем задать только сами, присвоив переменной. Для этого его и стоит использовать, когда нужно явным образом указать отсутствие значения.

        function a(b?: null | number) {
            if (b === null) {
                // Точно знаем, что b - нулевое
            } else if (typeof b === "undefined") {
                // b не задали, возможно стоит задать значение по умолчанию
            } else {
                b.toFixed();
            }
        }
        a(1);
        a(null);
        a();


        Нет никакого смысла позволять переменной быть и null и undefined с одинаковой семантикой, это только вероятность ошибок увеличит.


        1. VolCh
          14.08.2018 11:21

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


        1. TheShock
          14.08.2018 12:29

          И зачем? Какая разница в реальном коде будет в первом и втором условии? Полагаться на разницу между null и undefined — признак попахивающего кода и никакое адекватное речью это не пропустит. Если всегда использовать двойное сравнение — количество ошибок сводится к минимуму, а если вы хотите дать возможность не передавать параметр, то хорошим тоном будет дать возможность пропустить его через нул


      1. faiwer
        14.08.2018 12:29

        Между a == null и a === null || a === undefined я бы всегда выбирал второе. Оно очевидно до предела. В первом же случае приходится чесать репу и вспоминать неочевидные правила приведения типов, кому оно надо?


  1. subzey
    14.08.2018 01:25
    +1

    Интервью на TS с одним вопросом: Напишите memoize (даже без параметра-хэшера, чтобы не убивать мозг).


    Если написанная функция работает правильно и возвращает не any, а дженерик, значит, кандидат умеет и привык пользоваться сильными сторонами TS. Остальное — мелочи


    1. amakhrov
      14.08.2018 08:08

      А кстати, как написать memoize, принимающий функцию с любым количеством аргументов?


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



      1. subzey
        14.08.2018 10:23

        Вообще любого количества любых аргументов?


        Я бы, наверное, строил дерево из


        interface HashNode<T> {
          map: Map<any, HashNode<T>>;
          weakMap: WeakMap<any, HashNode<T>>;
          value?: T;
        }

        И перебирая аргументы слева направо, траверсил бы его от корня:


        • Если последующий аргумент объект — следующая текущая нода становится тем, что вернёт .weakMap.get(arg) (WeakMap, чтобы утечек памяти не плодить)
        • Если примитив — .map.get(arg)
        • Если аргументы ещё не кончились, а текущая нода оказалась null, значит, промахнулись мимо кэша
        • Если аргументы кончились и в текущей ноде есть собственное свойство value, это вот оно.

        Ну или как-то так. Только такая мемоизация может оказаться "дороже", чем просто обёртываемую функцию дёрнуть)


      1. CepbluBoJlk
        14.08.2018 11:32

        Примерно так:

        function memoize<TS extends any[], R>(
            fn: (...args: TS) => R, 
            keyFn?: (...args: TS) => string): (...args: TS) => R {
              
          const cache: Record<string, { value: R }> = {};
          return (...args: TS) => {
            const key = (keyFn || (args => args.reduce((acc, arg) => (acc += String(arg)), "")))(args);
            return (cache[key] || (cache[key] = { value: fn(...args) })).value;
          };
        }
        
        const fn1 = (a: string) => 1;
        const fn2 = (a: string, b: boolean) => true;
        const fn3 = (a: string, b: boolean, f: (x: number) => void) => 1;
        const fn4 = (a: string, b: boolean, o: object) => 1;
        
        // (a: string) => number
        const mfn1 = memoize(fn1); 
        
        // (a: string, b: boolean) => boolean
        const mfn2 = memoize(fn2);
        
        // (a: string, b: boolean, f: (x: number) => void) => number
        const mfn3 = memoize(fn3, (a, b, fn) => `${a}, ${b}, ${fn(1)}`);
        
        // (a: string, b: boolean, o: object) => number
        const mfn4 = memoize(fn4);
        

        Правда нужен typescript версии > 3, blogs.msdn.microsoft.com/typescript/2018/07/12/announcing-typescript-3-0-rc


        1. amakhrov
          14.08.2018 17:56

          Спасибо! Это реально классно!


        1. subzey
          15.08.2018 01:31

          const memoIdentity = memoize(v => v);
          console.log(memoIdentity('hasOwnProperty'));

          ;)


  1. mSnus
    14.08.2018 04:31

    Простите, но


    Как в TypeScript проверять значения на равенство null и undefined?

    if (value) {
    }

    Вроде как это проверка на НЕравенство? Разные по смыслу вещи.


  1. monah_tuk
    14.08.2018 11:04

    Напомните название фильма, хочу пересмотреть, а вылетело из головы.


    1. Boomburum
      14.08.2018 12:43

      «1+1» (или «Неприкасаемые»), шикарный фильм :) Только аргентинскую версию не смотрите.


      1. Valery4
        14.08.2018 20:38

        А что за аргентинская версия?


        1. Boomburum
          15.08.2018 01:10
          +1

          В прошлом году аргентинцы сняли свою версию фильма, которая называется так же (1+1), в ней всё то же самое по смыслу и шуткам, но плохо всё остальное: игра актёров, сами актёры итд. Не знаю как так произошло :) видимо там нашли сценарий где-то на старом диске и решили снять киношку, не зная, что шедевр уже был снят.