I have this issue trying to infer type for function post
return type. It's second parameter is an object that has a transform
property. So if transform
parameter is provided then post
's return type should be transform
's return type, else the first the first generic type provided for post
function. I hope to be understood.
type DefautlHttpCallInit = {
throttle?: number;
baseUrl?: string;
url?: string;
onSend?(e: HttpCallInit) : void | Promise<void>;
}
export type HttpCallInit = RequestInit & DefautlHttpCallInit
export type HttpCallInitOf<T> = RequestInit & DefautlHttpCallInit & {
transform?: <TOut>(v: T) => TOut
};
export type HttpCallerInstance = {
post<T, TInit extends HttpCallInitOf<T>>(data?: T, init?: TInit): Promise<TInit extends {transform(e: T): infer XOut} ? XOut : T>;
}
//hack reference
let r = {} as HttpCallerInstance;
interface Post {
id?: number;
title: string;
}
interface User {
id: number;
userName: string;
}
interface UserPost extends Post{
user: User
}
const user = {/* info props */} as User;
r.post({title: 'New post'} as Post, {
//First: infer
transform(post) {
return Object.assign(post, {user}) as UserPost
}
})
Compiler Options
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictPropertyInitialization": true,
"strictBindCallApply": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"alwaysStrict": true,
"esModuleInterop": true,
"declaration": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"target": "Latest",
"module": "ESNext",
"moduleResolution": "node"
}
}
CodePudding user response:
you are specifying the generic TOut
as a generic on the transform itself but that is not what it represents, a generic on a method or function dictates behaviour when it gets called. You want to specify the transform output type at declaration (in the same place as T
) then specify it as a generic of the post
function like this:
export type HttpCallInitOf<T, TransformedType = unknown> = RequestInit & DefautlHttpCallInit & {
transform?: (v: T) => TransformedType
};
export type HttpCallerInstance = {
post<T, TransformedType = T>(data?: T, init?: HttpCallInitOf<T,TransformedType>): Promise<TransformedType>;
// TransformedType is defaults to T so if it can't be infered it stays as T
}