Home > Software design >  Can't return Tuple, Typescript. Instead it's interpreting as an Array
Can't return Tuple, Typescript. Instead it's interpreting as an Array

Time:01-08

I'm trying to add type-safety to the return of a general function a previous dev wrote, and need help with the syntax:

export function to(promise:Promise<any>) {  
  return promise
    .then(data => [null, data])
    .catch(err =>  [err, null]);
 }

It's a pretty simple function that takes in a promise, and returns a new promise whose output type is a tuple [Error|null, <The First Promise's Return Type>|null]. Essentially, the first value will always be an error if one occured (else null) and the second will be the result of awaiting the first promise (else null). You can use it like:

const [ err, result ] = await to(/*<API Call>*/);
if (err) {
    setError(err);
    // ...
} else if (result) {
    processApiResult(result);
    // ...
}

I know the following's wrong ((T|null)[] should not be an "array of either of those types", but instead "a tuple of those types with a specific order"), but I've been banging my head against the wall & this is as close as I could get.

export function to<T>(promise:Promise<T>):Promise<(T|null)[]|[Error, null]> {  
  return promise
    .then(data => [null, data])
    .catch(err => [err as Error, null]);
 }
 
const [ err, result ] = await to(api.changePassword(username, password));  
// const error : string | Error | null
// const result : string | null

The first param should be Error | null (not | string), and the second is correct as string | null.

CodePudding user response:

If you are certain that the tuples returned by to() will not be mutated, you can use const assertions to type this function.

export function to<T>(promise:Promise<T>):Promise<readonly [Error | null, T | null]>  {  
  return promise
    .then(data => [null, data] as const)
    .catch(err => [err as Error, null] as const);
}

const [ err, result ] = await to(api.changePassword(username, password));  
// err: Error | null
// result: string | null

This works because as const will transform the type of array literals to readonly tuples.

If you don't want the tuples to be readonly for some reason, then you can also do the following:

export function to<T>(promise:Promise<T>):Promise<[Error | null, T | null]>  {  
  return promise
    .then(data => [null, data] as [null, T])
    .catch(err => [err as Error, null] as [Error, null]);
}

const [ err, result ] = await to(api.changePassword(username, password));  
// err: Error | null
// result: string | null

Either way, you just need some away of telling the compiler that the values to() is returning are fixed-sized tuples, not arrays.

CodePudding user response:

function to<T>(promise:Promise<T>):Promise<[Error|null,T|null]> {
    return promise
        .then((data):[null,T] => ([null, data]))
        .catch((err) =>( [err, null]));
}

async function func() {
    const [ err, result ] = await to<string>(new Promise(resolve => resolve));
    console.log(err,result)
}

  • Related