Drilled-prog
@Drilled-prog
Программирую на ['php', 'js']

Как лучше сделать экспорт класса, чтобы вызывать его как функцию?

Допустим имеем такой код
class BaseLol {  /* ... */  }

class Lol extends BaseLol {
  constructor(public name: string) {
    super();
    this.name = name.toUpperCase();
  }
}

Надо что бы можно его использовать таким образом, и что бы были подсказки по параметрам:
import lol from './lol';
const instance = lol('qwe');


Экспортировать его можно таким образом например
export default (name: string) => new Lol(name);
Или так
export default (...args: ConstructorParameters<typeof Lol>) => new Lol(...args);


Вопрос в том можно ли сделать какой то хелпер что бы не прокладывать параметры каждый раз, хотелось бы что то типа такого:
function exportLol(classFromExport) {
  return (...args) => new classFromExport(...args);
}
export default exportLol(Lol);


но тогда нету подсказок по параметрам.

Плюс этот базовый класс может использоваться на проекте без тайпскрипта, что бы были хоть минимальные подсказки, можно переписать первую конструкцию таким образом:

class BaseLol {  
constructor() {
    return new Proxy(this, {
      get(target, prop) {
        if (prop === "init") {
          return (...args: any) => {
            const clone = Object.assign(
              Object.create(Object.getPrototypeOf(target)),
              target
            );
            clone.init(...args);
            return clone;
          };
        }
      },
    });
  }
/* ... */  
}

class Lol extends BaseLol {
  init(name: string) {
    this.name = name.toUpperCase();
    return this;
  }
}


И экспортировать потом таким образом:
export default new Lol().init;
Тогда можно использовать таким образом:
import lol from './lol';
const instance = lol('qwe');

И видны подсказки по параметрам.

Суть в том, чтобы было удобно писать классы без лишнего бойлерплейта, и использовать их внешне как функцию без оператора new.
Хз как лучше...
  • Вопрос задан
  • 270 просмотров
Решения вопроса 1
Aetae
@Aetae Куратор тега TypeScript
Тлен
Вам лень писать new? Имхо, это перебор. Не отвалятся у вас руки три лишних символа написать, и это будет куда нагляднее чем непонятные излишние функции.

Но рас так хочется, то тип для твоей функции таков:
function exportConstruct<P extends any[], T>(classFromExport: { new (...args: P): T; }): 
(...args: P) => T {
  return (...args) => new classFromExport(...args);
}

Но так ты теряешь например возможность использовать статические методы класса.

Если можешь использовать Proxy то можно просто сделать класс вызываемым:
function exportCallable<T extends { new (...args: any[]): any; }>(classFromExport: T) {
  return new Proxy(classFromExport, {
    apply(ctor, _, args) {
      return new ctor(...args);
    }
  }) as T & ((...args: ConstructorParameters<T>) => InstanceType<T>);
}

const Lol = exportCallable(class Lol extends BaseLol {
  constructor(public name: string) {
    super();
    this.name = name.toUpperCase();
  }
});

Lol('qwe');


Либо ты можешь просто добавть статический метод, который будет делать то же самое:
abstract class Newable {
  static new<P extends any[], T>(this: { new (...args: P): T; }, ...args: P): T { 
    return (new this(...args)) as T
  }
}

class BaseLol extends Newable {  /* ... */ }

class Lol extends BaseLol {
  constructor(public name: string) {
    super();
    this.name = name.toUpperCase();
  }
}

Lol.new('qwe');
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы