Эта статья - вторая часть из трех частей, в которых обсуждаются различные типы данных, доступные в программах TypeScript. Первая статья находится здесь. В этой статье основное внимание уделяется более традиционным типам данных, таким как число, строка, логическое значение, и типам, которые возникают с объектами, например, простым объектам, массивам и кортежам, более новым типам данных в JavaScript и TypeScript.

Логический тип

Тип boolean предназначен для хранения истинных и ложных значений. TypeScript может выводить boolean типы из присваивания, и вы можете специально объявить переменную как boolean типа. Вы также можете объявить переменную определенного boolean type (либо true, либо false), и этой переменной нельзя присвоить другое значение boolean.

Вот некоторые примеры:

let flag = true // boolean type inferred
let negative = false // boolean type inferred
let flag :boolean = true // boolean type annotated
const t = true // an inferred type literal
let alwaysTrue :true = true // an annotated type literal
let alwaysFalse :false = false // an annotated type literal

Вы знакомы со многими из этих присвоений, но, вероятно, не со всеми, которые относятся к литералам типов. Литерал типа использует определенное значение в качестве типа данных, и вы можете сделать это в TypScript с помощью true и false. После объявления переменной с литералом типа эта переменная не может содержать никакого другого значения. Вот пример:

let t :true = true
t = false

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

test.ts:2:1 - error TS2322: Type 'false' is not assignable to type 'true'.
2 t = false
  ~
Found 1 error.

Переменная t теперь является литералом истинного типа и не может содержать другого значения.

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

Тип числа

Тип number используется для хранения чисел. В отличие от других строго типизированных языков, TypeScript предоставляет только тип number для числовых данных. Вы не можете указывать целые числа, числа с плавающей запятой, длинные числа и т. Д.

Тип number позволяет выполнять обычные числовые операции с ним, такие как сложение, вычитание, умножение, деление и модуль, а в библиотеке Math есть множество математических функций, которые также работают с числовыми данными.

Как и в случае с другими типами данных, тип number можно вывести или указать с помощью аннотации типа, как показано в примерах ниже:

let salary = 20000 // number type is inferred
const PI = 3.14159 // number type is inferred
let hours :number = 40 // number type is annotated
const e :number = 2.71828 // number type is annotated

Вы также можете использовать литералы типов с числовыми данными:

let pi :3.14159 = 3.14159
let standardHours :40 = 40

Значения pi и standardHours теперь являются литералами типа и не могут быть изменены. Это очень похоже на создание констант, и вам, вероятно, следует воздержаться от использования литералов типов таким образом и вместо этого использовать константы.

Тип bigint

Ограничение размера для числового типа данных - 253. Если вам нужно сохранить число, превышающее это, вы должны использовать тип BigInt, который в TypeScript представлен как bigint.

Есть несколько способов инициализировать переменные с типом bigint. Вот некоторые примеры:

let n = 4567n 
// an integer with n after it is a BigInt and the type is inferred
const x = 230000n // bigint type is inferred
let z :bigint = 100000n
let s :100n = 100n // a bigint type literal

Тип bigint можно использовать со стандартными арифметическими операторами (+, -, *, /), за исключением модуля, а BigInts можно использовать с операторами отношения. Например:

let x :bigint = 100n
let y :bigint = 200n
let c :bigint = x * y

Тип строки

Тип string используется для текстовых данных, которые могут включать числа. Строки формируются путем помещения одинарных или двойных кавычек вокруг текста, который вы хотите ввести как строку. Единственный оператор, который работает со строками, - это оператор конкатенации (+), и есть много функций, которые вы также можете вызывать для работы со строками, например substr, slice, length и так далее.

Вот несколько примеров того, как создавать новые строковые переменные:

let name = "Billy"
let greeting = 'Hello'
let id = '28348'
let last :string = "Pyle"
let firstFiveLetters = "a"+"b"+"c"+"d"+'e'
let fn :mike = "Mike" // a type literal

Как всегда, вы должны позволить TypeScript определять тип строки из назначенных данных, а не аннотировать тип в вашей программе.

Тип символа

Тип symbol - относительно новый тип данных в JavaScript. Основная цель этого типа - определить свойства объекта и карты как уникальные сущности, чтобы вместо них по ошибке использовалось какое-то другое строковое значение, что заставляет JavaScript создавать новое свойство с этим именем.

Переменная типа symbol создается с помощью функции Symbol. Вы не вызываете ключевое слово new с этой функцией, только имя функции со значением. Вот несколько примеров объявления переменных с типом symbol:

let first_name = Symbol("first_name")
let last_name :symbol = Symbol("last_name")

Вы можете либо позволить TypeScript вывести тип symbol, либо аннотировать его своим объявлением.

Вы также можете объявить переменные уникального типа symbol, как в этом примере:

const sym :unique symbol = Symbol("unique")

Уникальный symbol полностью уникален и не может быть равен любому другому symbol, кроме самого себя.

Тип объекта

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

let student = {
  name : "John Doe",
  major : "Art"
}
let dog = {
  name : "Fido",
  speak : "Woof!"
}
console.log(typeof(student)) // displays object
console.log(typeof(dog)) // displays object

Но тип обоих объектов - это просто объект, а не конкретный «вид» каждого объекта. Такой тип типизации называется структурной типизацией.

Вы можете использовать аннотацию типа object, но это приводит к странному поведению:

let person :object = {
  name: 'Terri'
}
console.log(person.name) // error message: Property name does
                         // not exist on type object

Однако, если вы позволите TypeScript определить тип данных:

let person = {
  name: 'Terri'
}
console.log(person.name) // displays Terri

TypeScript распознает человека как объект, а имя как свойство объекта.

Тип массива

На самом деле массивы не являются типами данных, это особый тип object. В JavaScript массивы могут содержать значения любого типа, что придает массивам ощущение Дикого Запада, потому что в большинстве языков программирования массивы однородны, то есть в них можно хранить данные только одного типа. TypeScript, однако, дает нам несколько вариантов обеспечения однородности.

Во-первых, давайте посмотрим, что произойдет, если мы создадим массивы в TypeScript, как в JavaScript. Вот пример кода:

let values = []
values.push(1)
values.push("hello")
values.push(true)
console.log(values) // displays [1, "hello", true]

Мы можем поместить любое значение данных в массив.

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

let array-name: data-type [] = [укажите здесь необязательные начальные значения или оставьте пустым]

Вот пример работы аннотации этого типа:

let numbers :number[] = []
numbers.push(100)
numbers.push(200)
numbers.push("hello")// error: Argument of type 'string' is not
                     // assignable to parameter of type 'number'

Вы также можете позволить TypeScript определить тип данных массива, инициализировав массив хотя бы одним значением, как в этом примере:

let numbers = ["one"]
numbers.push("two")
numbers.push(3) // error: Argument of type 'number' is not
                //assignable to parameter of type 'string'

Но что, если вам нужен (не дай бог) массив смешанных типов данных? Вы можете аннотировать массив с помощью типа any:

let values :any[] = []
values.push(1)
values.push("two")
value.push(false)

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

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

let values = [1, "two"]

Теперь, если я попытаюсь написать следующее:

values.push(false)

Я получаю это сообщение об ошибке:

Argument of type 'boolean' is not assignable to parameter of type 'string | number'.

По сути, я создал массив типа number или string. Значения любого из этих типов могут быть помещены в массив, но другие типы данных не допускаются.

Кортежи

Кортеж - это своего рода массив, в котором длина элементов и типы данных элементов фиксированы. Вот пример объявления кортежа:

let dept :[string, string, string, string] =
          ['Raymond', 'Mike', 'Mayo', 'Danny']

Размер фиксирован на четырех элементах, а тип кортежа - [string, string, string, string].

Если я попытаюсь добавить один элемент в кортеж:

dept = ['Raymond', 'Mike', 'Mayo', 'Danny', 'Cynthia']

Я получаю сообщение об ошибке, указывающее, что тип [string, string, string, string, string] не может быть назначен типу [string, string, string, string].

Кортежи обеспечивают большую безопасность типов, которая недоступна для массивов из-за гибкости массива JavaScript. Во многих приложениях предпочтительнее использовать кортежи, а не массивы.

Вы можете предоставить необязательные элементы в кортеж, чтобы элементы кортежа могли содержать до необязательного числа или меньше, но не более. Например:

let grades  :[number, number, number, number, number?] =
  [81, 77, 85, 98, 100]
console.log(grades) // displays 81, 77, 85, 98, 100
grades = [75, 82, 68, 88]
console.log(grades) // displays 75, 82, 68, 88

Но если я попытаюсь присвоить кортежу шестую оценку:

grades = [71, 73, 83, 88, 91, 99]

Я получаю сообщение об ошибке, указывающее, что тип [number, number, number, number, number, number] не может быть назначен типу [number, number, number, number, [number | undefined].

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

let numbers :[number, ...number[]] = [1,2,3]
numbers = [1,2]
numbers = [1]
numbers = [1,2,3,4,5,6,7,8,9,10]

Как показывает этот пример, при вводе кортежа с параметром rest кортеж может содержать 3 элемента, 2 элемента, 1 элемент или даже 10 элементов. Использование параметра rest обеспечивает максимальную гибкость для выражения количества элементов кортежа.

Доступные только для чтения массивы и кортежи

TypeScript позволяет создавать неизменяемый массив или кортеж с помощью ключевого слова readonly. Когда массив или кортеж объявлены доступными только для чтения, вы не можете добавлять или удалять данные из структуры (конечно, кортеж в любом случае имеет фиксированный размер), и если вы хотите сделать это, вы должны использовать присваивание для внесения изменений или используйте неизменяющую функцию.

Вот некоторые примеры:

let numbers :readonly number[] = [1,2,3]
//numbers.push(4) // not allowed because numbers is readonly
numbers = [1,2,3,4] // can assign new data on readonly array
numbers = numbers.concat(5) // can use concat with assignment
console.log(numbers) // displays [1, 2, 3, 4, 5]
// numbers.pop() // not allowed because numbers is readonly
numbers = numbers.slice(0,4)
console.log(numbers) // displays [1, 2, 3, 4]

На этом завершается часть 2 моего введения в типы TypeScript. В части 3 я расскажу об остальных типах TypeScript, чтобы мы могли перейти к использованию этих типов для решения интересных вычислительных задач.

Благодарим за прочтение и, пожалуйста, ответьте на эту статью или напишите мне по электронной почте с вашими комментариями и предложениями.