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) {}
我是否可以使用函数(或方法上的装饰器)来评估传入的类并生成新类,删除所有第一个参数,以便我可以使用新类进行扩展,或者它只是无法执行此操作
答案 0 :(得分: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后,我们现在不需要为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;
}
这不仅缩短了,而且解决了许多问题
答案 1 :(得分:1)
如果由于某种原因,你关心实际尝试实现这个东西,你可以做类似以下的事情。请注意,我只会用两个参数替换方法。如果你需要做所有的方法,打字必须更加精细,如@ 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);
在使用这样的类玩游戏时可能会有各种各样的注意事项,所以要小心你真正理解你的用例以及面对不符合它的行为时会发生什么。
希望有所帮助。祝你好运!