Description
TypeScript Version:
2.8 and 2.9
Search Terms:
Cannot create declaration files from static factory class returned from a function.
Code
(pay special attention to class SomeClass
)
namespace Test {
export interface IType<TInstance = object> {
new(...args: any[]): TInstance;
}
export function ClassFactory<TBaseClass extends IType, TClass extends any, TFactory extends any, TNamespace extends object>
(namespace: TNamespace, base: { $__type: TBaseClass }, getType: (base: TBaseClass) => [TClass, TFactory], exportName?: keyof TNamespace, addMemberTypeInfo = true)
: TClass & TFactory & { $__type: TClass } {
return null;
}
export function FactoryBase<TBaseFactory extends IType>(baseFactoryType: TBaseFactory) {
return class FactoryBase {
static 'new'?(...args: any[]): any;
static init?(o: object, isnew: boolean, ...args: any[]): void;
};
}
function TestDec<T>(c:T):T { return null; }
export namespace Loader {
export var _SomeClass = ClassFactory(Loader, void 0,
(base) => {
@TestDec
class SomeClass {
static readonly SomeClassFactory = class Factory extends FactoryBase(null) {
static 'new'(): SomeClass { return null; }
static init(o: SomeClass, isnew: boolean) {
}
};
}
return [SomeClass, SomeClass["SomeClassFactory"]];
}
);
}
}
The code compiles and runs OK, no problems. The issue is that when I enable the flag to export declarations as well I get this error:
Scripts/testscript.ts(20,20): error TS4025: Exported variable '_SomeClass' has or is using private name 'SomeClass'.
Expected behavior:
It is expected that the d.ts
files are generated without giving me issues. Without declarations it compiles fine. The partial workaround (see below) makes the error pointless.
Actual behavior:
Error message (see above).
Other Details:
This works:
namespace Test {
export interface IType<TInstance = object> {
new(...args: any[]): TInstance;
}
export function ClassFactory<TBaseClass extends IType, TClass extends any, TFactory extends any, TNamespace extends object>
(namespace: TNamespace, base: { $__type: TBaseClass }, getType: (base: TBaseClass) => [TClass, TFactory], exportName?: keyof TNamespace, addMemberTypeInfo = true)
: TClass & TFactory & { $__type: TClass } {
return null;
}
export function FactoryBase<TBaseFactory extends IType>(baseFactoryType: TBaseFactory) {
return class FactoryBase {
static 'new'?(...args: any[]): any;
static init?(o: object, isnew: boolean, ...args: any[]): void;
};
}
function TestDec<T>(c:T):T { return null; }
export namespace Loader {
export var _SomeClass = ClassFactory(Loader, void 0,
(base) => {
// @TestDec <- CANNOT ADD DECORATORS, CANNOT SUPPORT ANGULAR, ETC., WITHOUT UGLY WORKAROUNDS
var c = class SomeClass {
static readonly SomeClassFactory = class Factory extends FactoryBase(null) {
static 'new'(): SomeClass { return null; }
static init(o: SomeClass, isnew: boolean) {
}
};
};
return [c, c["SomeClassFactory"]];
}
);
}
}
Notice however that I lost the @TestDec
decorator doing this above (YES, I know a decorator is just a function). Not a very pleasant experience for Angular users.
Now, the following code I change SomeClassFactory
to protected
and it works ONLY if declarations are turned off; HOWEVER, when declarations are turned are on, this also fails with error: testscript.ts(22,20): error TS4094: Property 'SomeClassFactory' of exported class expression may not be private or protected.
It's protected for a reason - I do not want people to see it in the code completion list on the exported property.
namespace Test {
export interface IType<TInstance = object> {
new(...args: any[]): TInstance;
}
export function ClassFactory<TBaseClass extends IType, TClass extends any, TFactory extends any, TNamespace extends object>
(namespace: TNamespace, base: { $__type: TBaseClass }, getType: (base: TBaseClass) => [TClass, TFactory], exportName?: keyof TNamespace, addMemberTypeInfo = true)
: TClass & TFactory & { $__type: TClass } {
return null;
}
export function FactoryBase<TBaseFactory extends IType>(baseFactoryType: TBaseFactory) {
return class FactoryBase {
static 'new'?(...args: any[]): any;
static init?(o: object, isnew: boolean, ...args: any[]): void;
};
}
function TestDec<T>(c:T):T { return null; }
export namespace Loader {
export var _SomeClass = ClassFactory(Loader, void 0,
(base) => {
// @TestDec <- CANNOT ADD DECORATORS, CANNOT SUPPORT ANGULAR, ETC., WITHOUT UGLY WORKAROUNDS
var c = class SomeClass {
protected static readonly SomeClassFactory = class Factory extends FactoryBase(null) {
static 'new'(): SomeClass { return null; }
static init(o: SomeClass, isnew: boolean) {
}
};
};
return [c, c["SomeClassFactory"]];
}
);
}
}
It's very frustrating to have it compile and run PERFECTLY, but then fail to generate declaration files. I keep getting blocked at every turn and it's driving me nuts lol. It's days like this I just want to switch back to pure JavaScript and save time (except then I realize all the bugs I'll probably get which would offset any time saved coding it lol ... :*( ).