Skip to content

The property prototype of an abstract constructor type is unavoidably of type any. #56801

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

Closed
miguel-leon opened this issue Dec 15, 2023 · 4 comments

Comments

@miguel-leon
Copy link

miguel-leon commented Dec 15, 2023

πŸ”Ž Search Terms

abstract constructor prototype type any

πŸ•— Version & Regression Information

Latest version as of today.

⏯ Playground Link

https://www.typescriptlang.org/play?#code/MYGwhgzhAECC0G8C+AoFAXAngBwKZwGEB7AOwnQCcBXYdIi6AXmhNwHcAKASiYD44A3Gix5CpctVr0AkiXS4KEXLQCWpJtA6tOPRv1g8AZImjYKROiNwAuONCRCUKuQoBmYYPljEylGnQoAJVwAW1wAExUIxBRoOJZ2bltYIXjTc0scG0EUVGEsuFgAIwkPdB8Jf3oNMBLKMoSdPhynFwp3TzhpEOwiKBUikFwKvykGBFj42tLaRqTBaAB6RegFcwpJuLMLCyzkoTz80QB9AEYNDisiVzguAG0Acm3MvAeAXQElleB6CmV0ZJHfDHABMGm84lGAUez12rw+X2gzh+FD+tFsYBImCB0GOAGZwSNJAFZPJFP81CQYRk4bh3p9lkiSCi0QDoJjsRgCscACyEyHE+jBMKRCLUnZWemIln-QFck4AVnBxRm5QFVQo4pedIRjORv1l7KxKCAA

πŸ’» Code

class A {}

type AConstructor = new() => A;

type AConstructorIntersection = (new() => A) & { prototype: A };

interface AConstructorRemedied {
    new(): A;
    prototype: A;
}


type AAbstractConstructor = abstract new() => A;

interface AImpossibleConstructor {
    abstract new(): A; // error
    prototype: A;
}



type _1 = (typeof A)['prototype']; // correct: A

type _2 = AConstructor['prototype']; // incorrect: any

type _3 = AConstructorIntersection['prototype']; // incorrect: any

type _4 = AConstructorRemedied['prototype']; // correct: A

type _5 = AAbstractConstructor['prototype']; // incorrect: any

πŸ™ Actual behavior

There's no way of making the type of prototype something other than any, not even with an intersection.

πŸ™‚ Expected behavior

A way to make the type of prototype something other than any. It seems that constructor types have a hidden prototype of type any. Shouldn't it be of type unknown so that at least the intersection works? (or wouldn't it be better for it to be the same type as InstanceType directly?)

Additional information about the issue

No response

@fatcerberus
Copy link

Possible (ugly) workaround: Omit<new () => A, "prototype"> & { prototype: A }

@jcalz
Copy link
Contributor

jcalz commented Dec 15, 2023

(@fatcerberus Omit loses the construct signature)

Instead of trying to use abstract new inside an object type, you can do this:

type AAbstractConstructor = abstract new () => A;
interface AAbstractConstructorRemedied extends AAbstractConstructor {
    prototype: A;
}

This will have the prototype property you care about while maintaining the abstractness of the constructor

Playground link

@miguel-leon
Copy link
Author

miguel-leon commented Dec 15, 2023

Thanks for the workaround @jcalz, silly me I didn't think of this while coming up with a minimal reproduction.

The thing is, the type that returns the construct signature is a parametric type.
I just tried

// error
interface WithPrototype<Class extends abstract new (...args: any) => any> extends Class {
    prototype: InstanceType<Class>
}

type AbstractConstructorWithPrototype<Class extends abstract new (...args: any) => any> =
    WithPrototype<abstract new (...args: ConstructorParameters<Class>) => InstanceType<Class>>;

playground

But the interface declaration errors.
Do you think that as a parametric type then it becomes unavoidable? πŸ˜“
Or maybe is there another workaround?

Thanks anyway for the suggestion.

@miguel-leon
Copy link
Author

miguel-leon commented Dec 16, 2023

I messed around with it for a bit and managed to make it work like this:

type AbstractConstructor<Class extends abstract new (...args: any) => any> =
    abstract new (...args: ConstructorParameters<Class>) => InstanceType<Class>;

interface AbstractConstructorWithPrototype<Class extends abstract new (...args: any) => any> extends AbstractConstructor<Class> {
    prototype: InstanceType<Class>
}

playground

The fact that the "hidden" prototype is of type any instead of (at least) unknown, it's a little bit odd, but since there's a work around, I'll close this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants