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

No way to get generic type of an abstract class inside a factory function #35576

Closed
xaviergonz opened this issue Dec 9, 2019 · 4 comments · Fixed by #36392
Closed

No way to get generic type of an abstract class inside a factory function #35576

xaviergonz opened this issue Dec 9, 2019 · 4 comments · Fixed by #36392
Labels
Bug A bug in TypeScript
Milestone

Comments

@xaviergonz
Copy link

TypeScript Version: 3.7.3

Search Terms: abstract class, generic, infer

Code

type InstanceViaNew<T> = T extends { new (...args: never[]): infer R } ? R : never
type InstanceViaProto<T> = T extends { prototype: infer R } ? R : never

function classFactory1<T>() {
  class CS {
    x!: T
  }

  return CS
}

const CSS = classFactory1<string>()
type CS1 = InstanceViaNew<typeof CSS>["x"] // ok, string
type CS2 = InstanceViaProto<typeof CSS>["x"] // not ok, any :(

function classFactory2<T>() {
  abstract class ACS {
    x!: T
  }

  return ACS
}

const ACS = classFactory2<string>()

type AC1 = InstanceViaNew<typeof ACS>["x"] // not ok, never (because abstract classes have no new) :(
type AC2 = InstanceViaProto<typeof ACS>["x"] // not ok, any :(

Expected behavior:

There should be a way to make abstract classes keep the generic information and use it.

Actual behavior:

There's no way to make abstract classes keep the generic information and use it (unless they are type-casted to non abstract classes by adding to them a fake constructor).

Related Issues: #26829

@xaviergonz
Copy link
Author

I have to add that if in classFactory2 the return statement is changed to

return ACS as typeof ACS & {prototype: typeof ACS }

Then oddly enough the "InstanceViaNew" works, yet "InstanceViaProto" still doesn't (I'd have expected the opposite)

@RyanCavanaugh
Copy link
Member

InstanceViaProto ought to work here; we're probably failing to apply the type mapper in the right place?

@xaviergonz
Copy link
Author

@rbuckton I think the PR will certainly make InstanceViaNew work (nice!), but I guess it wouldn't fix the InstanceViaProto case?

@xaviergonz
Copy link
Author

xaviergonz commented Jan 24, 2020

Also I found out that if you do something like this:

function classFactory2<T>() {
  abstract class ACS {
    constructor(_y: number) {}
    x!: T
  }

  return ACS
}

const ACS = classFactory2<string>()

// this doesn't work because the abstract has no constructor (expected I guess)
type AC3 = InstanceType<typeof ACS>["x"] // Type 'typeof ACS' does not satisfy the constraint 'new (...args: any) => any'
type AC4 = ConstructorParameters<typeof ACS> // Type 'typeof ACS' does not satisfy the constraint 'new (...args: any) => any'.

// but the intersection of the type with anything makes it somehow work... ?
// as if the intersection made the class get "unabstracted"
type AC1 = InstanceType<(typeof ACS) & {whatever: any}>["x"] // weirdly enough this gives string
type AC2 = ConstructorParameters<(typeof ACS) & {whatever: any}> // weirdly enough this gives [number]

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

Successfully merging a pull request may close this issue.

2 participants