TypeScript Version: 3.3.3333
Code
_src/test.ts:_
export function test () {
return class {
private privateMember () {
}
};
}
_tsconfig.json:_
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"declaration": true
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules"
]
}
Expected behavior:
No compilation error.
Actual behavior:
Compiler prints error:
$ tsc
src/test.ts(1,17): error TS4094: Property 'privateMember' of exported class expression may not be private or protected.
Playground Link: not possible to provide.
Workaround
Declare the return type explicitly:
export function test (): new() => Object {
return class {
private privateMember () {
}
};
}
The error message didn't accidently write itself... exported anonymous classes can't have private or protected members if declaration emit is enabled, because there's no way to represent that in a .d.ts file.
This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.
It is possible to use private in declaration files. In my project, for example, I found a lot of them in _node_modules_ folder:
_tslint/lib/formatters/checkstyleFormatter.d.ts:_
export declare class Formatter extends AbstractFormatter {
static metadata: IFormatterMetadata;
format(failures: RuleFailure[]): string;
private escapeXml; <- HERE
}
I think this is an issue, too.
Perhaps we should extend d.ts to handle this case then. Ideally the declarations file and the ts source file would be 100% compatible.
It is currently not possible to generate declaration files when using a class mixin pattern, even if that pattern would otherwise compile and type-check just fine. It would be nice to have support for this 🙏
Shouldn't this issue be open?
In particular, it'd be nice to be able to do something like
function foo<T>(arg: T): SomeType<T> { ... }
type Bar = {num: number}
type FooBar = ReturnType<typeof foo<Bar>>
I get what you're saying, that foo<Bar>
is already a type, so typeof doesn't make sense. But I think you get what's missing.
This is valid:
type FooBar = ReturnType<typeof foo>
But in that example, TypeScript sets the type of T
to unknown
, so the type of FooBar is SomeType<T>
.
Basically, it just intuitively seems like there should be some way to pass a generic arg there, but we can't, and TypeScript automatically sticks unknown
into it. Example on playground shows automatic unknown type for T
.
If a function foo
has a generic param, then the typeof
operator could perhaps return a generic type, and we could write:
type FooBar = ReturnType<(typeof foo)<Bar>>
That would be a breaking change to TypeScript though.
I re-opened this at https://github.com/microsoft/TypeScript/issues/36060
Interesting fix:
Moving the mixin to its own file and doing a default
export works:
type Constructor<T = {}> = new (...args: any[]) => T
export default <T extends Constructor>(base: T) => class SomeClass extends base {
protected something: boolean = false
}
While a named export throws the error:
type Constructor<T = {}> = new (...args: any[]) => T
export const mixin = <T extends Constructor>(base: T) => class SomeClass extends base {
protected something: boolean = false
}
Most helpful comment
Perhaps we should extend d.ts to handle this case then. Ideally the declarations file and the ts source file would be 100% compatible.