Migrate blind boxes
When we convert Typescript from JavaScript with one click, any is the most convenient way to do it. It is not friendly to maintenance (although it can be run). At the same time, each variable is a blind box for us. What type is it?
Type Derivation
Derivation of basic types
The type derivation of basic data types is quite simple
let a = 1; type A = typeof a; // number; let b = '2' type B = typeof b; // string; let c; type C = typeof c; // undefined;
A typeof can deduce the original value type!
What are the basic data types we have sorted out?
// That's right, 7 data typestype Base = string | number | boolean | null | undefined | symbol | bigint;
Derivation of objects
Here we will implement how to deduce ordinary objects
let obj = { a: 1, b: '2', c: true, d: null }; type Obj = typeof obj; // { a: number; b: string; c: boolean; d: null; }
That's right, that's that simple!
Derivation of arrays
Why do the above objects besides the array? Because arrays are special in typescript, they can be declared with both original ancestors and arrays.
It can also be said that the array contains the Yuanzu
type isContain = [1, 2] extends Array<number> ? true : false; // true
Try to continue to implement derivation through typeof
let arr = [1, '2', null, false]; type Arr = typeof arr; // (string | number | boolean | null)[] ??? type Arr1 = typeof arr[0] // string | number | boolean | null ???
Well, what I got was an array of union types. Is it different from what we expected?
We define an array but do not declare the type. For arrays, the default is Array. Because the values of number, string, null, and boolean are constantly filled, it finally becomes Array<string | number | boolean | null>, and every element is a union type
Reorganize, what do we need?
[1, '2', null, false]
-> [number, string, null, boolean]
What we want is Yuanzu[number, string, null, boolean]
, not arrayArray<string | number | boolean | null>
To sort out todo, what we need is:
- Fixed-length array type
- Each element has an independent type
let arr = [1, '2', null, false] as const; type Arr = typeof arr; // readonly [1, '2', null, false] type Arr1 = typeof arr[0] // 1
The first todo is implemented, and the second one is a bit like, but it is not right. What we want is the data type, not a specific value
Implement a generic of a conversion type
What we want is 1 to number, 'A' to string, just list all the basic types and convert them.
type GetType<T> = T extends string ? string : T extends number ? number : T extends boolean? boolean : T extends null ? null : T extends undefined ? undefined : T extends symbol ? symbol : T extends bigint ? bigint : T; type Arr1 = typeof arr[0] // number type Arr2 = typeof arr[1] // string
Then you can realize the type conversion of the entire array by traversing the original one again.
type TransArr<T extends Array<unknown>, R extends unknown[] = []> = { 'loop': TransArr<T, [...R, T extends Base ? GetType<T[R['length']]>: T[R['length']] ] >, 'result': R, }[T['length'] extends R['length'] ? 'result': 'loop']; let arr = [1, '2', null, false] as const; type Arr = typeof arr; type ArrType = TransArr<Arr>; // [number, string, null, boolean]
Derivation of functions
The derivation of functions is actually not necessary. Why do you say that, function parameters and value types are uncontrollable except for individual operators or explicit types
If (a, b) => a * b , the return value must be number
If (a) => (a), the return value must be Promise
let fn1 = (a, b) => a * b; type Fn1 = typeof fn1; // (a: any, b: any) => number function fn2(a) { return (a); } type Fn2 = typeof fn2; // (a: any) => Promise<any>
Most of the results obtained by function after passing through typeof are
(a: any, b: any, ...) => any;
This type can limit the number of functions with more parameters
function fn3(a, b, c) { return a + b + c; } function fn4(d) { return d + 1; } type Fn3 = typeof fn3; // (a: any, b: any, c: any) => any type Fn4 = typeof fn4; // (d: any) => any type F3_4 = Fn3 extends Fn4 ? true : false; // false type F4_3 = Fn4 extends Fn3 ? true : false; // true
In other words, functions with more parameters always contain functions with fewer parameters.
According to the above judgment, we can use this to realize the function's judgment
type isFunc<T> = (() => any) extends T ? true : false;
Improve the guidance
- The basic type returns the type directly
- TransArr generic to array
- The function directly returns the value of typeof
- It uses keyof to traverse objects.
type Trans<T> = T extends Base ? GetType<T> : T extends Array<unknown> ? TransArr<T> : isFunc<T> extends true ? T : { [key in keyof T]: T[key] extends Base ? GetType<T[key]> : T[key] extends Array<unknown> ? TransArr<T[key]> : Trans<T[key]>; };
test
let a1 = 1; type test1 = Trans<typeof a1>; // number let a2 = '2'; type test2 = Trans<typeof a2>; // string let a3 = [1, '2', true, '3', 4] as const; type test3 = TransArr<typeof a3>; // [number, string, boolean, string, number] let a4 = { a: 1, b: true, c: { a: 1, b: [1, '2'] }, d: [true, null] } as const; type test4 = Trans<typeof a4>; // { // readonly a: number; // readonly b: boolean; // readonly c: { // readonly a: number; // readonly b: readonly [number, string]; // }; // readonly d: readonly [boolean, null]; // } let a5 = { a: [ { b: [ { c: 1 } ] } ] } as const; type test5 = Trans<typeof a5>; // { // readonly a: readonly [{ // readonly b: readonly [{ // readonly c: number; // }]; // }]; // } let a6 = (a, b, c) => a + b + c; type test6 = Trans<typeof a6>; // (a: any, b: any, c: any) => any let a7 = [ function fn() { return 1; }, (a, b) => a * b, (a) => (a) ] as const; type test7 = TransArr<typeof a7>; // [() => number, (a: any, b: any) => number, (a: any) => Promise<any>] let a8 = { a: 1, b: [true, null], c: [() => void, (a, b) => a], d: { e: [ (a, b) => null, { f: [1] } ] } } as const; type test8 = Trans<typeof a8>; // { // readonly a: number; // readonly b: readonly [boolean, null]; // readonly c: readonly [() => undefined, (a: any, b: any) => any]; // readonly d: { // readonly e: readonly [(a: any, b: any) => null, { // readonly f: readonly [number]; // }]; // }; // }
This is the end of this article about using typescript to deduce blind box types with existing variables. For more related typescript blind box types, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!