I am trying to make a function general enough to take any informer and any mapper and apply dependency injection for the logic not to be changed if the informer and mapper were to change, however, it seems like I am doing something wrong about my function argument generalization.
Also if I were to change unknown to any in the myFunc inform signature, it would warn me that any is not advice and is unexpected.
Interfaces
interface Document {
name: string
}
interface Mapper {
(doc: Document): unknown
}
Function
const myFunc = async (
inform: (payload: unknown)=>Promise<Record<string, unknown>>,
document: Document,
mapper: Mapper): Promise<string> => {
const response = <Record<string, unknown>> await inform(mapper(document));
return response
})
Problem
class APIClient {
async inform(payload: RequestInfo): Promise<Record<string, unknown>> {
// Implementation
}
}
const apiClient = new APIClient();
const result = await myFunc(
apiClient.inform, //Complaining that unknown cannot be assigned to RequestInfo.
someData,
someMapper
);
CodePudding user response:
unknown is indeed difficult to use as a generalization mean.
And as you figured out, you could use any instead, but that removes any type safety.
You have a use case for generics, where you do not know the exact data types in advance, but you know that there must be some relations between them:
mappermust take an argument typeDocument2(caution:Documenttype is already available in global scope of many environments, name collision will give unexpected result, mainly interface merging, so I will useDocument2instead)- It must return a type "P" (not known in advance) that
informcan take as input.
Therefore you need at least 1 generic type ("P" above):
const myFunc = async <P, R>(
inform: (payload: P) => Promise<Record<string, R>>, // inform takes P in
document: Document2,
mapper: (doc: Document2) => P) => { // mapper outputs P
const response = await inform(mapper(document));
return response
}
Here is an example where "P" is string:
const someData: Document2 = {
name: 'hello'
}
const someMapper = (doc: Document2) => doc.name // Outputs a string
const result = myFunc(
apiClient.inform, // RequestInfo is string | Request, so a string in is fine!
someData,
someMapper
);
