4

A common structure I use in TS is taking a plain JSON object definition and turning it into a class at runtime. For example:

export type LessonDef = {
  id: string
  title: string
  slug: string
  shortdesc: string
  explanation: string
  exercises: {
    from: string
    message: string
    translation: string
    hint?: string
    feedback?: { [key: string]: string }
  }[]
}

export class Lesson {
  constructor(readonly def: LessonDef) {
    Object.assign(this, def)
  }

  // Additional methods go here
}

The problem is that the type system doesn't understand the result of the Object.assign. How can I tell TypeScript that Lesson extends the type of LessonDef?

7
  • Try to make def argument as a public, like here. It will create def property in this with all expected properties. TS does not track mutations, hence compiler is unable to figure out that this has been changed Commented Dec 14, 2021 at 10:15
  • Alas, I do specifically want all of the properties to be on the top-level object, rather than accessing them through the def. Commented Dec 14, 2021 at 10:26
  • 1
    ok, you need to declare LessonDef as an interface instead of type. See this example Commented Dec 14, 2021 at 10:29
  • Oh wow, I had no idea you could have overlapping names for interfaces/classes like that! Thanks, with a slight modification that works perfectly. If you want to write it up as an answer I'll accept. Commented Dec 14, 2021 at 10:39
  • To be honest your question is a duplicate. It is been answered by @Titian Cernicova-Dragomir but I'm unable to find the link but I remember solution, so I'm not feeling comfortable answering on this question Commented Dec 14, 2021 at 10:42

1 Answer 1

0

Please see related answer

You can merge your class declaration and interface.

Consider this example:

interface Lesson {
    id: string
    title: string
    slug: string
    shortdesc: string
    explanation: string
    exercises: {
        from: string
        message: string
        translation: string
        hint?: string
        feedback?: { [key: string]: string }
    }[]
}

declare let x: Lesson;

class Lesson {
    constructor(def: Lesson) {
        Object.assign(this, def);
    }
}

const result = new Lesson(x)

result.exercises // ok

Playground

@Cerberus thank you for pointing out my mistake regarding using {... def} instead of def

However, there is a drawback, you can refer to this.exercises before Object.assign

class Lesson {
    constructor(def: Lesson) {
        this.exercises //ok, <----- DRAWBACK!
        Object.assign(this, def);
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

"to avoid creating of extra def property in this" - could you explain this part? AFAIK, Object.assign does not even know the name of passed variables, so no extra property would be created.
@Cerberus you are right, it is my mistake. I have already fixed it. Thank you!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.