【问题标题】:Typescript new class by evaluate params通过评估参数键入新类
【发布时间】:2023-03-12 07:35:01
【问题描述】:
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) {}

我可以有一个函数(或方法上的装饰器)来评估传入类并生成新类并删除所有第一个参数,以便我可以使用新类进行扩展,或者它不能这样做

【问题讨论】:

  • 我想你想要一个通用版本?
  • 是的,所以我可以替换任何传入的类
  • 只是对类型感兴趣?或者你想要创建类 B 的实现吗?因为在实现中可能会有不同的选择..
  • 仅限类型。我只想让我的接口 C 来扩展按摩的 B 类
  • 实际上我会在声明文件(d.ts)中使用它

标签: class typescript decorator evaluate


【解决方案1】:

3.0 答案见下文

您可以使用与此answer 类似的方法。您将需要替换构造函数的返回类型,并使用映射类型来创建省略第一个参数的新函数:

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 个参数,应该足够了,但可以添加更多。

编辑

自从回答了最初的问题以来,打字稿已经改进了这个问题的可能解决方案。随着Tuples in rest parameters and spread expressions 的添加,我们现在不需要为RemoveArgReplaceInstanceType 提供所有重载:

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;
}

这不仅更短,而且解决了许多问题

  • 可选参数仍然是可选的
  • 参数名称被保留
  • 适用于任意数量的参数

【讨论】:

【解决方案2】:

如果出于某种原因,您关心实际尝试实现这个东西,您可以执行以下操作。请注意,我只会用两个参数替换方法。如果您需要执行所有方法,则键入必须更精细,如@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);

在使用此类类玩游戏时可能有各种注意事项,因此请注意您真正了解您的用例以及在面对不符合它的行为时会发生什么。

希望对您有所帮助。祝你好运!

【讨论】:

  • 然后你可以把protoVal.length === 2改成protoVal.length &lt;= 2,条件类型从T[K] extends (a: any, payload: infer P) =&gt; infer R ? (payload: P) =&gt; R : T[K];改成T[K] extends (a: any, payload: infer P) =&gt; infer R ? (payload: P) =&gt; R : T[K] extends (a: any) =&gt; infer R ? () =&gt; R : T[K];。 }
  • 感谢您的帮助
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-06
  • 2020-04-15
  • 2020-04-19
  • 2015-12-01
相关资源
最近更新 更多