Reputation: 57195
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
Reputation: 9821
type InterfaceOf<ClassType> = {
[Member in keyof ClassType]: ClassType[Member];
};
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
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
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
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
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
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