danday74
danday74

Reputation: 57195

Typescript automatically get interface properties in a class

Hello TypeScript experts.

I have the following code but I have to repeat the interface properties in the class otherwise I get:

Class incorrectly implements interface

When using an interface, is there a TypeScript shorthand for doing this without having to declare Id: number; and all the other properties in the class? Thx

interface INavigation {
  Id: number;
  AppId: number;
  NavId: number;
  Name: string;
  ParentId: string;
  PageURL: string;
  Position: string;
  Active: string;
  Desktop: string;
  Tablet: string;
  Phone: string;
  RoleId: string;
  Target: string;
}

class Navigation implements INavigation {

  Id: number;
  AppId: number;
  NavId: number;
  Name: string;
  ParentId: string;
  PageURL: string;
  Position: string;
  Active: string;
  Desktop: string;
  Tablet: string;
  Phone: string;
  RoleId: string;
  Target: string;

  constructor(navigation: any) {
    this.Id = navigation.Id
    this.AppId = navigation.NavAppId
    this.NavId = navigation.NavId
    this.Name = navigation.NavName
    this.ParentId = navigation.NavParentId
    this.PageURL = navigation.NavPageURL
    this.Position = navigation.NavPosition
    this.Active = navigation.NavActive
    this.Desktop = navigation.NavDesktop
    this.Tablet = navigation.NavTablet
    this.Phone = navigation.NavPhone
    this.RoleId = navigation.NavRoleId
    this.Target = navigation.NavTarget
  }
}

Upvotes: 45

Views: 22609

Answers (7)

Gil Epshtain
Gil Epshtain

Reputation: 9821

Short answer, pure TypeScript

type InterfaceOf<ClassType> = {
    [Member in keyof ClassType]: ClassType[Member];
};

Example of use:

class MyClass {
  private num: number; // private member will be ignored by 'InterfaceOf'
  public bool: boolean;
  public foo() {};
}

type MyClassType = InterfaceOf<MyClass>;

class MyClass2 implements MyClassType { // Error from this line, missing foo
    public bool: boolean;
}

The error will say:
Class 'MyClass2' incorrectly implements interface InterfaceOf<MyClass>. Type 'MyClass2' is missing the following properties from type InterfaceOf<MyClass>: foo

Upvotes: 4

DumpusWumpus
DumpusWumpus

Reputation: 21

I was messing around trying to do this and found the following solution by using the classes own type for the constructor parameter:

export class Test {
    prop1: string
    prop2: number

    constructor (data: Test) {
        this.prop1 = data.prop1
        this.prop2 = data.prop2
    }
}

If you needed just the properties of the class (to ignore methods) you'd have to create a new type using Pick

type TestI = Pick<Test, 'prop1' | 'prop2'>

Upvotes: 2

Mohsin Ejaz
Mohsin Ejaz

Reputation: 414

in case you using angular you can pass whole component as prop which is crazy 😜

Child Component TS:

@Input() parent!: YourParentClass;

Parent Component HTML:

<child [parent]="this" ></child>

Upvotes: 0

Quentin Gary
Quentin Gary

Reputation: 21

A mix between the 2 answers, for avoid long assignment in the constructor by using class/interface merging and Object.assign in the constructor :

interface Foo {
    a: number;
    b: number;
}

interface Baz extends Foo { }

class Baz  {
    c: number = 4

  constructor (foo: Foo) {
    Object.assign(this, foo, {})
  }
  
  getC() {
    return this.c
  }
}

let foo: Foo = {
    a: 3,
    b: 8
}

let baz = new Baz(foo)
// keep methods and properties
console.warn(baz)

Upvotes: 2

SamB
SamB

Reputation: 3273

This is now possible in Typescript using class/interface merging.

interface Foo {
    a: number;
}

interface Baz extends Foo { }
class Baz {
    constructor() {
        console.log(this.a); // no error here
    }
}

https://github.com/Microsoft/TypeScript/issues/340#issuecomment-184964440

Upvotes: 74

samira mokaram
samira mokaram

Reputation: 113

Class declarations should explicitly implement interfaces.

Upvotes: -2

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 250186

There is no built-in support for this.

We can however use a function that returns a class as the base type of our class. This function can lie a little bit and claim it implements the interface. We could also pass in some defaults for the members if necessary.

interface INavigation {
  Id: number;
  AppId: number;
  NavId: number;
  Name: string;
  ParentId: string;
  PageURL: string;
  Position: string;
  Active: string;
  Desktop: string;
  Tablet: string;
  Phone: string;
  RoleId: string;
  Target: string;
}

function autoImplement<T>(defaults?: Partial<T>) {
  return class {
    constructor() {
      Object.assign(this, defaults || {});
    }
  } as new () => T
}

class Navigation extends autoImplement<INavigation>() {
  constructor(navigation: any) {
    super();
    // other init here
  }
}

If we want to have a base class, things get a bit more complicated since we have to perform a bit of surgery on the base type:

function autoImplementWithBase<TBase extends new (...args: any[]) => any>(base: TBase) {
  return function <T>(defaults?: Partial<T>): Pick<TBase, keyof TBase> & {
    new(...a: (TBase extends new (...o: infer A) => unknown ? A : [])): InstanceType<TBase> & T
  } {
    return class extends base {
      constructor(...a: any[]) {
        super(...a);
        Object.assign(this, defaults || {});
      }
    } as any
  }
}

class BaseClass {
  m() { }
  foo: string
  static staticM() { }
  static staticFoo: string
}

class Navigation extends autoImplementWithBase(BaseClass)<INavigation>() {
  constructor(navigation: any) {
    super();
    // Other init here
  }
}

Navigation.staticFoo
Navigation.staticM
new Navigation(null).m();
new Navigation(null).foo;

Upvotes: 17

Related Questions