Home > Software engineering >  Typescript: Optional data transform which returns different type if used
Typescript: Optional data transform which returns different type if used

Time:04-07

I have a function which fetches data and optionally transforms the response to another interface.

I'm having trouble expressing this in Typescript though:

type ObjMap = Record<string, any>;

function mapResponse<T extends ObjMap, U extends ObjMap = T>(
  apiData: T,
  transform: (apiModel: T) => U = x => x
) {
  return transform(apiData)
}

// Should return AppUser
const data1 = mapResponse<ApiUser, AppUser>(
  userData,
  u => ({ fullName: `${u.firstName} ${u.lastName}`})
)

// Should return ApiUser
const data2 = mapResponse<ApiUser>(userData)

TS Playground

The above code produces an error here:

x => x
    ~~

Type 'T' is not assignable to type 'U'.
'T' is assignable to the constraint of type 'U', but 'U' could be instantiated with a different subtype of constraint 'ObjMap'.(2322)

How can I type this so that the

  • Return type is ApiUser if no transform is defined (we can just use the default transform x => x, or none if that's easier)
  • Returns ApiUser if the transform is defined?

CodePudding user response:

One option is to have an implementation signature that is not generic. here ou can specify the default value for the parameter:

function mapResponse<T extends ObjMap, U extends ObjMap = T>(
  apiData: T,
  transform?: (apiModel: T) => U
):U
function mapResponse(
  apiData: Record<string, any>,
  transform: (apiModel: Record<string, any>) => Record<string, any> = x => x
) {
  return transform(apiData)
}

Playground Link

CodePudding user response:

Something like this might be the answer... I simplified your code for ease of reproduction.

function mapResponse<T>(apiData: T): T
function mapResponse<T, U>(apiData: T, transform: (apiModel: T) => U): U
function mapResponse<T, U = T>(
    apiData: T,
    transform?: (apiModel: T) => U
): T | U {
    const trans = transform ?? (x => x as unknown as U)
    throw Error()
}

The implementation might get messy, but the interface to the outside world is consistent.

Playground Link

  • Related