laitimes

TypeScript enumerations and type constraints

author:Lu Rongtao
TypeScript enumerations and type constraints

● In the previous chapter, we talked about the interface of TS

● In this chapter, we will talk about TS enumerations and constraints

enumerate

Recognize enumerations

● The concept of enums is found in many computer languages, but there is no such concept in JS, and to compensate for this shortcoming, enumeration types are added to TS

● What is an enumeration?

Enumeration ( mei ju ): Enumeration means to list one by one, list all cases, then when taking the value, only these few can be used, and the others are not available

Enumerations in computer languages: Put all constants in a set so that several constants become a set of related content

TypeScript enumerations and type constraints
// 针对一个业务逻辑, 我需要频繁用到四个方向的字符串
const UP = 'up'
const RIGHT = 'right'
const DOWN = 'down'
const LEFT = 'left'           

● For the above four variables

● I don't care about any logic, I can't limit you to using only one of these four variables

// 封装一个功能函数
function util(dir) {}           

● No matter what method you use, you can't limit the dir parameter to the four directions listed above

● At this point, we can use the enumeration

● First, in TS, create an enumeration set with the enum keyword and put the four constants we need into it

enum Direction {
  UP = 'up',
  RIGHT = 'right',
  DOWN = 'down',
  LEFT = 'left'
}           

Make a DIrection enumeration collection, which can be used to restrict certain data

function util(dir: Direction) {}           

● It is agreed that the value of the dir parameter can only be a constant in the enumeration collection of Direction, and nothing else

TypeScript enumerations and type constraints

● As long as you don't write the contents of the Direction enumeration, you can't do it

Numeric enumeration

● Numeric enumeration : Every constant in an enumeration type is a number

● In TS, every constant in an enumeration, when you do not set a value, defaults to the number type

enum Pages {
    ONE,    // 0
    TWO,    // 1
    THREE   // 2
}           

● Your constants in the enumeration, the first default value is 0, followed by +1 increments

At this time

Pages.ONE => 0

Pages.TWO => 1

Pages.THREE => 2

● We can also specify the value ourselves

enum Pages {
    ONE = 10,    // 10
    TWO = 20,    // 20
    THREE = 30   // 30
}           

● At this time, the constants in the enumeration set are the values we specify

● We can also specify partial values

enum Pages {
    ONE = 10,    // 10
    TWO,         // 11
    THREE        // 12
}           

● The unspecified constant after the specified constant will be incremented once according to the rule of +1

enum Pages {
    ONE,         // 0
    TWO = 10,    // 10
    THREE        // 11
}           
enum Pages {
    ONE,         // 0
    TWO = 10,    // 10
    THREE,       // 11
    FOUR = 30,   // 30
    FIVE         // 31
}           

String enumeration

● String enumeration: The value of each constant in the enumeration collection is of type string

● In TS, you must specify a value for the string type to appear

enum Direction {
  UP = 'up',
  RIGHT = 'right',
  DOWN = 'down',
  LEFT = 'left'
}           

● In TS, enumeration constants are not the same as anything, including raw strings

function util(dir: Direction) {}           
TypeScript enumerations and type constraints

● This is because, in TS, each constant within an enumeration is a unique value

● So when you use an enumeration to qualify a piece of data, you can only use the values in the enumeration

● This also avoids word mistakes that you make due to hand mistakes, such as whether you think 'form' and 'from' are the same word

Heterogeneous enumeration

● Heterogeneous enumeration: In fact, it is a mixture of numeric enumeration and string enumeration in an enumeration collection

But you probably won't use it that way, because we, as a collection of data, don't usually mix numbers and strings

enum Info {
  ONE,
  UP = 'up',
  TWO = 2,
  LEFT = 'left'
}           

● There is a point to note here

Because in the enumeration collection, when you do not set a value for a certain key, it will default to the previous value +1

So if the previous one is a string enumeration, then the next one must be assigned manually, otherwise an error will be reported

If the previous one is a numeric enumeration, then the next one can be assigned manually without the need and will be calculated according to the previous +1

Enumeration merges

● Enumerations within TS, which support merging

● Multiple enumeration types can be written separately and will be automatically merged at compile time

enum Direction {
  UP = 'up',
  RIGHT = 'right',
  DOWN = 'down',
  LEFT = 'left'
}

enum Direction {
  TOP = 'top',
  BOTTOM = 'bottom'
}

function util(dir: Direction) {}

util(Direction.BOTTOM)
util(Direction.LEFT)           

● The two enums defined here are called Direction, which will be automatically put together at compile time without conflict

Reverse mapping

● The number enumeration in TS will compile the key and value upside down at the same time when compiling

enum Pages {
    ONE,    // 0
    TWO,    // 1
    THREE   // 2
}           

● Using this as an example, how does he compile it

var Pages;
(function (Pages) {
    Pages[Enum["ONE"] = 0] = "ONE"
    Pages[Enum["TWO"] = 1] = "TWO"
    Pages[Enum["THREE"] = 2] = "THREE"
})(Pages || (Pages = {}));           

● The result of the compilation

Pages = {
    ONE: 0,
    TWO: 1,
    THREE: 2,
    '0': 'ONE',
    '1': 'TWO',
    '2': 'THREE'
}           

● That is, when we use it inside TS, if it is a numeric enumeration

● Then we can get the corresponding number through the key, or we can get the corresponding key through the corresponding number

enum Pages {
    ONE,    // 0
    TWO,    // 1
    THREE   // 2
}

console.log(Pages.ONE)    // 0
console.log(Pages.TWO)    // 1
console.log(Pages.THREE)  // 2
console.log(Pages[0])     // 'ONE'
console.log(Pages[1])     // 'TWO'
console.log(Pages[2])     // 'THREE'           

Constant enumeration

● Constant enumeration, which is modified by adding the const keyword to the enumeration

● When compiling, the enumeration content will be deleted, and only the compilation result will be retained

● And for numeric enumerations, the reverse mapping capability is not supported, and can only be accessed using keys

● Non-constant enumeration

enum Pages {
    ONE,    // 0
    TWO,    // 1
    THREE   // 2
}

console.log(Pages.ONE)
console.log(Pages.TWO)
console.log(Pages.THREE)           

○ Compiled js file

TypeScript enumerations and type constraints

● Constant enumeration

const enum Pages {
    ONE,    // 0
    TWO,    // 1
    THREE   // 2
}

console.log(Pages.ONE)
console.log(Pages.TWO)
console.log(Pages.THREE)           

○ Compiled js file

TypeScript enumerations and type constraints

Type constraints

● In TS, there is also a very magical keyword called type

● Type is also called type alias has many magical features, not only supports interface-defined object structures, but also supports any handwriting type

● Let's start with a very simple example

TypeScript enumerations and type constraints
let n1: number | string | boolean
let n2: number | string | boolean
let n3: number | string | boolean           

● Looking at the above code, we define three variables: n1 and n2 and n3

○ The restrictions on types are number or string or boolean

● It is very troublesome to write

● At this time, we can use type to alias it

type Info = number | string | boolean
let n1: Info
let n2: Info
let n3: Info           

● In this way, our code has become concise

● Maybe friends think that this is not used much, but type is not the only function

Common use of type

● Basic type of alias

type n = number
let num: n = 100           

○ This is a very basic use, giving the type number an alias called n

○ In the future, when using n to restrict variables, you are actually using number

● Basic type union

type i = number | string
let str: i = '千锋大前端'
str = 100           

○ This is the union type, and the number or string type has an alias called i

○ When we use i to restrict a variable, the variable is restricted to number or string

● Object type

type User = { name: string, age: number }
let person: User = { name: '千锋大前端', age: 10 }           

○ This is the object type, much like interface, and the usefulness is basically the same

● Object federation type

type User = { name: string, age: number }
type Person = User & { gender: boolean }
let person: Person = { name: '千锋大前端', age: 10, gender: true }           

○ This is the object union type, much like the extends inheritance of interface

Tuple type

type data = [ number, string ]
let info: data = [ 10, '千锋大前端' ]           

● Limited volume

type color = 'yellow' | 'orange' | 'blue'
function util(c: color) {}
util('yellow')           

○ This color is limited to a few values, and color is used to constrain a variable in the future

○ This variable can only accept these values, which is more similar to enum

What type and interface have in common

1. Both object or function types can be constrained

○ interface

interface User { name: string; age: number }
interface Func { (x: number): number }           

○ type

type User = { name: string; age: number }
type Func = (x: number) => number           

○ We can see that the two definitions are slightly different, but the later usage is basically the same

2. Extension type

The interface uses extends for inheritance

interface Person {
    name: string
    age: number
}

// 使用 extends 关键字继承自 Person
interface Student extends Person {
    classRoom: number
}
let s: Student = { name: '千锋大前端', age: 10, classRoom: 1 }           

○ type is implemented using crossovers

type Person = {
    name: string
    age: number
}

// 使用 交叉(&)
type Student = Person & {
    classRoom: number
}
let s: Student = { name: '千锋大前端', age: 10, classRoom: 1 }           

3. Federation type

The interface uses extends to inherit the type

type Person = {
    name: string
    age: number
}

// 使用 extends 关键字继承自 Person
interface Student extends Person {
    classRoom: number
}
let s: Student = { name: '千锋大前端', age: 10, classRoom: 1 }           

○ type uses cross(&) to extend the interface

interface Person {
    name: string
    age: number
}

// 使用 交叉(&)
type Student = Person & {
    classRoom: number
}
let s: Student = { name: '千锋大前端', age: 10, classRoom: 1 }           

The difference between type and interface

1. Interface supports automatic merging of multiple declarations, type does not support

interface User {
    name: string
    age: number
}
interface User {
    classRoom: string
}
/*
    真实的 User 接口
    {
        name: string
        age: number
        classRoom: string
    }
*/           

○ type If you declare a duplicate identifier, an error will be reported

TypeScript enumerations and type constraints

2. Default export syntax for ES6 modular syntax

○ Interface supports default export at the same time as declaration

export default interface User {
    name: string
    age: number
}           

○ type must be declared first, in default export

type User = {
    name: string
    age: number
}
export default User           

○ You must declare it first, when the default export is performed, if you directly write the default export, an error will be reported

TypeScript enumerations and type constraints

3. type You can use the typeof keyword to get a certain data type

let box = document.querySelector('.box')
type EleType = typeof box           

○ Here defines an EleType identifier that automatically restricts the type of box detected according to the typeof keyword

4. type supports using the in keyword to traverse into mapped types

type names = 'firstName' | 'lastName' | 'AKA'
type nameType = {
    [key in names]: string
}
/*
    真实的 nameType 类型
    {
        firstName: string
        lastName: string
        AKA: string
    }
*/