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.