#Mapped keyof types to class

89 messages · Page 1 of 1 (latest)

agile cape
#

how do I write a version of this code that actually works

interface ID {
  id: string;
}

export class BaseClientEntity<T extends ID> {
  [key: keyof T]: T[key];
  constructor(base: T) {
    for (const key in base) {
      if (base.hasOwnProperty(key)) {
        (this as any)[key] = base[key];
      }
    }
  }
}
pseudo sable
#

don't use loops / reduction functions to copy / transform key/values from an object

#

because it's not that easy to type

#

because you'd need to declare an empty object first
but then you wouldn't be able to assign keys/values to an empty object

#

if you make it partial, then you still need to remove the partial before retning it, etc.

#

so it's not that easy

#

I recommend using Object.entries(), Array.prototype.map(), and Object.fromEntries()

#

so that's for the assignment part

#

about this part, [key: keyof T]: T[key];, you can just use implements to implement an interface

#

just like in regular OOP

ember fiberBOT
#
Ascor8522#7606

Preview:```ts
interface ID {
id: string
}

export class BaseClientEntity implements ID {
id!: string

constructor(baseClientEntity: {id: string}) {
Object.assign(this, baseClientEntity)
}
}```

pseudo sable
#

but that means you'll have to repeat some code

#

@agile cape

#

one thing you could do, is use an abstract class that you will inherit from

#

that way, you'll have a separate ID type, while not having to repeat those fields

ember fiberBOT
#
Ascor8522#7606

Preview:```ts
abstract class ID {
id!: string

constructor(id: {id: string}) {
Object.assign(this, id)
}
}

export class BaseClientEntity extends ID {
constructor(baseClientEntity: {id: string}) {
super(baseClientEntity)
Object.assign(this, baseClientEntit
...```

pseudo sable
#

like so

agile cape
#

ok

#

lemme try this

agile cape
#

but you need to repeat everything from the interface into the every version of it

#

and there's a lot of it

#

a decent chunk of it is generated code

#

so it's not always BaseClientEntity extends ID

pseudo sable
#

hence my 2nd solution

#

but yeah, there is no easy fix

#

maybe if you provide more context I'm sure we can work out a better solution

#

but yeah, mixing raw types and classes in TS is always a bit of a pain

agile cape
#

I basically have a bunch of plain interfaces generated

#

I want to "augment" them into classes

pseudo sable
#

and combining types/interfaces together is easier and provides as much safety

#

just create classes in the first place

agile cape
#

not possible

pseudo sable
#

no reason to use interfaces as DTO in TS, you can use clases to achieve the same thing

agile cape
#

again, a decent chunk of it is generated by a code generation tool

pseudo sable
#

then don't use classes

#

but raw objects

agile cape
#

fair

#

but less ergonomics

#

I guess I can redesign my APIs

#

I feel like a main issue with TypeScript that many people haven't noticed is that typescript usually maintains a good separation of values and types

#

except for classes

#

they're values

#

but also types

pseudo sable
#

typescript usually maintains a good separation of values and types
don't really see what you mean by that

#

except for classes
they're values
but also types
the fact an object is a class doesn't matter at all in TS
only it's "general shape" matters

#

structural typing in a nutshell

agile cape
pseudo sable
#

right, there is a difference between a JS object you can use (value)

#

and a TS type that will be erased and that can only be used to check your code

#

however, there are ways to create types from values

#

like typeof

agile cape
#

yeah

pseudo sable
#
const foo = 123;
type Foo = typeof foo;
//   ^?
agile cape
#

do types live in the same namespace as values

pseudo sable
#

yes, no reason to keep them separate

#

they are related after all

agile cape
#

would something like

const Foo = 123;
type Foo = typeof Foo;

work

pseudo sable
#

no, can't use the same name

#

and please, variables should be camelCase in JS/TS

agile cape
#

I know that

agile cape
#

I was just asking this as an example to ask if it's possible for the types and values to have a same name

#

if you think about it, that's what classes are

#

they declare a type with the same name as the value

pseudo sable
#

right, you can merge a variable and a type together

#

but would not recommend

agile cape
#

yeah

#

sounds painful

pseudo sable
#

if you think about it, that's what classes are
they declare a type with the same name as the value
right, the same name can be used both to create new instances and to refer to the type of an instance

#

and to refer to the type of the class itself, have to use typeof

agile cape
#

ok, so from what I see here I shouldn't try to augment interfaces to classes

#

but just use classes outright

#

or interfaces

pseudo sable
#

yes, either of those

#

but combining them will be kind of a pain

agile cape
#

I'm just basing my designs off how discord.js and similar libraries are built

#

they cache heavily

pseudo sable
#

especially if you want to use classes instances for their class "properties" (instanceof + methods + inheritance)

agile cape
#

and it's generally a mess

#

I just want a way to implement an interface without repeating myself too much

pseudo sable
#

right, it becomes more about keeping track of where the data is, and in what state it is, if it needs to be re-validated or not, etc.

agile cape
#

it is possible

#

again, type merging the interface with the class

#

so can't really be checked properly by the compiler

#

but still, much of what is here is for internal library code

agile cape