rv: any rv will be any.
class A {
  static groupBy<T>(xs: T[], key: keyof T) {
    return xs.reduce((rv: any, x) => {
      (rv[x[key]] = rv[x[key]] || []).push(x);
      return rv;
    }, {});
  }
}
How to use it:
interface Person {
  name: string;
  age: number;
  salary: number;
}
const data: Person[] = [
  {name:"deepak", age: 30, salary: 2000},
  {name:"deepak1", age: 32, salary: 1000},
  {name:"deepak", age: 29, salary: 3000}
]
class A {
  static groupBy<T>(xs: T[], key: keyof T) {
    return xs.reduce((rv: any, x) => {
      (rv[x[key]] = rv[x[key]] || []).push(x);
      return rv;
    }, {});
  }
}
console.log(A.groupBy(data, "name"))
Defination from Lodash:
groupBy(
            predicate?: Lodash.ListIterator<T, boolean> | Lodash.DictionaryIterator<T, boolean> | string,
            thisArg?: any,
        ): Lodash.Dictionary<T[]>;
        groupBy<R extends {}>(predicate?: R): Lodash.Dictionary<T[]>;
Sync group return an object and object cant have any other element as key other than string|number. Else you can follow more genric solution.
interface Any<T> {
  [key: string]: T[];
}
interface SMap<T> {
  [key: string]: T;
}
class A {
  static groupBy<T extends SMap<string>>(xs: T[], key: keyof T) {
    return xs.reduce((rv: Any<T>, x) => {
      if (!rv[x[key]]) {
        rv[x[key]] = [];
      }
      rv[x[key]].push(x);
      return rv;
    }, {});
  }
}