As a front-end developer, YOU must know that TypeScript is often used as a type constraint in projects, which enables static checking in JavaScript, a weakly typed language, and promotes the evolution of front-end engineering. While studying TypeScript, My little friend (yu Hao) found some of TS’s fun function, only happy not happy, then share this article to everyone.

The original text may be a little difficult to understand, the author has tried to add some description, but to fully appreciate the TS “type gymnastics” secret, or have to practice.

What are we going to do

Our goal was to programmatically implement a Fibonacci algorithm using TypeScript’s type declarative syntax. In other words, similar to the existing machine code to instruction set, binary to decimal, assembly language to high-level programming language, let the type definition syntax also be programmed.

So the Fibonacci sequence code that we’re going to implement is going to look like this, right?

const fib = (n: number) :number= > n <= 1 ? n : fib(n - 1) + fib(n - 2);

for (let i = 0; i < 10; i++) {
  console.log(i, fib(i));
}
Copy the code

The running results are as follows:

Procedure is no problem, the end of the flower!

Just kidding, this is the only JavaScript that uses TypeScript Type definitions. We really want to do down down down, which is TS Type for FIbonacci

import { Fib, Add } from './fib-type';

type one = Fib<1>;
type zero = Fib<0>;
type Two = Add<one, one>;
type Five = Add<Two, Add<Two, one>>;
type Fib5 = Fib<Five>;
type Fib9 = Fib<9>;
type r0 = Fib<Zero>; // type r0= 0
type r1 = Fib<One>; // type r1 = 1
type r2 = Fib<Two>; // type r2 = 1
type r3 = Fib<3>; // type r3 = 2
type r4 = Fib<4>; // type r4 = 3
type r5 = Fib<5>; // type r5 = 5
type r6 = Fib<6>; // type r6 = 8
type r9 = Fib<9>; // type r9 = 34
type sum = Add<r9, r6>; // type sum = 42
Copy the code

What should we do

To implement the Fibonacci sequence, refer to the code from the beginning, which has basic comparison, addition, and loop syntax, so we also need to use the type system to implement these three functions in turn

2.1 Implementation of addition

In order to implement addition, you need to implement some tool types first

// The length of the yuan
type Length<T extends any[]> = T['length'];
type one = 1 

// Use extends to compare numbers for equality
type a111 = 0 extends one ? true : false // type a111 = false
type a112 = 1 extends one ? true : false // type a112 = true
Copy the code

The implementation of range is implemented recursively

/ / pseudo code
function range(n, list=[]){
  if(n<=0) return list.length
  return range(n-1[1. list]) }Copy the code

The limitation of TypeScript is that there are no loops. You can only use recursion instead of loop. There are several similar ways to write this later

// Create a tuple of the specified length with the return value as the second argument
type Range<T extends Number = 0, P extends any[] = []> = {
  0: Range<T, [any. P]>;1: P;
}[Length<P> extends T ? 1 : 0];

// Concatenate two tuples
type Concat<T extends any[], P extends any[]> = [...T, ...P];

type t1 = Range<3>;
// type t1 = [any, any, any]

type Zero = Length<Range<0> >;// type Zero = 0
type Ten = Length<Range<10> >;// type Ten = 10

type Five = Length<Range<5> >;// type Five = 5

type One = Length<Range<1> >;Copy the code

With the above tool syntax, it is easy to implement addition by simply asking for the length of the combination of the two elements

  type Add<T extends number, P extends number> = Length<
    Concat<Range<T>, Range<P>>
  >;
  type Two = Add<One, One>;
  // type Two = 2
  type Three = Add<One, Two>;
  // type Three = 3
Copy the code

Now that we have addition, how do we subtract? In general, subtraction and division are more difficult than addition, so we need more tool-type functions!

2.2 Tool Functions

2.2.1 Implement some basic tool types

  • Shift: Deletes the first element
  • Append: Inserts an element at the end of a tuple
  • IsEmpty / NotEmpty: Determines that the list is empty
// remove the first element of the tuple [1,2,3] -> [2,3]
type Shift<T extends any[] > = ((. t: T) = > any) extends (
  _: any. Shift: infer P ) =>any
  ? P
  : [];

type pp = Shift<[number.boolean.string.Object] >// type pp = [boolean, string, Object]

// Append to the tuple
type Append<T extends any[], E = any> = [...T, E];
type IsEmpty<T extends any[]> = Length<T> extends 0 ? true : false;
type NotEmpty<T extends any[]> = IsEmpty<T> extends true ? false : true;
type t4 = IsEmpty<Range<0> >;// type t4 = true

type t5 = IsEmpty<Range<1> >;// type t5 = false
Copy the code

2.2.2 Logical types

  • And:a && b
// Logical operation
type And<T extends boolean, P extends boolean> = T extends false
  ? false
  : P extends false
  ? false
  : true;
type t6 = And<true.true>;
// type t6 = true

type t7 = And<true.false>;
// type t7 = false

type t8 = And<false.false>;
// type t8 = false

type t9 = And<false.true>;
// type t9 = false
Copy the code

2.2.3 Less than or equal to

Pseudocode: The main idea is to pull an element from the list at the same time. The list that goes to 0 first is shorter

function dfs (a, b){
    if(a.length && b.length){
        a.pop()
        b.pop()
        return dfs(a,b)
    }else if(a.length){
        a >= b
    }else if (b.length){
        b > a
    }
}
Copy the code

Idea: Convert a comparison of numbers into a comparison of list lengths

// The value of the tuple is less than or equal to T <= P, and the length of the tuple is smaller than 0
type LessEqList<T extends any[], P extends any[] > = {0: LessEqList<Shift<T>, Shift<P>>;
  1: true;
  2: false;
}[And<NotEmpty<T>, NotEmpty<P>> extends true
  ? 0
  : IsEmpty<T> extends true
  ? 1
  : 2];


// The number is less than or equal to
type LessEq<T extends number, P extends number> = LessEqList<Range<T>, Range<P>>;

type t10 = LessEq<Zero, One>;
// type t10 = true
type t11 = LessEq<One, Zero>;
// type t11 = false

type t12 = LessEq<One, One>;
// type t12 = true
Copy the code

2.3 Implementation of subtraction

Subtraction has two ideas, list length subtraction evaluation and number subtraction evaluation

2.3.1 List subtraction

The default is large reduction, small reduction of large just need to judge the opposite, and then add a symbol on the line, here for the sake of simplicity did not achieve, can refer to the pseudo code as follows:

/ / pseudo code
const a = [1.2.3];
const b = [4.5];
const c = [];
while(b.length ! == a.length) { a.pop(); c.push(1);
}// c.length === a.length - b.lengthconsole.log(c.length);

// Subtract T - P from the tuple, and subtract one element from the tuple. When the length reaches 0, all that is left is the result. Use the third argument to carry the result
type SubList<T extends any[], P extends any[], R extends any[] = []> = {
  0: Length<R>;
  1: SubList<Shift<T>, P, Apped<R>>;
}[Length<T> extends Length<P> ? 0 : 1];
type t13 = SubList<Range<10>, Range<5> >;// type t13 = 5
Copy the code

2.3.2 Digital subtraction

Idea: Convert numbers into tuples and then compare

// Set size cannot be negative, default is greatly reduced
// Subtraction
type Sub<T extends number, P extends number> = {
  0: Sub<P, T>;
  1: SubList<Range<T>, Range<P>>;
}[LessEq<T, P> extends true ? 0 : 1];

type t14 = Sub<One, Zero>;
// type t14 = 1
type t15 = Sub<Ten, Five>;
// type t15 = 5
Copy the code

With these tools, we can translate the Fibonacci sequence implementation code, which we initially implemented in JavaScript, into TypeScript type encoding

Fib: JS function –> TS type

In JavaScript, we use functions

const fib = (n: number): number= > n <= 1 ? n : fib(n - 1) + fib(n - 2);
Copy the code

In TypeScript, when we use types, we’re simply changing the way we write them. We use type functions to describe operations

Because of TypeScript’s recursive limitations, you can’t solve for very large items, but the fun is over

export type Fib<T extends number> = {
  0: T;
  1: Add<Fib<Sub<T, One>>, Fib<Sub<T, Two>>>;
}[LessEq<T, One> extends true ? 0 : 1];

type r0 = Fib<Zero>;
// type r10= 0
type r1 = Fib<One>;
// type r1 = 1

type r2 = Fib<Two>;
// type r2 = 1

type r3 = Fib<3>;
// type r3 = 2

type r4 = Fib<4>;
// type r4 = 3

type r5 = Fib<5>;
//type r5 = 5

type r6 = Fib<6>;
// type r6 = 8
Copy the code

Finally, some other fun things to do:

  • TypeScript type metaprogramming: Implements 8-bit arithmetic
  • New in TypeScript 4.1: Vuex is a string template?
  • The Ts type system implements linear lookup

Four,

Does watching TypeScript implement Fibonacci sequences take you back to your “pipeline CPU” lab days?

IT’s development by leaps and bounds in recent decades, more and more “programmers” to join in the Internet industry, under some high-level language and development framework, “programmers” coding is also need to focus only on the business logic implementation, few people will pay attention to the computer is how to implement the underlying addition, subtraction, multiplication, and division, social in progress, of course, technology is also in the rapid iteration, Occasionally I stop to recall the first line of “Hello World! Code at that time rejoice oneself, perhaps that is we all can not go back to the youth…

Search the public number: DYBOY, the first time to get the latest technology thinking articles, push bytedance!