Рекомендации и запреты
Общие типы
Number, String, Boolean, Symbol и Object
❌ Не следует использовать типы Number, String, Boolean, Symbol, или Object. Эти типы относятся к не примитивным упакованным объектам, которые практически никогда не используются должным образом в коде JavaScript.
/* WRONG */ function reverse(s: String): String;
✅ Следует использовать типы number, string, boolean, и symbol.
/* OK */ function reverse(s: string): string;
Вместо Object, используйте тип object (добавлен в TypeScript 2.2).
Дженерики
❌ Не следует использовать дженерический тип, который не использует свой параметр типа. Подробнее см. на странице FAQ TypeScript.
any
❌ Не следует использовать any в качестве типа, если вы не занимаетесь миграцией проекта JavaScript в TypeScript. Компилятор эффективно рассматривает any как «отключить проверку типов для этого элемента». Это похоже на добавление комментария @ts-ignore к каждому использованию переменной. Это может быть очень полезно при первой миграции проекта JavaScript в TypeScript, так как вы можете установить тип для элементов, которые еще не мигрировали, как any, но в полном проекте TypeScript вы отключаете проверку типов для любых частей программы, использующих его.
В случаях, когда вы не знаете, какой тип хотите принять, или когда нужно принять любой тип, потому что вы будете слепо передавать его без взаимодействия с ним, можно использовать unknown.
Типы обратных вызовов
Типы возвращаемых значений обратных вызовов
❌ Не следует использовать тип возвращаемого значения any для обратных вызовов, значение которых будет проигнорировано:
/* WRONG */
function fn(x: () => any) {
x();
} ✅ Следует использовать тип возвращаемого значения void для обратных вызовов, значение которых будет проигнорировано:
/* OK */
function fn(x: () => void) {
x();
} ❔ Почему: Использование void безопаснее, поскольку предотвращает случайное использование возвращаемого значения x не проверенным способом:
function fn(x: () => void) {
var k = x(); // oops! meant to do something else
k.doSomething(); // error, but would be OK if the return type had been 'any'
} Необязательные параметры в обратных вызовах
❌ Не следует использовать необязательные параметры в обратных вызовах, если вы не имеете в виду это:
/* WRONG */
interface Fetcher {
getObject(done: (data: unknown, elapsedTime?: number) => void): void;
} Это имеет очень специфическое значение: обратный вызов done может быть вызван с 1 аргументом или с 2 аргументами. Вероятно, автор хотел сказать, что обратный вызов, возможно, не зависит от параметра elapsedTime, но нет необходимости делать параметр необязательным для достижения этого — всегда допустимо предоставить обратный вызов, принимающий меньше аргументов.
✅ Следует писать параметры обратного вызова как не необязательные:
/* OK */
interface Fetcher {
getObject(done: (data: unknown, elapsedTime: number) => void): void;
} Перегрузки и обратные вызовы
❌ Не следует писать отдельные перегрузки, различающиеся только числом аргументов обратного вызова:
/* WRONG */ declare function beforeAll(action: () => void, timeout?: number): void; declare function beforeAll( action: (done: DoneFn) => void, timeout?: number ): void;
✅ Следует писать единственную перегрузку, используя максимальное число аргументов:
/* OK */ declare function beforeAll( action: (done: DoneFn) => void, timeout?: number ): void;
❔ Почему: Для обратного вызова всегда допустимо игнорировать параметр, поэтому нет необходимости в более короткой перегрузке. Предоставление более короткого обратного вызова в первую очередь позволяет передать некорректно типизированные функции, потому что они соответствуют первой перегрузке.
Перегрузки функций
Порядок
❌ Не следует помещать более общие перегрузки перед более конкретными перегрузками:
/* WRONG */ declare function fn(x: unknown): unknown; declare function fn(x: HTMLElement): number; declare function fn(x: HTMLDivElement): string; var myElem: HTMLDivElement; var x = fn(myElem); // x: unknown, wat?
✅ Следует сортировать перегрузки, помещая более общие сигнатуры после более конкретных сигнатур:
/* OK */ declare function fn(x: HTMLDivElement): string; declare function fn(x: HTMLElement): number; declare function fn(x: unknown): unknown; var myElem: HTMLDivElement; var x = fn(myElem); // x: string, :)
❔ Почему: TypeScript выбирает первую подходящую перегрузку при разрешении вызовов функций. Когда более ранние перегрузки «более общие», чем последующие, последующие перегрузки фактически скрываются и не могут быть вызваны.
Использование необязательных параметров
❌ Не следует писать несколько перегрузок, различающихся только конечными параметрами:
/* WRONG */
interface Example {
diff(one: string): number;
diff(one: string, two: string): number;
diff(one: string, two: string, three: boolean): number;
} ✅ Следует использовать необязательные параметры, когда это возможно:
/* OK */
interface Example {
diff(one: string, two?: string, three?: boolean): number;
} Обратите внимание, что это слияние должно происходить только тогда, когда у всех перегрузок одинаковый тип возвращаемого значения.
❔ Почему: Это важно по двум причинам.
TypeScript разрешает совместимость подписей, проверяя, можно ли вызвать любую подпись целевого объекта с аргументами источника, и разрешены дополнительные аргументы. Например, этот код раскрывает ошибку только в том случае, если подпись правильно записана с использованием необязательных параметров:
function fn(x: (a: string, b: number, c: number) => void) {}
var x: Example;
// When written with overloads, OK -- used first overload
// When written with optionals, correctly an error
fn(x.diff); Вторая причина — когда потребитель использует функцию «строгой проверки на null» в TypeScript. Поскольку не указанные параметры отображаются как undefined в JavaScript, обычно нормально передавать явное undefined функции с необязательными аргументами. Например, этот код должен быть корректным в строгих проверках на null:
var x: Example;
// When written with overloads, incorrectly an error because of passing 'undefined' to 'string'
// When written with optionals, correctly OK
x.diff("something", true ? undefined : "hour"); Использование объединений типов
❌ Не следует писать перегрузки, которые различаются по типу только в одном положении аргумента:
/* WRONG */
interface Moment {
utcOffset(): number;
utcOffset(b: number): Moment;
utcOffset(b: string): Moment;
} ✅ Следует использовать объединения типов, когда это возможно:
/* OK */
interface Moment {
utcOffset(): number;
utcOffset(b: number | string): Moment;
} Обратите внимание, что мы не сделали b необязательным, поскольку типы возвращаемых значений сигнатур различаются.
❔ Почему: Это важно для людей, «передающих» значение в вашу функцию:
function fn(x: string): Moment;
function fn(x: number): Moment;
function fn(x: number | string) {
// When written with separate overloads, incorrectly an error
// When written with union types, correctly OK
return moment().utcOffset(x);
}
© 2012-2023 Microsoft
Licensed under the Apache License, Version 2.0.
https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html