Pull to refresh

Comments 69

Вас не смущает, что в разъяснении вашего ответа на 4-й вопрос вы, фактически, объясняете, что он неправильный?
Имею малый опыт с TS, но неужели это и правда является ошибкой?
/* Неверно*/
interface Fetcher {
    getObject(done: (data: any, elapsedTime?: number) => void): void;
}

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

Красивей, это так?
interface Fetcher {
    getObject(done: (data: any) => void): void;
    getObject(done: (data: any, elapsedTime: number) => void): void;
}

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


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

interface Fetcher {
    getObject(done: IGetObjectCallback): void;
}

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


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

Там явное приведение типа не нужно?
a.getObject(<IGetObjectCallback>(x: any, y: number) => {})

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

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

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


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

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

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

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

new A().b.b.c.toLowerCase();


2) Нет таких способов. И в целом, я думаю, что использовать декораторы, как множественное наследование — bad practice. Если нужно добавить в класс функциональность с публичным интерфейсом, лучше сделать это явно, без магии. А декораторы как раз предназначены для того, чтобы изменить поведение класса, не меняя интерфейс.
Не понимаю, как это поможет? Приведите полный пример задачи.
UFO just landed and posted this here
Половина вопросов вытекает из знания JS.
2. Русское наименование дженериков меня вообще в ступор ввело, хотя знаю их еще с C# когда писал в 2005 году
3. Это не реализация, а описание. В принципе таким же методом можно и геттер объявить.
6. Спрашивать про .map в TS? Реально? Если он о них не знает, значит и остальное просто 0.
13. В 2018 кто то вообще пользуется module?
15. Всё так. Никогда не думали что все мыслят по разному и то что принятно в одной команде, легко не принято в другой?
19. Если вы до сих пор используете reference path, я вам сочуствую.
20. Возможность может быть и есть, но нежизнеспособно.
А объясните кто-нибудь ньюфагу от мира TS, что не так с module и reference path?
Документация советует использовать namespace, а не module, чтобы избежать когнитивных коллизий с ES Modules.
> Спрашивать про .map в TS? Реально? Если он о них не знает, значит и остальное просто 0.

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

А вообще я к тому, что заявлять «Если он о них не знает, значит и остальное просто 0.» в отношении какого-то малозначимого и не всеми используемого параметра трансляции, как-то очень близко к ложноотрицательному выводу. Тем более в наше время, когда правит, как вы заметили, разделение труда, и настройками сборок нередко занимаются специально обученные люди, которым этим мапы даром не сдались и сделают они их, в лучшем случае, если моё начальство обоснует их необходимость перед их начальством, плюс докажет, например, третьим людям, что создание этих мап в процессе сборки не являтся угрозой безопасности или является, но вторые люди исключат их утечку.
Ну тут сильно не согласен. Без карт отладка любого транспилированного модуля превращается в нечто нетривиальное. Да и зачем сидеть разбирать транспилированный код, если можно прекрасно дебажить исходный? Я как раз против того, чтобы этим занимались необученные люди. В нашей конторе несколько проектов пишущих фронт и я бы не сказал, что настройка сборки может производиться необученными людьми. Это создает кучу проблем и неудобств.
А насчет угроз безопасности, это смешно… Никто не заставляет эти мапы отдавать заказчику и т.п. Да и сами должны понимать, что JS код не защищен от слова НИКАК
Ну вот у меня три варианта: или отлаживать без карт, или пытаться пробивать (в том числе доказывая, что возможная утечка мап-файлов ничего опасного кроме того что и так есть в обфуцированном джс не даст, ну или хотя бы что вероятность такой утечки пренебрежимо мала) чтобы создали такую задачу и на кого-то назначили. Ну или лезть своими необученными руками в процесс сборки и предоставить готвое решение, но с теми ж доказываниями и убеждениями. Плюс доказывания и убеждения, что рабочего времени я на задачу практически не тратил, при этом рабочий код никуда не выносил, а, условно, в офисе ночевал и пилил сборку.
Да и зачем сидеть разбирать транспилированный код, если можно прекрасно дебажить исходный?

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


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

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


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

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

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


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

UFO just landed and posted this here

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

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

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

UFO just landed and posted this here
Какие вопросы вы задали бы тому, кто проходит собеседование, претендуя на должность, требующую знания TypeScript?
Прочитайте этот список из 20 вопросов и ответов и объясните, почему их ни в коем случае нельзя использовать при отборе кандидатов со знанием TypeScript.
if (value) {
}

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

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

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

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

Значения 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 с одинаковой семантикой, это только вероятность ошибок увеличит.
Увеличит если только позволять при одинаковой семантике использовать тождественное сравнение.

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

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

А еще пишете
i = i + 1
// вместо
i++

// или

object.prototype.method.call(object, param1, param2)
// вместо
object.method(param1, param2)

и другие усложняющие чтение кода глупости на случай, если ваш код будет читать мимо пробегающий доставщик пиццы, который прочитал только первые две главы книги о JavaScript?

Никакого привидения типов при сравнении null и undefined не происходит, они просто нестрого равны:
If x is null and y is undefined, return true.
If x is undefined and y is null, return true.


Умные люди, которые улучшали этот стандарт понимают, что в любом нормальном коде эти два значения тождественны и необходим простой и читабельный паттерн для сравнения, потому и ввели его явно. Используется он давно — я видел его активное применение еще в старом коде Mootools — все интересующиеся языком его давно знают
object.constructor.prototype, конечно
Ну, это же не для всех методов, а только для унаследованных. Иначе будет
TypeError: object.prototype.method is undefined
Я ведь исправил ошибку свою) [].constructor.prototype.push — так есть все методы, если всё корректно с constructor
Я ведь исправил ошибку свою)
Это которую?
let object = { method: (p1, p2) => console.log(p1,p2) };
object.constructor.prototype.method.call(object, param1, param2);

TypeError: object.constructor.prototype.method is undefined

let obj2 = new function(){ this.method = (p1, p2) => console.log(p1,p2); }
obj2.constructor.prototype.method.call(object, param1, param2);

TypeError: obj2.constructor.prototype.method is undefined
Ах, вы об этом. Я и не говорил, что решение идеальное. В оригинальном коде, с которого началась ветка тоже можно сломать, написав var undefined = true
можно сломать, написав var undefined = true
Написать можно, только он не сломается ;)
window.undefined — read-only свойство.
В глобальном пространстве может оно и ридонли, а вот в локальном — вполне себе записываемое.

(function (undefined) {
  function foo (arg) {
    if (a === null || a === undefined) {
      console.log('success');
    } else {
      console.log('fail');
    }
  }

  foo(); // fail
})(true)
Хитро́! Такое даже не проверял.

Ну значит я тот самый мимо-пробегающий доставщик пиццы. У меня код с == не пройдёт code review. К +=, apply, call претензий не имею.

Та не, доставщик пиццы — это тот, на которого рассчитано «а вдруг кто-то не читал документацию».

Вы — обычный синьйор, который выбирает плохое решение вместо хорошего исключительно из-за самодурства)

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


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

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


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

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


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


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

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


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

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

Примерно так:
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

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

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

;)

Вижу ключ как конкатенацию строк: acc += String(arg). Получается вызовы f(“a”, “bc”) и f(“ab”, “c”) попадут на одинаковый ключ в кеше?
В этом примере да, это не код для продакшена а для демонстрации возможности.
Такая реализация не поддерживает мемоизацию дженерик-функций. Зачем это может понадобиться — другой вопрос :)

Простите, но


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

if (value) {
}

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

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

«1+1» (или «Неприкасаемые»), шикарный фильм :) Только аргентинскую версию не смотрите.
А что за аргентинская версия?
В прошлом году аргентинцы сняли свою версию фильма, которая называется так же (1+1), в ней всё то же самое по смыслу и шуткам, но плохо всё остальное: игра актёров, сами актёры итд. Не знаю как так произошло :) видимо там нашли сценарий где-то на старом диске и решили снять киношку, не зная, что шедевр уже был снят.
Sign up to leave a comment.