Home > Software engineering >  rxjs map cannot hint type to project function with overload, causing type errors if not explicitly d
rxjs map cannot hint type to project function with overload, causing type errors if not explicitly d

Time:02-08

I am have a hard time figuring how to make typescript happy with this case:

const t1 = new BehaviorSubject("test");
const t2 = new BehaviorSubject(["test2"]);

function transformString(subject:string):string
function transformString(subject:string[]):string[]
function transformString(subject:string|string[]):string|string[]{
  if(Array.isArray(subject)){
    return subject.map(t=>t.toUpperCase());
  }
  return subject.toUpperCase();
}


t1.pipe(map(transformString)); // Type error
t2.pipe(map(transformString));

t1.next(transformString(t1.value));
t2.next(transformString(t2.value));

The type error I am receiving is this:

Argument of type 'OperatorFunction<string[], string[]>' is not assignable to parameter of type 'OperatorFunction<string, string[]>'. Type 'string[]' is not assignable to type 'string'.ts(2345)

I have added the last two lines just to test if the override was working correctly and it does. For now, the only solution I find is to hunt the types at the map level:

t1.pipe(map<string,string>(transformString));

But this is getting redundant since we do know t1's type already. Also, the plan was at first to include the map into a variable, but I left it outside for now.

Edit:

As @Zerotwelve pointed out, the code above can work without using the overloads. I did not catch the real issue from my code in the minimal code above.

The problem comes when this is used in another function:

function t1F(): Observable<string> {
  const t1 = new BehaviorSubject("test");
  return t1.pipe(map(transformString));
}

Here typescript complains that

Type 'Observable<string | string[]>' is not assignable to type 'Observable<string>'.   Type 'string | string[]' is not assignable to type 'string'.
    Type 'string[]' is not assignable to type 'string'.

CodePudding user response:

you can do it just like this:

const t1 = new BehaviorSubject("test");
const t2 = new BehaviorSubject(["test2"]);

function transformString(subject:string|string[]):string|string[]{
  if(Array.isArray(subject)){
    return subject.map(t=>t.toUpperCase());
  }
  return subject.toUpperCase();
}

t1.pipe(map(transformString));
t2.pipe(map(transformString));

CodePudding user response:

You can try something like the following:

function transformString<T extends string | string[]>(subject: T): T {
  if (Array.isArray(subject)) {
    return subject.map((t) => t.toUpperCase()) as T;
  }
  return subject.toUpperCase() as T;
}

function t1F(): Observable<string> {
  const t1 = new BehaviorSubject('test1');
  return t1.pipe(map(transformString));
}

function t2F(): Observable<string[]> {
  const t1 = new BehaviorSubject(['test2']);
  return t1.pipe(map(transformString));
}

  •  Tags:  
  • Related