Перейти к содержанию

Как надо и как нет

Основные типы

Number, String, Boolean, и Object

Никогда не используйте типы Number, String, Boolean или Object. Эти типы ссылаются на непримитивные упакованные типы, которые почти никогда не используются в JavaScript-коде.

/* НЕПРАВИЛЬНО */
function reverse(s: String): String

Используйте типы number, string и boolean.

/* ПРАВИЛЬНО */
function reverse(s: string): string

Если вам хочется применить тип Object, используйте вместо него any. На данный момент в TypeScript нет способа указать, что объект должен быть "непримитивным".

Обобщения

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

Типы функций обратного вызова

Типы возвращаемых значений функций обратного вызова

Не используйте any в качестве типа возвращаемого значения для функций обратного вызова, чье возвращаемое значение игнорируется:

/* НЕПРАВИЛЬНО */
function fn(x: () => any) {
  x()
}

Используйте тип void в подобных случаях:

/* ПРАВИЛЬНО */
function fn(x: () => void) {
  x()
}

Почему: использование void более безопасно, поскольку защищает от случайного использования возвращаемого значения x без проверки типов:

function fn(x: () => void) {
  var k = x() // упс! планировалось написать что-то другое
  k.doSomething() // ошибка, но все было бы нормально, если бы тип возвращаемого значения был 'any'
}

Необязательные параметры в функциях обратного вызова

Не используйте необязательные параметры в функциях обратного вызова, кроме случаев, когда они необходимы:

/* НЕПРАВИЛЬНО */
interface Fetcher {
  getObject(done: (data: any, elapsedTime?: number) => void): void
}

Это имеет следующее значение: функция обратного вызова done может быть вызвана либо с одним, либо с двумя аргументами. Разработчик, вероятно, намеревался выразить то, что функция обратного вызова может не обращать внимания на параметр elapsedTime, однако для этого не нужно делать параметр необязательным -- передача функции обратного вызова, которая принимает меньшее число аргументов, всегда допускается.

Всегда используйте обязательные параметры для функции обратного вызова:

/* ПРАВИЛЬНО */
interface Fetcher {
  getObject(done: (data: any, elapsedTime: number) => void): void
}

Перегрузки и функции обратного вызова

Не создавайте отдельных перегрузок, различающихся только числом аргументов у функции обратного вызова:

/* НЕПРАВИЛЬНО */
declare function beforeAll(action: () => void, timeout?: number): void
declare function beforeAll(action: (done: DoneFn) => void, timeout?: number): void

Создавайте одну общую перегрузку с максимальным числом аргументов:

/* ПРАВИЛЬНО */
declare function beforeAll(action: (done: DoneFn) => void, timeout?: number): void

Почему: функция с меньшим числом параметров всегда допустима, поэтому необходимости в более короткой перегрузке нет. Добавление в начало варианта с более короткой перегрузкой приводит к тому, что неподходящие по типу функции будут допускаться, поскольку подходят к первой перегрузке.

Перегрузки функций

Упорядочивание

Не помещайте более общие перегрузки перед более специфичными:

/* НЕПРАВИЛЬНО */
declare function fn(x: any): any
declare function fn(x: HTMLElement): number
declare function fn(x: HTMLDivElement): string

var myElem: HTMLDivElement
var x = fn(myElem) // x: any, wat?

Сортируйте перегрузки так, чтобы более общие находились после более специфичных:

/* ПРАВИЛЬНО */
declare function fn(x: HTMLDivElement): string
declare function fn(x: HTMLElement): number
declare function fn(x: any): any

var myElem: HTMLDivElement
var x = fn(myElem) // x: string, :)

Почему: при разрешении вызовов функции TypeScript выбирает первую подходящую перегрузку. Если первая перегрузка является более общей, чем последующие, то последующие перегрузки оказываются скрыты и не могут быть вызваны.

Используйте необязательные параметры

Не создавайте несколько перегрузок, отличающихся только конечными аргументами:

/* НЕПРАВИЛЬНО */
interface Moment {
  diff(b: MomentComparable): number
  diff(b: MomentComparable, unitOfTime: string): number
  diff(b: MomentComparable, unitOfTime: string, round: boolean): number
}

Используйте необязательные параметры, если это возможно:

/* ПРАВИЛЬНО */
interface Moment {
  diff(b: MomentComparable, unitOfTime?: string, round?: boolean): number
}

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

Почему: Это важно по двум причинам.

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

function fn(x: (a: string, b: number, c: number) => void) {}
var x: Moment
// Если записано с перегрузками, все нормально -- использована первая перегрузка
// Если записано с необязательными параметрами, обнаруживается ошибка
fn(x.diff)

Вторая причина -- применение к коду, использующему библиотеку, "строгой проверки на null". Поскольку пропущенные параметры для JavaScript выглядят как undefined, то, как правило, функции с необязательными параметрами можно явно передать undefined. Следующий код, например, должен компилироваться при строгой проверке на null:

var x: Moment
// Если записано с перегрузками, то несправедливая ошибка, так как передается 'undefined' вместо 'string'
// Если записано с необязательными параметрами, то все нормально
x.diff(something, someOtherThing ? undefined : 'hour')

Используйте объединения

Не создавайте перегрузок, которые отличаются только типом одного аргумента:

/* НЕПРАВИЛЬНО */
interface Moment {
  utcOffset(): number
  utcOffset(b: number): Moment
  utcOffset(b: string): Moment
}

Используйте объединения, если это возможно:

/* ПРАВИЛЬНО */
interface Moment {
  utcOffset(): number
  utcOffset(b: number | string): Moment
}

Обратите внимание, что параметр b не сделан необязательным, так как типы возвращаемых значений различаются.

Почему: Это важно для тех, кто передает значения "сквозь" функцию:

function fn(x: string): void
function fn(x: number): void
function fn(x: number | string) {
  // Если записано с отдельными перегрузками, то несправедливая ошибка
  // Если записано с объединениями, то все нормально
  return moment().utcOffset(x)
}

Ссылки