Comments 27
type Languages = {ru: boolean} | {en: boolean}
// теперь нельзя использовать ru и en вместе
Можно. Чтобы гарантировать ровно одно из двух полей, надо "запретить" второе:
type Languages = {ru: boolean, en?: never} | {ru?: never, en: boolean};
Так же тут следует упомянуть discriminated unions
К слову, тут же может пригодиться и infer для получения аргументов и возвращаемого значения:
Тут лучше подойдут стандартные Parameters
и ReturnType
.
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
А какие "некоторые другие проблемы" есть?
Например, проблемы с безопасностью, как в этой статье https://dev.to/ivanzm123/dont-use-enums-in-typescript-they-are-very-dangerous-57bh
В этой статье говорится, что могут быть проблемы если кто-то догадается передавать числа в методы в которых сигнатура enum...
Т.е. это является проблемой?
С таким подходом и зная про as any мы можем сказать, что весь ts не безопасный
type IsNumber<T> = T extends number ? true : false
в таких случаях стоит еще не забывать про дистрибутивность, т.к. например IsNumber<1 | string> будет не false, а boolean, что далеко не всегда является ожидаемым поведением. чтобы отключить дистрибутивность, можно использовать специальный синтаксический костыль:
type IsNumber<T> = [T] extends [number] ? true : false
Согласен. Но теперь надо ещё помнить, что IsNumber<never> тоже стало true, и это в каких-то кейсах может быть неожиданно, хотя и логично.
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
.
Typescript: лучшие практики