This is the seventh day of my participation in the August More text Challenge. For details, see:August is more challenging

Annotate the type of this

Every function in JavaScript has this variable, not limited to class methods; Different calls to the function will have different values for this, which can easily make the code fragile and difficult to understand

Let x = {a(){return this}} x.a() // this is an x objectCopy the code

The value of this depends on how the function is called, not how it is declared; If you use this in a function, declare the type of this in the first argument to the function. TypeScript ensures that this is exactly the type you expect each time the function is called. An unusual argument to this, but a reserved word, is part of the function signature.

Generator function

Generator functions are a convenient way to generate a set of values; The use of generators to generate exactly the generated values; The generator is lazy, calculating the next value only when requested by the user.

function* createFibonacciGenerator() {
    let a = 0;
    let b = 1;
    while (true) {
        yield a;
        [a, b] = [b, a+b]
    }
}
​
let fibonacciGenerator = createFibonacciGenerator()
console.log(fibonacciGenerator.next()) // { value: 0, done: false }
console.log(fibonacciGenerator.next()) // { value: 1, done: false }
console.log(fibonacciGenerator.next()) // { value: 1, done: false }
console.log(fibonacciGenerator.next()) // { value: 2, done: false }
console.log(fibonacciGenerator.next()) // { value: 3, done: false }
console.log(fibonacciGenerator.next()) // { value: 5, done: false }
console.log(fibonacciGenerator.next()) // { value: 8, done: false }
Copy the code
  • The asterisk (*) before the function name indicates that this is a generator function. The call generator returns an iterable

  • This generator can always generate values

  • The generator uses the yield keyword to produce values. The user asks the generator to supply the next value. Yield sends the result to the user and then stops execution until the user requests another value. The while(true) loop here doesn’t run forever, it doesn’t crash

  • To calculate the next Fibonacci sequence, assign B to A and a + b to B

    Call createFibonacciGenerator and you get an IterableIterator. Each time next() is called, the iterator evaluates the next Fibonacci number and yields it

The iterator

An iterator is the opposite of a generator: a generator is a way of generating a set of values, and an iterator is a way of using those values

Iterable: An object that has a Symbol. Iterrator property whose value is a function that returns an iterator

Iterator: Defines an object with the next method, which returns an object with the value and done properties

Custom iterators or iterables

let numbers = {
    *[Symbol.iterator]() {
        for (let i = 0; i < 10; i++) {
            yield i
        }
    }
}
Copy the code

Numbers is an iterator. Calling the generator function numbers[symbol.iterator]() returns an iterable iterator; In addition to defining our own iterators, we can use JavaScript’s built-in common collection types (Array, Map, Set, String)

Call sign

Most of the time the Function type is not the end result we want; Object describes all objects. Similarly, Functoin can represent all functions, but does not represent the specific type of the function

function sum(a: number, b: number): number {
  return a + b
}
Copy the code

(a: number, b: number) => Number is a TypeScript syntax for function types, also known as call signatures. If you pass a function as an argument to another function, or as a return value of a function, you use this syntax annotation

The two parameter names a and b are only an ideogram and do not affect the assignment of type functions

The call signature of a function contains only the code at the type level, that is, only the type and no value. So the call signature of a function can indicate the type of the argument, the type of this, the type of the return value, the type of the remaining arguments, and the type of the optional arguments, but the default value cannot be derived (because the default value is a value, not a type). The call signature has no function body and cannot deduce the return type, so an annotation must be displayed

type User = (nickname: string, email: string) => void; let user: 'nickname' => {console.log(' ${nickname} ': ${nickname} ')} User ('Forest', '[email protected]') [email protected]Copy the code

First, we declare a function expression user of type user. Since the types of member variables are already annotated when you define User, the parameters in User do not need to be annotated again. TypeScript can derive this from User

Context type derivation

The function expression user declares the type as user, so TypeScript can derive the types nickname and email from the context. This behavior is called context type derivation, and is a feature of TypeScript type derivation

function times(f: (index: number) => void, n: number) {
    for (let i = 0; i < n; i++) {
        f(i)
    }
}
​
times(n => console.log(n), 6)
Copy the code

The above code will print 0, 1, 2, 3, 4, 5; TypeScript can deduce that n is a number from the context because in The Times signature, we declare that the parameter index to f is a number

Function overloading

Function overload is a function that has multiple call signatures

In most programming languages, once a function is declared with specific arguments and return types, the function can only be called with those arguments, and the return value is of the same type. But that’s not the case with JavaScript; Because JavaScript is a dynamic language, there are more ways to call a function than methods; Not only that, but the type of the output depends on the type of the input parameters

TypeScript also supports dynamic function declarations, and the output type of a function depends on the input type, thanks to TypeScript’s static type system

In general, when declaring an overloaded function type, each overloaded signature must be assignable to the implementation signature. However, when declaring an implementation signature, the kennel is generally broader, ensuring that all overloaded signature interfaces can be assigned to the implementation signature

let reserve: Reserve = ( from: any, toOrDestinatoin: any, destination? : any ) => { //.... }Copy the code

The above code is feasible; When overloading, you should make the implementation of the signature as concrete as possible, so that the function is easy to implement

The browser DOM API is heavily overloaded; For example, createElement creates an HTML element. Its parameter is a string representing an HTML tag and the return value is an HTML element of the corresponding type. TypeScript has built-in types for each HTML element. Such as:

  • said<a>Elements of theHTMLAnchorElement
  • said<canvas>Elements of theHTMLCanvasElement
  • said<table>Elements of theHTMLTableElement
  • .

CreateElement is implemented by overloading as follows:

Type CreateElement = {(tag: 'a'): HTMLAnchorElement 'table'): HTMLTableElement (tag: string): Function createElement(tag: 'a');} function createElement(tag: 'a'); HTMLAnchorElement function createElement(tag: 'canvas'): HTMLCanvasElement function createElement(tag: 'table'): HTMLTableElement function createElement(tag: string): HTMLElement { // ... }Copy the code

TypeScript parses overloads in the order they are declared. Full type signatures are not limited to overloading how a function is called, but can also describe the properties of the function