Spec-Zone.ru › TypeScript 5.1

Рекомендации и запреты

Общие типы

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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API