I didn’t know typescript could play like this until I saw a guy in Zhihu implement a Chinese chess program with typescript type operation some time ago.

A infer operator is used to infer the infer operator. Infer means the type variables to be inferred in the extends condition statement.

Here is the typescript system’s built-in ReturnType to infer the type of data, where infer R can be interpreted as a placeholder and the ReturnType.

infer

Now that we know the use of infer we can practice our usage

  • Take the first element in the array
  • takehello worldIn thehello

Take the first element in the array

typeAn array of1 = [686.999];
typeGets the first element of an array < any arrayextends any[]> = any arrayextends[First element: infer the first element of the array... rest element: infer the rest of the array] The first element of the array:any;

typeThe first element is = gets the first element of the array < array1>;
Copy the code

We tested it and we got 686.

takehello worldIn thehello

typeHello world ='hello world';
typeGets the world < stringextends string> = stringextends `${infer R} world` ? R : any;
typeWorld = Get world < Hello world >Copy the code

The two examples above show that infer represents a type variable to be inferred from the extends condition statement

Falt flatten the array

Falt flattens an array of any number of layers. By default, it flattens one layer

[1[2[3]]].flat() / / [1, 2, [3]]
Copy the code

Let’s look at the implementation of Falt. I wrote the following two.

// add recursion
function flat(arr, depth = 1) {
  if (depth === 0) return arr;
  return arr.reduce((r, c) = > {
    if (Array.isArray(c)) {
      return [...r, ...flat(c, depth - 1)];
    }
    return[...r, c]; } []); }// recursion + destruct
function flat(arr, depth = 1) {
    if (depth === 0 || arr.length === 0) return arr;
    const [t, ...rest] = arr;
    if (Array.isArray(t)) {
        return [...flat(t, depth - 1), ...flat(rest, depth)];
    }
    return [t, ...flat(rest, depth)];
}
Copy the code

Typescript doesn’t have a type that can be traversed directly like Reduce, so it takes the second approach.

Decompose problems

  • Represented by array [‘length’]depth
  • Type generateddepthArray of length
  • The type minus one representsdepth-1

Represented by array [‘length’]depth

There is a depth -1 operation in the above code, but in typescript you can’t subtract one directly from a type. A trick here is to use the length of an array.

typeLength is3Array = [any.any.any]
typeGets the array length < arrayextends any[]> = array []'length'];
typeThree = get the array length < the length is3An array of >Copy the code

Type generateddepthArray of length

So we can represent a number as long as we have a type that generates an array of arbitrary length. I’m just going to recurse here

typeGenerates an empty array of the corresponding length < length, resulting in an arrayextends any[] = []> = result array ["length"] extendsThe length? Result array: Generates an empty array of the corresponding length < length, [any. Result array]>;typeLength of3Generate an empty array of the corresponding length <3>;
Copy the code

The type minus one representsdepth-1

To do depth -1, you just subtract one element from the depth array to make it depth -1. So here we use infer

// Use deconstruction to remove the first element of the array
typeArray length minus one < arrayextends any[] = []> = arrayextends[First element: infer the first element of the array... rest element: infer the rest of the array] Remaining arrays: arrays;typeMinus one < Nextends number> = Get the array length < array length minus 1 < generate an empty array of the corresponding length <N>>>;typeNine = minus one <10>
Copy the code

And that gives us depth minus 1.

Flat flat arbitrary level array

With the above types, typescript versions of Flat can be implemented using flat functions that extend if else statements to function logic

typeFlatten any hierarchical array < multidimensional arrayextends any[], layer numberextends number = 1> = layersextends 0? Get array length < multidimensional array >extends 0? Multidimensional arrays: Multidimensional arrays: Multidimensional arraysextends[First element: infer the first element of the array... rest element: infer the rest of the array] The first element of the arrayextends any[]? [... flatten the first element of the array, subtract one layer >>,... flatten the first element of the array, subtract one layer >>,... flatten the first element of the array, subtract one layer >>,... flatten the remaining array, layers >] : multi-dimensional array;Copy the code

Let’s do a complex array leveling two-layer test. OK

The complete code

typeGenerates an empty array of the corresponding length < length, resulting in an arrayextends any[] = [] > = result array ["length"] extendsThe length? Result array: Generates an empty array of the corresponding length < length, [any. Result array]>;typeArray length minus one < arrayextends any[] = []> = arrayextends[First element: infer the first element of the array... rest element: infer the rest of the array] Remaining arrays: arrays;typeMinus one < Nextends number> = Get the array length < array length minus 1 < generate an empty array of the corresponding length <N>> >;typeFlatten any hierarchical array < multidimensional arrayextends any[], layer numberextends number = 1> = layersextends 0? Get array length < multidimensional array >extends 0? Multidimensional arrays: Multidimensional arrays: Multidimensional arraysextends[First element: infer the first element of the array... rest element: infer the rest of the array] The first element of the arrayextends any[]? [... flatten the first element of the array, subtract one layer >>,... flatten the first element of the array, subtract one layer >>,... flatten the first element of the array, subtract one layer >>,... flatten the remaining array, layers >] : multi-dimensional array;typeArray = Flatten any level array <[[0], [1[2The [[3[4]]], [5[6].7]]], [8[9]], 10].2>;
Copy the code

The last

The key to the above implementation is how to increase or decrease numbers and reasonable functional logic. If you’re not familiar with it, you can start with something simpler, like flattening out a one-dimensional array. The function of drawing is a one-dimensional array and can be implemented with infer.

const faltern = (arr) = > {
    if (arr.length === 0) return arr;
    const [top, ...rest] = arr;
    if (Array.isArray(top)) {
        return faltern([...top, ...rest]);
    }
    return [top, ...faltern(rest)];
};
Copy the code