#Overload a generic class constructor and type a property based on parameters

36 messages · Page 1 of 1 (latest)

opaque tusk
#

Hello I am trying to auto-type a class property based on the constructor's entry parameters. Ive tried something like the following:


type TypeA = { a: string };
type TypeB = { b: boolean };
type TypeC = { c: number };

class MyClass<T extends TypeA | TypeB | TypeC> {
  prop: T;
  foo?: string;
  bar?: number;

  constructor (prop: TypeA, param2?: number)
  constructor (prop: TypeB, param2?: string)
  constructor (prop: TypeC)
  constructor(prop: T, param2?: number | string) {
    this.prop = prop;
    if (typeof param2 === 'number') {
      this.bar = param2;
    } else if (typeof param2 === 'string') {
      this.foo = param2;
    }
  }
}

declare const prop: TypeB;

const instance = new MyClass(prop, 'test');

instance.prop; // should be TypeB!

In this case, the property is being types as the union of types, even though I passed a prop of a specific type in the union. Is there a way to achieve having the property typed depending on the constructor parameter type?

wooden totem
#

Can you console.log(param2) plz ?

tacit ember
#

uhm that's not relevant to the issue

wooden totem
dark cedar
#

Why

wooden totem
#

bc nrly typeof have to work '-'

#

here:

if (typeof param2 === 'number') {
      this.bar = param2;
    } else if (typeof param2 === 'string') {
      this.foo = param2;
    }
#

he want to set this.bar if param2 is an number no ?

dark cedar
#

no that has nothing to do with the question

wooden totem
#

just wait i cancel my auto traduction '-'

tacit ember
#

i'd guess the constructor doesn't do inference over the generic on the class? thonk

wooden totem
#

i suppose that to, just want to check if thats can pose problem

tacit ember
#

it's not even relevant

wooden totem
#

hmm, maybe yes

tacit ember
#

oh it's inferred if you don't use the overloads, since T can be inferred the argument

wooden totem
#

ohhh mb

#

its my fault soo sryy

#

bad translation

opaque tusk
#

The issue is that the overloads seem to be "genericizing" the property type as if TS couldnt resolve the type based on teh appropriate constructor signature used

#

im trying to figure out if there is a way to tell TS that the property type will be T based on the constructor overload used

elder chasmBOT
#
that_guy977#0

Preview:```ts
type TypeA = {a: string}
type TypeB = {b: boolean}
type TypeC = {c: number}

class MyClass<T extends TypeA | TypeB | TypeC> {
prop: T
foo?: string
bar?: number

constructor(prop: Extract<T, TypeA>, param2?: number)
constructor(prop: Extract<T, TypeB>, param2?: stri
...```

tacit ember
#

this works, but idk if i really like this kind of pattern

proud sentinel
#

How about something using discriminated union of tuples for the arguments?

elder chasmBOT
#
mishall8399#0

Preview:```ts
type TypeA = {a: string}
type TypeB = {b: boolean}
type TypeC = {c: number}

type CtorArgs =
| [TypeA, number?]
| [TypeB, string?]
| [TypeC]

class MyClass<T extends TypeA | TypeB | TypeC> {
prop: T
foo?: string
bar?: number

constructor(...args: Extract<CtorArgs, {0: T}>
...```

proud sentinel
#

No overloads

opaque tusk
opaque tusk
#

Thank you both! @proud sentinel that is a new concept/syntax for me, looks like a cleaner way to define overloads 😄

#

!resolved

#

Ahh, one caveat @proud sentinel , how would I annotate the different constructors ?

proud sentinel
opaque tusk
#

With constructors you can annotate them so that the description shows in the intellisense , like this:

/**
 * Constructor A
 * - This constructor is specific to TypeA, this is a nice description
 * - More info here
 */
constructor(prop: Extract<T, TypeA>, param2?: number)
#

the param name isnt that critical but it would also be nice to name semantically

proud sentinel
tacit ember