Машинопись нового класса по оценке параметров - PullRequest
0 голосов
/ 30 апреля 2018
class A {
   public aCall(a: any, payload: string) {}
   public bCall(a: any, payload: number) {}
   public cCall(a: any) {}
   .
   .
   .
}

function createNewClass(aCtor: A) {
  // load all of the A method and remove first params
  // generic code on here
  // Final result should be like this
  return class B {
    public aCall(payload: string) {}
    public bCall(payload: number) {}
  }
}

// C.d.ts
interface C extends createNewClass(A) {}

Могу ли я иметь функцию (или декоратор в методе) для оценки входящего класса и генерирования нового класса с удалением всех первых параметров, чтобы я мог использовать новый класс для расширения или он просто не может это сделать

Ответы [ 2 ]

0 голосов
/ 30 апреля 2018

Если по какой-то причине вы действительно пытаетесь реализовать эту вещь, вы можете сделать что-то вроде следующего. Обратите внимание, что я собираюсь заменить методы только двумя аргументами. Если вам нужно использовать все методы, то ввод текста должен быть более сложным, как в ответе @ TitianCernicova-Dragomir:

type RemoveFirstArgOfTwoArgMethods<T> = { [K in keyof T]:
  T[K] extends (a: any, payload: infer P) => infer R ? (payload: P) => R : T[K];
}

function createNewClass<T>(aCtor: new (...args: any[]) => T): new (...args: any[]) => RemoveFirstArgOfTwoArgMethods<T> {

  const B = (class extends (aCtor as any) {}) as new (...args: any[]) => RemoveFirstArgOfTwoArgMethods<T>;

  // you will need to actually decide what that first argument will be
  const firstVal: any = "whoKnows";

  Object.keys(aCtor.prototype).forEach(k => {
    const protoVal = (aCtor.prototype)[k];
    if ((typeof protoVal === 'function') && (protoVal.length === 2)) {
      B.prototype[k] = function (...args: any[]) { return (protoVal as Function).call(this, firstVal, ...args) }
    }
  })

  return B;
}

Идея состоит в том, что он расширит исходный класс, но заменит его методы с двумя аргументами новыми методами с одним аргументом, которые вызывают исходный метод с постоянным первым аргументом (в данном случае это строка "whoKnows", но вы можете захотеть что-то еще).

Вы можете убедиться, что вышеперечисленное работает:

class A {
  public aCall(a: any, payload: string) {
    console.log("aCall(" + a + "," + payload + ")");
  }
}

const a = new A();
a.aCall("explicit", "call"); // aCall(explicit, call);

const C = createNewClass(A);
const c = new C();
c.aCall("implicit"); // aCall(whoKnows, implicit);

Вероятно, существуют всевозможные предостережения, когда речь идет о играх с такими классами, поэтому будьте осторожны, чтобы вы действительно поняли свой вариант использования и то, что происходит, когда сталкиваетесь с поведением, которое ему не соответствует.

Надеюсь, это поможет. Удачи!

0 голосов
/ 30 апреля 2018

См. Ниже для ответа 3.0

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

type RemoveFirstArg<TCtor extends new (... args: any[]) => any > = ReplaceInstanceType<TCtor,  { [P in keyof InstanceType<TCtor>]: RemoveArg<InstanceType<TCtor>[P]> }>
function createNewClass<TCtor extends new (... args: any[]) => any >(aCtor: TCtor) : RemoveFirstArg<TCtor>{
    // load all of the A method and remove first params
    return null as any;
}

type IsValidArg<T> = T extends object ? keyof T extends never ? false : true : true;
type RemoveArg<T> = T extends (a: infer A, b: infer B, c: infer C, d: infer D, e: infer E, f: infer F, g: infer G, h: infer H, i: infer I, j: infer J) => infer R ? (
    IsValidArg<J> extends true ? (b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J) => R :
    IsValidArg<I> extends true ? (b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I) => R :
    IsValidArg<H> extends true ? (b: B, c: C, d: D, e: E, f: F, g: G, h: H) => R :
    IsValidArg<G> extends true ? (b: B, c: C, d: D, e: E, f: F, g: G) => R :
    IsValidArg<F> extends true ? (b: B, c: C, d: D, e: E, f: F) => R :
    IsValidArg<E> extends true ? (b: B, c: C, d: D, e: E) => R :
    IsValidArg<D> extends true ? (b: B, c: C, d: D) => R :
    IsValidArg<C> extends true ? (b: B, c: C) => R :
    IsValidArg<B> extends true ? (b: B) => R :
    IsValidArg<A> extends true ? () => R :
    T
) : never

type ReplaceInstanceType<T, TNewReturn> = T extends new (a: infer A, b: infer B, c: infer C, d: infer D, e: infer E, f: infer F, g: infer G, h: infer H, i: infer I, j: infer J) => infer R ? (
    IsValidArg<J> extends true ? new (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J) => TNewReturn :
    IsValidArg<I> extends true ? new (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I) => TNewReturn :
    IsValidArg<H> extends true ? new (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H) => TNewReturn :
    IsValidArg<G> extends true ? new (a: A, b: B, c: C, d: D, e: E, f: F, g: G) => TNewReturn :
    IsValidArg<F> extends true ? new (a: A, b: B, c: C, d: D, e: E, f: F) => TNewReturn :
    IsValidArg<E> extends true ? new (a: A, b: B, c: C, d: D, e: E) => TNewReturn :
    IsValidArg<D> extends true ? new (a: A, b: B, c: C, d: D) => TNewReturn :
    IsValidArg<C> extends true ? new (a: A, b: B, c: C) => TNewReturn :
    IsValidArg<B> extends true ? new (a: A, b: B) => TNewReturn :
    IsValidArg<A> extends true ? new (a: A) => TNewReturn :
    new () => TNewReturn
) : never

//Usage
class A {
    public aCall(a: any, payload: string) { }
    public bCall(a: any, payload: number) { }
}

// Extending a class
class C extends createNewClass(A) { }

new C().aCall('xxx')

//For interfaces we can just use the type
interface IC extends RemoveFirstArg<typeof A> { }

Примечание Причина множества похожих строк в том, что нам нужно переназначить каждый конструктор / функцию с определенным количеством аргументов. Вышеприведенная реализация работает для 10 аргументов, которых должно быть достаточно, но можно добавить еще.

Редактировать

Поскольку на первоначальный вопрос был дан ответ, машинопись улучшила возможное решение этой проблемы. С добавлением кортежей в параметрах отдыха и выражениях нам теперь не нужно иметь все перегрузки для RemoveArg и ReplaceInstanceType:

type IsValidArg<T> = T extends object ? keyof T extends never ? false : true : true;

type ArgumentTypes<T> = T extends (... args: infer U ) => any ? U: never;
type ReplaceInstanceType<T, TNewInstance> = T extends new (...args: any[])=> infer R ? new (...a: ArgumentTypes<T>) => TNewInstance : never;

type ArgumentTypesSkipOne<T> = T extends (a: any, ... args: infer U ) => any ? U: never;
type RemoveArg<T> = T extends (a: any, ...args: any[])=> infer R ? (...a: ArgumentTypesSkipOne<T>) => R : T;

type RemoveFirstArg<TCtor extends new (... args: any[]) => any > = ReplaceInstanceType<TCtor,  { [P in keyof InstanceType<TCtor>]: RemoveArg<InstanceType<TCtor>[P]> }>

function createNewClass<TCtor extends new (... args: any[]) => any >(aCtor: TCtor) : RemoveFirstArg<TCtor>{
    // load all of the A method and remove first params
    return null as any;
}

Это не только короче, но и решает ряд проблем

  • Необязательные параметры остаются необязательными
  • Имена аргументов сохраняются
  • Работает для любого количества аргументов
...