nanobar
nanobar

Reputation: 66405

Typescript: Optional data transform which returns different type if used

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

Upvotes: 0

Views: 83

Answers (2)

spender
spender

Reputation: 120498

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

Upvotes: 1

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 250106

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

Upvotes: 1

Related Questions