Pull to refresh
4
0.2
Send message

В этом последнем примере - частный случай: объектный литерал, создаваемый вручную "на лету". Он дополнительно проверяется на отсутствие посторонних полей в объекте. А для всего остального - только проверка указанных полей.

Но только если B | A заменить на { x: number, y?: number }, то совсем всё по-другому, проверки делаются корректно.

Ваш второй пример тоже не сообщает об ошибке типов, если немного поменять obj

Глянул код, не смог найти причину ошибки. Взял вариан с моим решением, и написал так, поменяв Paths, вроде попустило. Навскидку примерно всё то же самое, почему работает, непонятно. Есть дублирование кода в Paths, но не стал заморачиваться.

Если речь об этом, то такая проблема не только в интерфейсах, но и в типах и даже, как ни прискорбно, в классах

{} - это не "пустой объект", а "что угодно, кроме null и undefined". В переменную х можно воткнуть любое значение, кроме этих двух.

Передаваемый объект соответствует типу А по правилам TS, значит соответствует и типу A | B

В моем примере A | B вообще должно схлопнуться до A, потому что B - подтип A. Но даже если и не был подтипом, всё равно проверка недостаточна.

Ну, например, что в виде параметра может закатиться объект с полем y, которое не number, а TS уверен, что проверки на наличие поля достаточно. Результат можно увидеть, нажав кнопку Run.

Да и в структурной типизации TS хватает всякого странного, например вот

Но, кажется, сам по себе промис определен именно в спецификации языка. Т.е. среда выполнения обязана предоставить микротаски для колбэков then (обычные таски, которые "макро", на голом js создать не из чего, а вот зарезолвленный промис - вполне).

Да, согласен. Поскольку здесь any требуется для бивариантности параметра, то ещё можно это указать явно:

type Guard<P = unknown, T extends P = P> = {f(x: P): x is T}['f'];

а в SomeOf уже добавить проверку для P. И never в типе SomeOf я бы поменял на (x: never) => x is never, чтобы нельзя было передать в тот же Array.filter или ещё куда. Итого:

type Guard<P = unknown, T extends P = P> = {f(x: P): x is T}['f'];
type Param<F> = [F] extends [(x: infer P) => unknown] ? P : never;

type SomeOf<T extends Guard[]> =
  T[number] extends Guard<infer P extends Param<T[number]>, infer R>
    ? (x: P) => x is R
    : (x: never) => x is never;

function someOf<T extends Guard[]>(...guards: T) {
    return ((x: never) => guards.some(guard => guard(x))) as SomeOf<T>;
}

----

Вообще типизация метода Array.filter вызывает вопросы. Например, вот такой простой кейс:

const numArr: (1 | 2 | 3)[] = [];
const guard = (x: number): x is 3 | 4 => true;
const filtered = numArr.filter(guard); // (2 | 1 | 3)[]

Здесь filtered по логике должен быть 3[], но увы. Кажется, правильнее было бы как-то так:

filter<S>(predicate: (value: S | Exclude<T, S>) => value is S, thisArg?: any): (S & T)[];

и тогда по идее мои поправки из первого поста будут лишними.

Я допускаю, что это плохо в некоторых отдельных случаях. Но по поводу "всегда" интересно увидеть обоснование.

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

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

Да, нормализация данных, я уж и подзабыл) Но тогда это в копилку лишнего бойлерплейта, разве нет? Мы вынуждены нормализовывать, а в мобх можно обойтись просто массивом объектов.

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

Интересная статья, спасибо.

Применение any вместо never в качестве типа по умолчанию — вынужденная необходимость из-за ограничения T extends P, вытекающего из природы тайпгарда

можно заменить на unknown

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

Это стоит сделать, потому что прямо сейчас добавление "лишнего" пункта в someOf молча ломает типизацию, и фильтр перестает работать в режиме тайпгарда. Если в самом последнем примере добавить в копилку, допустим, is(2), то filtered будет Event[]

Допилил этот кейс, небольшие изменения в строках 41-48

в этом примере несколько другое. А именно, что конкретно брать за "N" в О-большом. Если длину стороны матрицы, то так и будет O(N^2)

Только если соседние элементы различаются на единицу. В задаче нет такого условия.

Правило 4 применимо для простого кейса - когда сложность внутреннего цикла не зависит от текущей итерации внешнего. Если зависит, то придется аккуратно просуммировать сложности, и там может выйти по разному. Например, в пузырьковой сортировке так и остается O(N^2), а вот для того же heapify внезапно оказывается O(N)

Можете кинуть ссылку на Playground с ошибкой?

1
23 ...

Information

Rating
2,115-th
Location
Москва, Москва и Московская обл., Россия
Registered
Activity

Specialization

Frontend Developer
Senior
JavaScript
TypeScript
React
HTML
CSS
Web development
Redux
MobX
Webpack