Skip to content

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

Closed
@miguel-leon

Description

@miguel-leon

πŸ”Ž 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

Activity

fatcerberus

fatcerberus commented on Dec 15, 2023

@fatcerberus

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

jcalz

jcalz commented on Dec 15, 2023

@jcalz
Contributor

(@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

miguel-leon commented on Dec 15, 2023

@miguel-leon
Author

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

miguel-leon commented on Dec 16, 2023

@miguel-leon
Author

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @jcalz@fatcerberus@miguel-leon

        Issue actions

          The property prototype of an abstract constructor type is unavoidably of type any. Β· Issue #56801 Β· microsoft/TypeScript