Topic describes
The original address has a class called EffectModule
class EffectModule {}
Copy the code
This object has a variety of attributes, such as string, number, function, etc., where the function attribute can only have two types of signature:
asyncMethod<T, U>(input: Promise<T>): Promise<Action<U>>
syncMethod<T, U>(action: Action<T>): Action<U>
Copy the code
Now we have a function called connect that takes an instance of EffectModule and turns it into another object that has only the same method as the EffectModule, but whose type signature has been changed:
interfaceAction<T> { payload? : Ttype: string
}
asyncMethod<T, U>(input: Promise<T>): Promise<Action<U>> becomes asyncMethod<T, U>(input: T): Action<U> syncMethod<T, U>(Action: Action<T>): Action<U> becomes syncMethod<T, U>(Action: T): Action<U>Copy the code
Example: EffectModule is defined as follows:
interfaceAction<T> { payload? : T;type: string;
}
class EffectModule {
count = 1;
message = "hello!";
delay(input: Promise<number>) {
return input.then(i= > ({
payload: `hello ${i}! `.type: 'delay'
}));
}
setMessage(action: Action<Date>) {
return{ payload: action.payload! .getMilliseconds(),type: "set-message"}; }}Copy the code
After connect:
// The problem is to replace any with solutions to make ts compile properly
Connect returns the same value as Connected. Ts compiles
type Connect = (module: EffectModule) = > any
const connect: Connect = m= > ({
delay: (input: number) = > ({
type: 'delay',
payload: `hello 2`
}),
setMessage: (input: Date) = > ({
type: "set-message",
payload: input.getMilliseconds()
})
});
type Connected = {
delay(input: number): Action<string>
setMessage(action: Date): Action<number>}const effectModule = new EffectModule()
const connected: Connected = connect(effectModule)
Copy the code
Type Connect = (module: EffectModule) => any, replace any with solution, let Connect return the same value as Connected, and let ts compile properly
Obviously, what we need to do is:
- Extract the function type of the EffectModule
- Solve the promise/action for the function’s arguments and return values
Extract the function type of the EffectModule
And speaking of anything, it’s a Pick or Omit Omit operation. However, ts does not have the same method as object.keys ().filter, which requires the mapping type +never to do special processing. The whole process is: mapping type = if the value is a function type, return key, otherwise never =, evaluate the mapping type, and get the key of the function type
Mapping type
Key is used to map one type to another. Key uses a syntax similar to for in to iterate over each key in the old type:
type mapType0<T> = {
[k in keyof T]: T[k]
}
Copy the code
Add [keyof T] to the end of the mapping type, which is equivalent to the method valueof.
const o = {
a: 1,
b: '2'
}
type map1 = mapType0<typeof o>[keyof typeof o]
// string | number
Copy the code
Pick and Omit
Now that we know the mapping type, we can implement a Pick based on this (ts already comes with it)
type myPick<T, K extends keyof T> = {
[k in K]: T[k]
}
Copy the code
The second generic parameter constraint key comes from T, which ensures that some of the original object’s keys are retrieved. 8. Omit anything. 8
type MyOmit<T, K extends keyof any>
= Pick<T, Exclude<keyof T, K>>;
Copy the code
Get the key whose value is of type function
type FunctionKeys<T> = {
[k in keyof T]: T[k] extends Function ? k : never
}[keyof T]
type functionKeys = Pick<EffectModule.FunctionKeys<EffectModule>>
Copy the code
Use Omit Omit
type noFunctionKeys<T> = {
[k in keyof T]: T[k] extends Function ? never : k
}[keyof T]
type functionKeys = Omit<EffectModule.noFunctionKeys<EffectModule>>
Copy the code
Now, you have an object with only a key of type value
Solve the promise/action for the function’s arguments and return values
infer
Infer refers to type variables to be inferred in conditional statements of condition type, which can be understood as solving equations. Infer X means x is the variable to be solved. Infer is equivalent to a marking function. For example, ReturnType is achieved by infer
type MyReturnType<T> = T extends(... args:any[]) => infer P ? P : any;
Copy the code
Let’s look at two more examples, such as resolving promises and getting the item type of the array:
type UnPromisify<T> = T extends Promise<infer U> ? U : T
type UnArray<T> = T extends (infer U)[] ? U : T
Copy the code
Implementation effect
So, based on the previous steps, we can extract function parameters, return values, and solve promises and actions
type UnPromisify<T> = T extends Promise<infer U> ? U : T
type UnAction<T> = T extends Action<infer U> ? U : T
type MapTypeToUnPromisifyAndUnAction<T extends any[]> = {
[k in keyof T]: UnAction<UnPromisify<T[k]>>
}
type Connect = (module: EffectModule) = >({[functionKey in keyof functionKeys] : (input: MapTypeToUnPromisifyAndUnAction<
Parameters<functionKeys[functionKey]>
>[number]) = >UnPromisify<ReturnType<functionKeys[functionKey] > >})Copy the code
All the code
type FunctionKeys<T> = { [k in keyof T]: T[k] extends Function ? k : never }[keyof T]
type functionKeys = Pick<EffectModule.FunctionKeys<EffectModule>>
type UnPromisify<T> = T extends Promise<infer U>?U : T
type UnAction<T> = T extends Action<infer U>?U : T
type MapTypeToUnPromisifyAndUnAction<T extends any[] > ={
[k in keyof T]: UnAction<UnPromisify<T[k]>>
}
type Connect = (module: EffectModule) = >({[functionKey in keyof functionKeys] : (input: MapTypeToUnPromisifyAndUnAction<
Parameters<functionKeys[functionKey]>
>[number]) = >UnPromisify<ReturnType<functionKeys[functionKey] > >})Copy the code