Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
209 views
in Technique[技术] by (71.8m points)

typescript - How to define an interface as a class property?

When inheriting from a class, you have to use the super property to pass the arguments required to the constructor method of the parent class, therefore, you have to remember what are the required fields, so you'll to do the following for example:

class Person {
  constructor(name: string, age: number) {
    // ...
  }
}

class Boy extends Person {
  constructor(name: string, age: number) { //see, I have to remember property & type
    super(name, age);
  }
}

So, I was thinking of a way of doing this, one of the solutions is to change from using positional arguments to named arguments, so you can use the builder pattern to achieve this, then you'll use an interface, and you'll share that interface between the child and the parent, so:

interface PersonProps {
  name: string;
  age: number;
}

class Person {
  constructor(props: PersonProps) {
    // props.name ...... props.age
  }
}

class Boy extends Person {
  constructor(personSuper: PersonProps) {
    super(personSuper);
  }
}

Now the problem with this approach is that we're breaking the rule of cohesion, which means that all the code that is related together should go in a single standalone unit.

So, to write a better code, -of course- you'll split these files into multiple files, so the Person class should go to a separate file, and the Boy class should go to a separate file, but the problem is that each of these two files is depending on the PersonProps interface, In my opinion, it's not a good idea to make a separate file for that interface, because the Person class and that interface are highly related together, so what I'll be doing is to let the PersonProps interface live with the Person class in one file.

So here is the file tree now:

.
├── Boy.js
├── Boy.ts
├── Person.js
├── Person.ts
└── tsconfig.json

Now, The Person class is complete, the problem now is with the Boy class, because it also depends on the PersonProps interface.

So you might be thinking that I will export the PersonProps interface from the Person.ts module, in fact, I'll not, basically because you may have more than one class in one file, for example, the Person.ts file may later include more than one class, and not all of those classes will have an interface, therefore, we'll break the pattern, and slowly the code will become garbage, therefore, what I'm looking for, is to include that interface inside the Person class, then all of the instances of that Person class will inherit the PersonProps interface within the instance.

That's a lot of talking, here is how both files look like now:

Person.ts:

interface PersonProps {
  name: string;
  age: number;
}

class Person {
  constructor(props: PersonProps) {
    // props.name ...... props.age
  }
}

Boy.ts:

import { Person } from "./Person";

class Boy extends Person {
  constructor(personSuper: PersonProps) {
    super(personSuper);
  }
}

Now We need to connect the Boy.ts with the PersonProps interface, one of the folks will say "Hey, just go ahead and export the interface from Person.ts, export interface PersonProps..." That solution might work, but However, No! as I mentioned this will break the pattern, and the code will slowly start to make a lot of noise.

Here we'll arrive at 2 good solutions:

[FIRST]:

inside Person.ts, remove the export keyword from beside the Person class, and by the end of the file, just write:

export = {
  Person: {
    Person,
    PersonProps, // ERROR!
  },
};

This is giving a compile-time error, for a very unknown logical reason, why that's not allowed!?

[SECOND]:

As I mentioned earlier, just let the PersonProps be property inside the class instances, so:

So the complete file will look the following:

Person.ts:

interface PersonProps {
  name: string;
  age: number;
}

class Person {
  PersonProps: interface; // ERROR!
  constructor(props: PersonProps) {
    // props.name ...... props.age
    this.PersonProps = PersonProps; // ERROR!
  }
}

There are two compile-time errors here:

  1. An interface is not a type.
  2. You can't set an interface as a property within a class.

Now, finally, after writing this very long question:

  • Do you have any idea of how to complete this solution?
  • Do you have a better idea?
question from:https://stackoverflow.com/questions/65651455/how-to-define-an-interface-as-a-class-property

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Your problem is usual:

classes can have only values as properties, not types. Don't worry, there is a way! Namespaces in TypeScript can also have types and it can have the same name as any value (class, function, or variable). Code can look like this:

class Person {
    constructor(props: Person.Props) {
        // ...
    }
}

namespace Person {
    export interface Props {
        name: string;
        age: number;
    }
}

But remember, this is not class property, so you can't use this.Props or this['Props'].

Secondly

If you use a normal TypeScript compiler, use ECMAScript modules. TypeScript automatically compiles to Node.js modules. You used it in the import but not in the export. Don't use export = {}. See TypeScript Docs for more.

In tsconfig.json:

{
    "compilerOptions": {
        // ...
        "moduleResolution": "node",
        "module": "commonjs"
    }
}

And then:

Person.ts

export class Person {
    constructor(props: Person.Props) {
        //...
    }
}

export namespace Person {
    export interface Props {
        name: string;
        age: number;
    }
}

Boy.ts

import {Person} from "./Person";

export class Boy extends Person {
    constructor(props: Person.Props) {
        super(props);
    }
}

Note:

If you don't know why export is in the namespace, that's, therefore, property in the namespace without export is private (you can access it only in the namespace TypeScript Docs for more. Also, name the property props not the same as an interface.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...