Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to specify return type of class extends T implements IFooable #15001

Open
AllNamesRTaken opened this issue Apr 4, 2017 · 6 comments
Open
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript

Comments

@AllNamesRTaken
Copy link

TypeScript Version: 2.2.1 / nightly (2.2.0-dev.201xxxxx)

Code

// A *self-contained* demonstration of the problem follows...
export type Constructor<T> = new (...args: any[]) => T;
export interface IFooable { FooProp: string; }
export function FooableMixin<T extends Constructor<{}>>(Base: T) {
    return class extends Base implements IFooable {
        FooProp: string;
        constructor(...args: any[]) {
            super(...args);
        }
    }
}
export class BaseBar { BarProp: string = "baz"; }
export class FooableBar extends FooableMixin ( BaseBar ) {}
let foobar = new FooableBar();
foobar.FooProp = foobar.BarProp; 
/* This is ok since FooableBar extends an Anonymous class inheriting from BaseBar and implementing IFooable */

If I turn on "declaration: true" in tsconfig.json this constuct breaks since the Anonymous class counts as private and not exported type (error TS4060) which is kind of natural since it is Anonymous.

What we need is a way to describe Classes as return types.

Expected behavior:

A d.ts file with a type declaration for the Mixin function Fooable that defines a Anonymous Class return type in the style of:

declare function Fooable(T extends Constructor<{}>): (class extends T implements IFooable);

Actual behavior:

TS4060 error when compiling with the "declaration" flag.
If casting the Anonymous Class return value to or T, the interface is lost.

@AllNamesRTaken
Copy link
Author

Duplicate of #14075, #14597
Solution for now seem to be to:

  1. add return type : T & ICtor<IFooable> to the Mixin function to get rid of TS4060
  2. declare the union type as const before using it to extend.

This makes mixins much more cumbersome to use.

export const _FooableBar = FooableMixin(BaseBar);
export class FooableBar extends _FooableBar {}

@aluanhaddad
Copy link
Contributor

I wonder if this would be resolvable with #6606

@mhegazy
Copy link
Contributor

mhegazy commented Apr 27, 2017

class expressions are not allowed in type positions. the fix would be to allow it, and then emit it in the declaration emitter.

@mhegazy mhegazy added Suggestion An idea for TypeScript In Discussion Not yet reached consensus labels Apr 27, 2017
@trusktr
Copy link
Contributor

trusktr commented Oct 14, 2019

A partial workaround is to declare the return type using types pre-defined outside of the function, but non-class types can not have protected members, and so inheriting protected members from the mixin class isn't possible (without manual casting).

@trusktr
Copy link
Contributor

trusktr commented Oct 14, 2019

Nice!

Looks like a recent release (3.6.4?) has fixes that allow my lib to specify "types": "./src/index.ts" in package.json (pointing directly to a regular TS file, not a .d.ts declaration file!) without any issues.

This allows me to set "declaration": false in tsconfig.json, and rely on the actual code for type checking, which allows the return types to be inferred!

It does mean that I need to ship the source TS files instead of just declaration files.

@nuthinking
Copy link

I too have problems with declarations and mixins. Shipping the source is an option but I would prefer otherwise. Any idea on how to do that?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

5 participants