2

If I have a TypeScript interface like below:

interface myInterface {
    prop1: string;
    prop2: string;
    prop3: number;
    prop4: boolean;
    ..
    ..
    ..
    prop30: string
}

I want to create a Class that implements myInterface and I only know the verbose way to do so like below:

class MyClass implements myInterface {
    prop1;
    prop2;
    prop3;
    prop4;
    ..
    ..
    prop30;

    constructor(data: myInterface) {
        this.prop1 = data.prop1;
        this.prop2 = data.prop2;
        this.prop3 = data.prop3;
        this.prop4 = data.prop4;
        ..
        ..
        this.prop30 = data.prop30;
    }
}

Is there any way I can make this syntax shorter or any better way to implement such Class from interface?

4
  • 1
    Is this just a DTO? If so, do you really need a class for it? Commented Sep 22, 2021 at 21:26
  • Precisely what @VLAZ said - var x: myInterface = { prop1: "hello", prop2: "world", prop3: 77, ...} is a valid instance of the interface. You don't need a class here (just in case you weren't aware). Commented Sep 23, 2021 at 3:31
  • I agree you @VLAZ that it is a DTO but I also want some methods to change certain property values based on other property changes (e.g prop1 value changes when props2 and prop3 changes). Hope it makes sense. This is very case specific ask. Commented Sep 23, 2021 at 18:58
  • Then it's not a DTO if it has any logic, not just properties. Commented Sep 23, 2021 at 19:56

1 Answer 1

6

You can use Object.assign() to copy all the members of the data parameter to this at once, but TypeScript still requires you to declare the fields individually:

class MyClass implements MyInterface {
    prop1!: string;
    prop2!: string;
    prop3!: number;
    prop4!: boolean;
    /* ..
    ..
    ..*/
    prop30!: string;
    constructor(data: MyInterface) {
        Object.assign(this, data);
    }
}

so this is better, but not great.

(Also note that the compiler cannot verify that the properties are assigned, so you need to use the definite assignment assertion operator (!) in your field declarations to suppress warnings.)


You can save yourself this trouble if you are okay defining a class factory function and using it to produce a superclass. Here's the function:

function AssignCtor<T extends object>() {
    return class {
        constructor(t: T) {
            Object.assign(this, t)
        }
    } as { new(t: T): T }
}

The return value of AssignCtor<T> is asserted a type with a construct signature that accepts a value of type T and produces a class instance also of type T. This is what you want to do with MyInterface, so let's try it:

class MyClass extends AssignCtor<MyInterface>() implements MyInterface {

}

That's it. Let's make sure that MyClass behaves the way you expect:

function tryItOut(data: MyInterface) {
    const myClass = new MyClass(data);
    myClass.prop1.toUpperCase(); // okay    
}

Looks good. The value myClass is seen to have the properties of MyInterface.

Playground link to code

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks @jcalz I think class factory function would work for me. I will accept this as an answer once I implement it.
@jcalz, this is great! One thing I noticed is that the constructor allows unimplemented parameters to make it to the class instance. Any tips for protecting against this?
@DannyBeyrer if you can provide a minimal reproducible example of what you're talking about (like with a tsplay.dev link) then maybe? Note that comments on previously answered questions are not great places to get followup questions answered; if it turns out to be anything more than a one-liner, then you might want to make your own question post about it.
Thanks @jcalz. I posted my own question here: stackoverflow.com/q/70703950/4747264

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.