[Unity] 父类为泛型类且包含返回泛型的静态函数时,子类可以覆盖声明该函数以获得准确返回类型
相关issue:#216
class CB<T> {
public static GetInstance (): any // 因为#216原因不得不写成any,在C#侧它是T
}
class CBD extends CB$1<CBD> {
public static GetInstance (): CBD
}
这样处理起来感觉超麻烦的啊, 假定它是这样的嵌套类型呢:
class ClassB<T> {
public static mthod1(): System.Collections.Generic.List$1<T>;
public static mthod2(): System.Array$1<System.Array$1<T>>;
}
之前到时考虑过使用这样的模式进行声明, 但是因为不能支持getter和setter, 所以暂时搁置了:
type ConstructorType<T> = { new(...args: any[]): T };
class ClassA<T> {
public static GetInstance<T extends ClassA<any>>(this: ConstructorType<T>): T;
public static GetGenericTypeList<T extends ClassA<any>>(this: ConstructorType<T>): CS.System.Collections.Generic.List$1<T>;
}
class ClassB extends ClassA<ClassB>{
}
class ClassC extends ClassB {
}
使用是这样的:
let objB = ClassB.GetInstance(); //objB is ClassB
let objC = ClassC.GetInstance(); //objB is ClassC
let listB = ClassB.GetGenericTypeList(); //listB is System.Collections.Generic.List$1<ClassB>
let listC = ClassC.GetGenericTypeList(); //listC is System.Collections.Generic.List$1<ClassC>
这样处理起来感觉超麻烦的啊, 假定它是这样的嵌套类型呢:
class ClassB<T> { public static mthod1(): System.Collections.Generic.List$1<T>; public static mthod2(): System.Array$1<System.Array$1<T>>; }
也能做到吧。
这个方案只是在“有子类继承它时,让子类的类型声明可用”,没有解决直接用这个父类的问题,如果是继承的时候,你这个case应该也能跑的?
这是内部一个项目组的办法。
有一个可能比较完美的解决方案。但是与之前用法不同的。
比如对于Singleton<T>,换成如下实现方式(当伪代码看):
function Singleton<T>(typeT: T) {
if (!this == global) { throw new Error('Singleton is a genericTypeDefinition and should call as Singleton(System.Type) first'); }
return class Singleton$T extends System.Object {
public static getInstance(): T;
}
}
var SingletonGameObject = Singleton<GameObject>(puer.$typeof(UnityEngine.GameObject));
var singleGO = new SingletonGameObject();
这样SingletonGameObject的静态方法就有正常的类型,这个泛型类的用法(通过一次函数调用而不是puer.$generic得到具体泛型类)也比较符合新手的直觉。有个叫Jint的库是这么做的(不过它们对于非泛型类也要通过函数调用才能得到类,很奇怪,而且他们也没支持DTS)
但目前不太可能粗暴地替换原来的办法了,如果可以以扩展包的方式提供也不错。
尝试了一下, 是可行的:
declare global {
const __static_generic_arguments_builder__: unique symbol;
class ClassA<T> {
public self: T;
public field1: string;
public static get Instance(): any;
public static GetInstance(): any;
protected static [__static_generic_arguments_builder__]<T>(): {
//实现构造函数
new(...args: any): ClassA<T>;
//...
//....
//实现静态成员
readonly Instance: T;
GetInstance(): T;
}
}
function ClassA_static_generic_arguments_builder<T>(): {
//实现构造函数
new(...args: any): ClassA<T>;
//...
//....
//实现静态成员
readonly Instance: T;
GetInstance(): T;
};
class ClassB extends ClassA_static_generic_arguments_builder<ClassB>() {
}
class ClassC extends ClassA[__static_generic_arguments_builder__]<ClassC>() {
}
}
let objB = ClassB.GetInstance(); //type is ClassB
let objBself = objB.self; //type is ClassB
let objC = ClassC.GetInstance(); //type is ClassC
let objCself = objC.self; //type is ClassC
用unique symbol声明更好, 避免污染类型环境
如果把使用了泛型参数的静态成员移除的话, 可以直接简写成:
class ClassA<T> {
public self: T;
public field1: string;
protected static [__static_generic_arguments_builder__]<T>(): typeof ClassA<T> & {
//实现使用了泛型参数的静态成员
readonly Instance: T;
GetInstance(): T;
}
}
现在dts里genericDefinition的声明是类似List$1<T>这样的,List<T>还可以用
现在dts里genericDefinition的声明是类似
List$1<T>这样的,List<T>还可以用
List<T>应该不行, 首先是会污染类型环境, 开发者可能将其作为实际的值使用. 其次无法区分C#中相同类名的类型:
class ClassA<T>{ }
class ClassA<T1, T2>{ }
尝试了一下, 是可行的:
declare global { const __static_generic_arguments_builder__: unique symbol; class ClassA<T> { public self: T; public field1: string; public static get Instance(): any; public static GetInstance(): any; protected static [__static_generic_arguments_builder__]<T>(): { //实现构造函数 new(...args: any): ClassA<T>; //... //.... //实现静态成员 readonly Instance: T; GetInstance(): T; } } function ClassA_static_generic_arguments_builder<T>(): { //实现构造函数 new(...args: any): ClassA<T>; //... //.... //实现静态成员 readonly Instance: T; GetInstance(): T; }; class ClassB extends ClassA_static_generic_arguments_builder<ClassB>() { } class ClassC extends ClassA[__static_generic_arguments_builder__]<ClassC>() { } } let objB = ClassB.GetInstance(); //type is ClassB let objBself = objB.self; //type is ClassB let objC = ClassC.GetInstance(); //type is ClassC let objCself = objC.self; //type is ClassC
试了多种组合方式, 好像都需要手动声明一遍构造函数, 但最终发现只需要交换一下它们的位置就能调用到想要的方法(😂):
const __static_generic_arguments_builder__: unique symbol;
//合并两个类型,key相同的类型由第二个覆盖第一个
type Merge<First, Second> = {
[P in keyof First | keyof Second]: P extends keyof Second ? Second[P] : P extends keyof First ? First[P] : never
}
class ClassA<T> {
public self: T;
public field1: string;
public static get Instance(): any;
public static GetInstance(): any;
protected static [__static_generic_arguments_builder__]<T>(): {
//实现泛型参数静态成员
readonly Instance: T;
GetInstance(): T;
} & typeof ClassA<T>;
/* protected static [__static_generic_arguments_builder__]<T>(): Merge<typeof ClassA<T>, {
//实现泛型参数静态成员
readonly Instance: T;
GetInstance(): T;
}> & {
//实现构造函数
new(...args: any): ClassA<T>;
//...
//....
}; */
}