preface

There’s no denying that TypeScript has become an essential skill for a front-end engineer. Rigorous type detection, on the one hand, improves the maintainability and robustness of the program, on the other hand, it also subtly improves our programming thinking, that is, logic.

So, today I’ll take a quick look at TypeScript’s advanced types by combining a real-world development scenario with some of the type definitions in the Vue 3.0 source code.

interface

Interface is used for type detection on all structured data. For example, in real development, we would define objects or arrays to describe view structures.

Define the object

Common definitions of columns in a table are:

const columns = [
  {key: "username".title: "Username"},
  {key: "age".title: "Age"},
  {key: "gender".title: "Gender"}]Copy the code

In this case, we can define an interface named column:

interface column {
  key: string.title: string
}

/ / use
const columns: column[] = [
  {key: "username".title: "Username"},
  {key: "age".title: "Age"},
  {key: "gender".title: "Gender"}]Copy the code

Define a function

When we use axios in normal development, it can be called in many ways, such as axios.request(), axios.get(), and axios.put(). It essentially defines an interface to constrain Axios to have these methods, and it will look something like this:

export interface Axios {
  request(config: AxiosRequestConfig): AxiosPromise
  get(url: string, config? : AxiosRequestConfig): AxiosPromisedelete(url: string, config? : AxiosRequestConfig): AxiosPromise head(url:string, config? : AxiosRequestConfig): AxiosPromise options(url:string, config? : AxiosRequestConfig): AxiosPromise post(url:string, data? :any, config? : AxiosRequestConfig): AxiosPromise put(url:string, data? :any, config? : AxiosRequestConfig): AxiosPromise patch(url:string, data? :any, config? : AxiosRequestConfig): AxiosPromise }Copy the code

inheritance

Reusability is something we often think about when we write programs, such as component packaging, tool function extraction, function design patterns and so on. The same is true for interfaces, which can be inherited to duplicate some defined interface or class.

Here we take Vue 3.0 as an example. In the compiler stage, it divides AST Element Node into various nodes, such as ForNode, IFNode, IfBranchNode and so on. These special nodes inherit from Node:

Node inteface interface definition:

export interface Node {
  type: NodeTypes
  loc: SourceLocation
}
Copy the code

IFNode interface definition:

export interface IfNode extends Node {
  type: NodeTypes.IF
  branches: IfBranchNode[] codegenNode? : IfConditionalExpression }Copy the code

As you can see, the interface definition of Node is very clean. It describes the basic attributes needed for Node: the type of the Node, and the starting position of the LOC Node in the template. IFNode extends branches and codegenNode properties on the basis of Node, and overwrites Node’s type property to NodeTypes.IF.

Here are two properties of IFNode:

  • branchesThat’s what it corresponds toelseelse ifIt can be one or more nodes, so it’s an array.
  • codegenNodeVue 3.0AST ElementIt describes some properties of the node, for exampleisBlock,patchFlag,dynamicPropsWait a minute, these will add upruntimeTargeted update is closely related to targeted update.

Recently, I have also been writing an article about how Vue 3.0 implements runtime + compile to gracefully implement targeted updates and static upgrades. It should be completed by the end of next week.

summary

For the introduction and use of interface, we will stop here. Of course, it also has many advanced uses such as combining generics, defining function types, and so on. Interested students can learn about this aspect of the actual combat.

Cross type and union type

Cross type

Cross type name thought meaning, have cross effect. We can merge multiple types by crossing them. For example, in Vue3, the compile phase performs a transform in addition to baseParse, so that the final AST generates executable code. Therefore, it will correspond to multiple options in the compile phase, that is, it is also a cross type:

export type CompilerOptions = ParserOptions & TransformOptions & CodegenOptions
Copy the code

The CompilerOptions alias will be used for the baseCompiler stage:

export function baseCompile(
  template: string | RootNode,
  options: CompilerOptions = {}
) :CodegenResult {}
Copy the code

The crossover of multiple options is because baseCompiler is only the most basic compiler, and there are more advanced compiler-DOM, compiler-SSR, and so on.

The joint type

In the same way, union types have a combined effect. That is, sometimes when you want a variable to be of type String or number, you can constrain that variable with a union type. Such as:

let numberOrString: number | string
numberOrString = 1
numberOrString = '1'
Copy the code

Also, when using union types in real development, we often encounter prompts such as:

interface student {
  name: string
  age: number
}
interface teacher {
  name: string
  age: number
  class: string
}
let person: student | tearcher = {} as any

person.class = 161 "information"
// property 'person' does not exit on type `student`
Copy the code

At this point, we can use type assertions to tell TypeScript that we know what person is:

(person as teacher).class = 161 "information"
Copy the code

summary

For cross types and union types, this should be part of our normal development that we use a lot. And, from their concept, it is not difficult to understand that they are essentially the union and intersection of mathematics, but in the basic type has different performance.

Type protection and type differentiation

Type of protection

When we talked about the union type, we said that it is essentially an intersection, which also leads to the fact that we cannot directly use properties or methods outside of an intersection. Therefore, we need to use properties outside of the intersection by using type assertions in various cases to tell TypeScript what it is.

However, frequent use of type assertions undoubtedly reduces the readability of your code. TypeScript provides type protection to reduce type assertions. It is a subject-verb-object clause. For example, in the example above, we can implement type protection:

function isTeacher(person: Tearcher | Student) :person is Teacher {
   return(<Tearcher>person).class ! = =undefined
}
Copy the code

The isTeacher function is then used to determine the current type, and the corresponding attribute access is performed:

if (isTeacher(person)) {
  // Access teacher's unique attributes here
  person.class = 162 "information"
} else {
  person.name = "wjc"
}
Copy the code

The typeof and instanceof

With type protection, the question becomes: if I have multiple types in my union type, don’t I have to define multiple type-protected functions like isTeacher? Fortunately, using Typeof or Instanceof in TypeScript automatically implements type protection and differentiation.

typeof

Typeof is automatically type-protected in TypeScript for underlying types, such as:

function isNumberOrString(param: string | number) {
    if (typeof param === 'string') {
        return param.split(' ')}else {
        return param++
    }
}
Copy the code

instanceof

Unlike Typeof, which protects only base types, Instanceof protects all types. It implements type protection through constructors.

interface Person {
    name: string
    age: string
  }
  class Teacher implements Person {
    name: string
    age: string
    constructor(name: string, age: string) {
        this.name = name
        this.age = age
    }
    teach() {
      console.log("i am a teacher.")}}class Student implements Person {
    name: string
    age: string
    constructor(name: string, age: string) {
        this.name = name
        this.age = age
    }
    study() {
      console.log("i am a student.")}}const person: Student | Teacher = null as any

 if (person instanceof Teacher) {
     person.teach()
 } else {
     person.study()
 }
Copy the code

summary

For typeof or instanceof but in JavaScript are old knowledge, because of the type of traditional test. We will use the Object more String. The prototype. The toString (). In TypeScript, however, the two mix well.

Type the alias

Type aliases, I think the first thing that comes to mind is alias in Webpack for aliasing paths. However, type aliases in TypeScript are similar in appearance but not in meaning. In layman’s terms, it gives a name to a type:

type age = number
const getAge = () = > age

/ / is equivalent to
interface Age {
  getAge():number
}
Copy the code

Literal type

Literal types mean that we can implement enumerated types by using type aliases and union types, for example:

type method = get | GET | post | POST | PUT | put | DELETE | delete
Copy the code

Index type and mapping type

For index types and map types, here we use a common loadash function called pluck:

Implement in JavaScript:

function pluck(object, names) {
  return names.map(name= > object[name])
}
Copy the code

Implement in TypeScript:

function puck<T.K extends keyof T> (object: T, names: K[]) :T[K] []{
  return names.map(name= > object[name])
}
Copy the code

Here we define two generic variables T and K for puck, where K is the type inherited from all attribute names in T, so names in the parameter are constrained to be an array of attributes in T. This process is called type indexing. As for the return value T[K][] of puck function, it means that the returned value is an array, and the array value is constrained to the value of the attribute value K in T. This process is called index access.

It may be a little obscure to understand these two concepts, but it is logical to understand the process separately for each.

Write in the last

TypeScript, though, has become a must-have skill for front-end engineers. However, I believe that many partners still use Javascript more. So, there may be some confusion, how can I improve TypeScript programming? In fact, this problem is very simple, open source era, many of our problems can be solved by reading some open source project source code. I recommend reading the source code for Vue3.0 as a way to improve your TypeScript programming skills.

Writing is not easy, if you feel there is a harvest, you can handsome three combos!!

References:

  • TypeScirpt Field Guide
  • TypeScript official documentation
  • Vuejs/vue – next (github.com/vuejs/vue-n…

)

Review previous articles

  • From zero to one, take you to thoroughly understand the HMR principle in Vite (source code analysis)