Simple types: the scalars types
Chapter 2: Getting started with TypeScript
Chapters - Table of contents

Typing a variable

Typing variables using TypeScript is pretty straightforward. We can specify a type by adding a : prefix to any variable declaration, as follows:
1const name: string = 'John'
2
ℹ️ We will talk about typing objects, properties, and functions in the next part of this Chapter.

Scalar types

💡The term “scalar” comes from linear algebra, where it is used to differentiate a single number from a vector or matrix. The meaning in computing is similar. It distinguishes a single value like an integer or string from a data structure like an array.
Scalar types represent the basic unit of data that a variable or object property can handle. Let's review together the few scalar types to learn:
TypeScript scalar types are always written in lowercase
Don't mix String and string 👀 String represents a primitive class type, while string is a scalar type.

string

1const name: string = 'John'
2
3const empty: string = ''
4

Literal string types

TypeScript allows you to specify a range of string values as a type, as it follows:
1const all: 'all' = 'all' // valid
2
3const all: 'all' = 'John' // INVALID
4
ℹ️ Beware, a literal string type is not equivalent or a subset of a string type, a string literal helps to define an expected string value.

boolean

1const isTrue: boolean = true
2const isFalse: boolean = false
3

number

1const age: number = 29
2const price: number = 20.0
3
ℹ️ number contains both integers and floats.

bigint

When dealing, for example, with cryptography, working with more complex number data types such as bigintis frequent.
1// Creating a bigint via the BigInt function
2const oneHundred: bigint = BigInt(100);
3
4// BigInt64Array type is built on top of the bigint type
5const int = new BigInt64Array(1)
6const a = int[0] // a is of type `bigint`
7

null and undefined

While the existence of both null and undefined is often subject of controversy, please note that in JavaScript:
  • undefined represents the lack of a value (variable has no value)
  • null represents an explicit empty value (variable is empty)
TypeScript also supports the null and undefined types as it follows:
1const myEmptyVariable: null = null
2
3const myUndefinedVariable: undefined = undefined
4

Scalar types and arrays

Any type (not only scalar) can be "transformed" to an array by adding a [] prefix to the type declaration, as follows:
1const arrayOfString: string[] = ['John', 'Bob']
2

The special cases of tuples

Arrays with a fixed length are commonly called tuples.
For example, React's useState()returns a tuple: const [value, setValue] = useState(true)
Here's how to use tuples:
1const position: [number, number] = [1, 2];
2
3// tuple values can be labelled as it follows:
4const position: [x: number, y: number] = [1, 2];
5
Labeling tuples make the type more explicit and easier to use:
It is more explicit that values matches position axis: x and y
It is more explicit that values matches position axis: x and y

Assigning multiple types to a variable

Most real-world use-cases need to assign multiple types of values to a variable. TypeScript leverage the | operator to complete union types, as follows:
1const price: string | number = '1.0'
2const price: string | number = 1.0
3const price: string | number = 1
4
5// Arrays can have multiple item types:
6const prices: (string | number)[] = [1, 2.0, '30']
7
Union types allow defining enums value by leveraging string literals:
1const position: 'top' | 'right' | 'bottom' | 'left' = 'left'
2

More on TypeScript enums

TypeScript does provide an enum type as follows:
1// enums are numerics by default
2enum Position {
3  Top, // 0
4  Right, // 1
5  Bottom, // ...
6  Left,
7}
8
9// you can provide initializers
10enum Position {
11  Top = 1,
12  Right, // 2
13  Bottom, // 3
14  Left, // 4
15}
16
17// a string enum
18enum Position {
19  Top = 'top',
20  Right = 'right',
21  Bottom = 'bottom',
22  Left = 'left' ,
23}
24
25// more advanced use-case
26enum FileAccess {
27  // constant members
28  None,
29  Read = 1 << 1,
30  Write = 1 << 2,
31  ReadWrite = Read | Write,
32  // computed member
33  G = "123".length,
34}
35
More than types, enum are generated javascript objects** **at runtime.
For constant enums values only, it is possible to use the const operator to avoid any extra code generation from TypeScript, as follows:
1const enum Position {
2  Top = 'top',
3  Right = 'right',
4  Bottom = 'bottom',
5  Left = 'left' ,
6}
7
When using const enum, every usage of it will be replaced at compile time.
1const enum Position {
2  Top = 'top',
3  Right = 'right',
4  Bottom = 'bottom',
5  Left = 'left' ,
6}
7
8const position = Position.Top
9
10// will compile to the following JavaScript code:
11
12const position = 'top'
13

When to use enums or string literals

Regarding our Position enum, when should we use a string literal type or an enum type?
1interface TooltipProps {
2   position: 'top' | 'right' | 'bottom' | 'left'
3}
4
5// OR
6
7const enum Position {
8  Top = 'top',
9  Right = 'right',
10  Bottom = 'bottom',
11  Left = 'left' ,
12}
13
14interface TooltipProps {
15   position: Position
16}
17
The rule of thumb is the following:
  • use string literals union when the value (ex: 'left') can be known by the end-user developer
  • use string literals when it improves the Developer Experience
  • otherwise, use enum
In short, you might want to use enum for anything related to data and state, and string literal union for component props.
1<Tooltip position={'left'} /> // is nicer than:
2
3<Tooltip position={POSITION.LEFT} />
4

Details to keep in mind

A type can be null and undefined

With the default TypeScript configuration, all types also contain null and undefined:
1let name: string = "John" // valid
2
3name = null // valid
4
5name = undefined // valid
6
7name = 1 // INVALID (`name` is a `string`)
8
ℹ️ We will see in Chapter 4 that we can change the TypeScript configuration to be "stricter" and exclude null and undefined from types by default.

Should we annotate all variables?

As we saw in Chapter 1, TypeScript is very good at inferring types, so why do we need to annotate all variables?
The theoretical answer is: "You don't have to."
1// since name is a const variable, 
2//   name is of type "Charly" (literal type)
3const name = "Charly"
4
5// otherName type is not immutable, so,
6//   otherName type is `string`
7let otherName = "Charly"
8
However, when it comes to variables, I would highly recommend only using inference for constant values and explicitly specifying the expected type for variables with "dynamic value" (a value that comes from a function or another variable).
1// capitalize() might return a type other than `string`,
2//  by making name's type explicit, we avoid its type to change
3//  with capitalize() declaration 
4let name: string = capitalize(otherName)
5

Moving forward

In the next part, we will see how to type more advanced data types: objects.
We use cookies to collect statistics through Google Analytics.
Do not track
 
Allow cookies