Writing in the front

This article executes typescript in version 4.5.4Copy the code

Fibonacci

Now, although we’re all familiar with Fibonacci, let’s just say, for a well-known mathematical sequence, the way to extrapolate is as follows

  • Fib(0) = 0
  • Fib(1) = 1
  • Fib(n) = Fib(n-1) + Fib(n-2)

The final sequence is going to be 1, 2, 3

0 1 1 2 3 5 8 13 21 34 55 89...Copy the code

Implementation logic

After Fibonacci, take a look at the core points that typescript type reckoning addresses

  • The 0 and 1 numbers return themselves
  • Something is equal to the sum of the first two numbers
  • To calculate a number you need to loop or recurse to get the first two values
  • Only numbers can be entered and cannot be negative numbers

Let’s take a look at what typescript offers and what it lacks

First problem: the 0 and 1 numbers return themselves

This satisfaction can be achieved through extends

type GetSelf<T> = T extends 0 | 1 ? T : never;
/ / test
type Test0 = GetSelf<0>; / / 0
type Test1 = GetSelf<1>; / / 1
type Test2 = GetSelf<2>; / / 2
Copy the code

Second problem: some number is equal to the sum of the first two numbers

This gets tricky because typesript doesn’t add, meaning that typescript doesn’t know the result of 1 + 2 =, so it lists a todo

Third problem: Calculating a number requires looping or recursing to get the first two values

See if there’s any recursion in typescript. Yes, there is, such as implementing a linked list

type Node<T> = {
    val: T;
    next: Node<T>;
}
Copy the code

But how do we get out of the loop, and we need a value instead of returning an object and listing a todo

Fourth question

Only numbers can be entered and cannot be negative numbersCopy the code

Limiting numbers is easy. Extends Number does that. What about negative numbers?

What's the difference between a negative number and a positive number?Copy the code

Negative numbers are shown in multiple symbols, so the length of the string is not the same as the positive number, try

type len1 = '123'['length']; // number
type len2 = number[] ['length']; // number;
type len3 = [1.2.3] ['length']; / / 3
type len4 = [number.string] ['length']; / / 3
Copy the code

The lengths of strings and undefined arrays are surprisingly unpredictable, and only tuples seem to work

Negative numbers are less than zero, but typescript doesn’t compare sizes, so a todo

conclusion

We can solve the first problem and know that we can get the length of the progenitor by length. Todo is as follows

  • Add operation
  • A loop or recursive calculation with an escape condition
  • Judge non-negative

To solve the todo

+ 1 operation

Although most of the functions were not calculated in the previous round, a useful conclusion was drawn that the meta-ancestor can obtain the value of length. Can +1 be interpreted as PUSH and length is taken out? try

type Push<T extends Array<number>, P extends number> = [...T, P];

type arr1 = [1.2];
type arr2 = Push<arr1, 3>; / / [1, 2, 3]
type len1 = arr1['length'] / / 2
type len2 = arr2['length']; / / 3
Copy the code

So if we do +1, we should be able to add, +n is n times, and the end condition is n

Therefore, the addition operation can be converted to the primitive and then the length can be calculated, similar to the JavaScript Array(n).fill(0). The first step is to convert the number to Array

Transfer digital array

type ArrOf<T extends number, P extends Array<0> = []> = {['loop']: ArrOf<T, [...P, 0>; ['result']: P;
}[P['length'] extends T ? 'result' : 'loop'];

type arrof1 = ArrOf<5>; // [0, 0, 0, 0, 0]
Copy the code

Because we need to recurse and then jump out of the condition, and then return the value, we can construct an object and get the key, and the key is the key to jump out of the loop, and the judgment to jump out of the loop is that the length of the ancestor is equal to the number of inputs

Based on the above implementation, we can get the complete implementation of ADD

type ADD<A extends number, B extends number> = [...ArrOf<A>, ...ArrOf<B>]['length'];

type add1 = ADD<3.4>; / / 7
Copy the code

Although the result can be calculated, I was given a warning

A rest element type must be an array type.
Copy the code

I think he may not be able to calculate that the number returned is array, so we need to declare that the number returned by ArrOf is array, similar to array.from

type ArrFrom<T> = T extends Array<T> ? T : T;

type ADD<A extends number, B extends number> = [...ArrFrom<ArrOf<A>>, ...ArrFrom<ArrOf<B>>]['length'];
Copy the code

Now that we’ve done the addition and recursion, let’s look at non-negative numbers

Nonnegative judgment

What’s so special about negative numbers, that they have multiple symbols and always have the first sign

type str11 = 'abcde';
type str12 = str11[0]; // string
Copy the code

So we can’t infer from subscripts

type getFirst<T extends string> = T extends `${infer P}The ${string}` ? P : T;

type str11 = 'abcde';
type str12 = getFirst<str11>; // a
Copy the code

So we can convert numbers into strings and get signs, and then get negative numbers

type FirstStr<T extends number> = `${T}` extends `${infer P}The ${string}` ? P : T;

type isFu<T extends number> = FirstStr<T> extends The '-' ? true : false;

type isFu1 = isFu<0>; // false
type isFu2 = isFu<12>; // false
type isFu3 = isFu<-6>; // true
type isFu4 = isFu<-0>; // true
Copy the code

Fibonacci

All the pieces are in place to implement Fibonacci

type ArrOf<T extends number, P extends Array<0> = []> = {['loop']: ArrOf<T, [...P, 0>; ['result']: P;
}[P['length'] extends T ? 'result' : 'loop'];
// Line 8 indicates that the result may not be array
type ArrFrom<T> = T extends Array<T> ? T : T;

type ADD<A extends number, B extends number> = [...ArrFrom<ArrOf<A>>, ...ArrFrom<ArrOf<B>>]['length'];
// Line 23 indicates that the result may not be number
type NumberFrom<T> = T extends number ? T : T & number;

type ADD2<A extends number, B extends number> = NumberFrom<ADD<A, B>>;

type FirstStr<T extends number> = `${T}` extends `${infer P}The ${string}` ? P : T;
// Add negative judgment
type isFu<T extends number> = FirstStr<T> extends The '-' ? true : false;

type FIB<T extends number, A extends number = 0, B extends number = 1, N extends number = 0> = isFu<T> extends true
    ? never
    : T extends 0 | 1
? T
: {
    ['loop']: FIB<T, B, ADD2<A, B>, ADD2<N, 1> >; ['result']: B;
}[T extends ADD2<N, 1>?'result' : 'loop'];

type FIFU1 = FIB<-6> // never
type FI0 = FIB<0> / / 0
type FI1 = FIB<1>; / / 1
type FI2 = FIB<2>; / / 1
type FI3 = FIB<3>; / / 2
type FI4 = FIB<4>; / / 3
type FI5 = FIB<5>; / / 5
type FI6 = FIB<6>; / / 8
type FI7 = FIB<7>; / / 13
type FI8 = FIB<8>; / / 21
type FI9 = FIB<9>; / / 34
Copy the code

Write in the last

The code word is not easy, if it helps you, please click “like”, thank you guys.