Typescript: False error TS4094: ... exported class expression may not be private or protected.

Created on 13 Mar 2019  路  12Comments  路  Source: microsoft/TypeScript

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 () {
        }
    };
}
Working as Intended

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.

All 12 comments

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.

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
}
Was this page helpful?
0 / 5 - 0 ratings