Как стать автором
Обновить

Комментарии 27

type Languages = {ru: boolean} | {en: boolean}
// теперь нельзя использовать ru и en вместе

Можно. Чтобы гарантировать ровно одно из двух полей, надо "запретить" второе:

type Languages = {ru: boolean, en?: never} | {ru?: never, en: boolean};

Так же тут следует упомянуть discriminated unions

Спасибо за замечание, изменил пример, чтобы не вводить в заблуждение

К слову, тут же может пригодиться и infer для получения аргументов и возвращаемого значения:

Тут лучше подойдут стандартные Parameters и ReturnType.

Полностью согласен, это для примера, что с infer и функциями можно создавать довольно сложные типы

as const

Допустим, у вас есть массив, который приходит с бекенда

Пример неудачный - as const можно использовать только для compile-time констант, для данных с бекенда он ничем не поможет.

Используйте их везде, где только возможно.

И превратите свой код в невыносимую перловку из угловых скобочек вопросиков и неверов.

Да, правильнее было бы сказать - используйте только при необходимости. Пример:

// ненужный генерик
function getLen<T extends {length: number}>(x: T): number {
    return x.length;
}

// нормальный вариант без излишств
function getLen(x: {length: number}): number {
    return x.length;
}

Советы из TFM по генерикам - тут

Используйте readonly по умолчанию, это позволит избежать случайного перезаписывания типов в вашем интерфейсе

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

И стоит добавить сюда описание что такое type и что такое interface. В чем между ними основная разница и что предпочтительнее использовать. К сожалению, последнее мало кто понимает.

что такое type и что такое interface. В чем между ними основная разница и что предпочтительнее использовать. К сожалению, последнее мало кто понимает.

Расскажите вашу версию, что предпочтительнее использовать - type или interface (чтоб знать что отвечать на собеседованиях). А то, судя по всему, это просто вопрос принятых соглашений на проекте.

Можете привести пример из практики где использовали

IsNumber<T> = T extends number ? true : false

Да и для остальных практик хорошо было бы привести реальные примеры

Также отмечу, что в

Лучше юнионы

Лучше исполтзовать enum для поля logindata и ещё показать как работает type guard...

Конечно! Вот пример использования типа для определения принадлежности к массиву, который в дальнейшем используется для другой конструкции, которая обрабатывает разный набор данных (иногда бывает, что для одних данных может быть разный вывод типа).

type isArray<T> = T extends unknown[] ? true : false;
type MyType<T> = isArray<T> extends true ? T : { value: T };

const arr: MyType<number[]> = [1, 2, 3]; // arr имеет тип number[]
const obj: MyType<string> = { value: 'Error' }; // obj имеет тип { value: string }

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

Лучше исполтзовать enum для поля logindata

Могли бы подробнее объяснить? Насколько я знаю, enum не считается чем-то хорошим из-за раздувания размера бандла и некоторых других проблем.

В чем профит

type isArray<T> = T extends unknown[] ? true : false;
type MyType<T> = isArray<T> extends true ? T : { value: T };

const arr: MyType<number[]> = [1, 2, 3]; // arr имеет тип number[]

Не лучше

const arr: number[] = ...;

В данном случае проще, но если использовать сложные типы, которые довольно похожи друг на друга, то профит имеет место быть

Раздутие размера бандла с учётом серверного сжатия стримится к нулю)))

Не надо экономить на enum

А какие "некоторые другие проблемы" есть?

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

Т.е. это является проблемой?

С таким подходом и зная про as any мы можем сказать, что весь ts не безопасный

В целом, он нарушает концепцию Typescript (добавление слоя абстракций в виде типов), так как добавляет enum в код (который ещё и можно модифицировать, а потом ловить из-за этого ошибки), а не просто удаляет код, как те же перечисления

type IsNumber<T> = T extends number ? true : false

в таких случаях стоит еще не забывать про дистрибутивность, т.к. например IsNumber<1 | string> будет не false, а boolean, что далеко не всегда является ожидаемым поведением. чтобы отключить дистрибутивность, можно использовать специальный синтаксический костыль:

type IsNumber<T> = [T] extends [number] ? true : false

Согласен. Но теперь надо ещё помнить, что IsNumber<never> тоже стало true, и это в каких-то кейсах может быть неожиданно, хотя и логично.

type IsNumber<T> = [T] extends [number] ? [T] extends [never] ? false : true : false

В таком случае, лучше использовать дополнительную проверку на never, но теперь это выглядит не так изящно

function returnType<T>(arg: T): T {
  return arg;
}

Пожалуйста, не делайте так! такие функции с большими типами и интерфейсами потом "тяжело" тестировать, а именно писать обертки для моков.

Делайте чище и правильнее:

function doSomething<T extends {usedPropA: string; usedPropB: boolean}>(arg: T): T {...}

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

> Пожалуйста, не делайте так! такие функции с большими типами и интерфейсами потом "тяжело" тестировать, а именно писать обертки для моков.

Снижать качество кода ради того, чтобы он "проще тестировался" - обычно плохая идея.

Что?? Это не только из-за тестирования (а тестов обычно тоже много и важно его пистаь быстро и качественно), но и то что такую функцию можно переиспользовать с ругими объектами которые поддерживают требуемый интерфейс.

В примере T - любой тип. Вообще любой, задумайтесь! Вам не избежать рантайм ерроров. А вот если вы точно укажете что T это дженерик с минимальным требуемым интерфейсом по это будет корректно и ваш джун не натворит делов

В примере T - любой тип. Вообще любой, задумайтесь! Вам не избежать рантайм ерроров.

А можно привести пример - что надо передать в ф-ю returnType, чтобы получить рантайм эррор?

А вот если вы точно укажете что T это дженерик с минимальным требуемым интерфейсом по это будет корректно и ваш джун не натворит делов

Если генерик Т не будет удовлетворять минимальным требованиям то тайпчекер такой код просто не пропустит. Если пропустил - значит все в порядке с минимальными требованиями.

Не совсем понятно, что именно не делать. Функция returnType из примера ничего не делает со своим arg, потому не накладывает ограничений. Если внутри неё понадобится использовать arg.usedPropA, то разумеется, TS потребует указать такие ограничения через extends.

Если не понятно для чего, значит вы не так часто пользовались TSом

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории