2

I'm new to typescript and I'm trying to define a class properly that turns an object of a certain type to a class with the same properties. However, I can't properly define the keys on this class since they are generic:

interface RawAnswer {
  __typename: string;
  id: string;
  meta: Record<string, unknown>;
}

class TrackedAnswer<T> {
  // This doesn't work:
  // [K in keyof T]: T<K>

  constructor(obj: T) {
    Object.entries(obj).forEach(([key, value]) => {
      // this is just an example, I'm aware that this is useless
      this[key] = value;
    });
  }
}

const raw: RawAnswer = { __typename: "StringAnswer", id: "123", meta: {} };
const answer = new TrackedAnswer(raw);

I'd like the TrackedAnswer class to have the same index signature as RawAnswer in this case. Can someone help me out?

Jonas Metzener
  • 133
  • 1
  • 8

2 Answers2

0

I've found my self needing this pattern multiple times, this is how is solved it:

class TrackedAnswer<T extends TrackedAnswer<T>> {
  constructor(e: T) {
    Object.keys(e).forEach((k) => {
      // @ts-ignore
      this[k] = e[k];
    });
  }
}

class Answer extends TrackedAnswer<Answer> {
  public __typename!: string;
  public id!: string;
  public meta!: Record<string, unknown>;
}

const raw: Answer = { __typename: "StringAnswer", id: "123", meta: {} };
const answer = new Answer(raw);

playground

It's also included in my typescript helper library if you want to use it thrugh npm: https://github.com/Olian04/typescript-helpers/blob/master/src/lib/record.ts

Olian04
  • 6,480
  • 2
  • 27
  • 54
0

You can get auto-completion and have the class satisfy the interface with a static factory and cast:

interface RawAnswer {
  __typename: string;
  id: string;
  meta: Record<string, unknown>;
}

class TrackedAnswer<T> {

  constructor(obj: T) {
    Object.entries(obj).forEach(([key, value]) => {
      (this as any)[key] = value;
    });
  }
}

function trackedAnswerFactory<T>(obj: T) {
    return new TrackedAnswer(obj) as TrackedAnswer<T> & T;
}

const raw: RawAnswer = { __typename: "StringAnswer", id: "123", meta: {} };
const answer = trackedAnswerFactory(raw);

const iOnlyAcceptRawAnswer = (a: RawAnswer) => console.log("This is a RawAnswer");

iOnlyAcceptRawAnswer(answer);
hendalst
  • 2,957
  • 1
  • 24
  • 25