Home > Blockchain >  TS infer from return type of callback property of an object parameter
TS infer from return type of callback property of an object parameter

Time:11-08

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"
  }
}

Error

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
}
  • Related