We previously explained how to add new attributes to a class using a decorator and properly use the decorated class accordingly to the type system. Here we use a property which type is a generic and a method which argument is a generic.
Add a property
Here we add a property named data which is an instance of some Data class.
class Data {
name: string = ''
}
interface IFoo<D extends Data> {
data?: D
}
function decorate<
T extends { new (...args: any[]): {} },
D extends Data
>(constructor: T) {
return class extends constructor implements IFoo<D> {
data?: D
}
}
@decorate
class Foo {
static new<D extends Data>() {
return new Foo() as TFoo<D>
}
}
type TFoo<D extends Data> = Foo & IFoo<D>
Here is how one can use it
var foo = Foo.new()
console.log(foo.data)
foo.data = {
name: 'blabla'
}
console.log(foo.data.name)
We can also decorate using a strict subclass of data
class XData extends Data {
x = true
}
@decorate
class Bar {
static new<D extends XData>() {
return new Bar() as TBar<D>
}
}
type TBar<D extends XData> = Bar & IFoo<D>
Here is how one can use it
var bar = Bar.new()
bar.data = {
name: 'blabla',
x: true
}
console.log(bar.data.name)
If we want Foo to also accept Data subclasses, we must add any string signature to Data:
class Data {
[$: string]: any
name: string = ''
}
Here is how one can use it
foo.data = {
name: 'blabla',
x: true
}
console.log(foo.data.name)
Add a method
Here we add a method named test which sole argument is an instance of some Data.
class Data {
[$: string]: any
name: string = 'foo'
}
interface IFoo<D extends Data> {
test(data: D): void
}
function decorate<
T extends { new (...args: any[]): {} },
D extends Data
>(constructor: T) {
class U extends constructor implements IFoo<D> {
test(data: D) {
return data.name
}
}
return U
}
@decorate
class Foo {
static new<D extends Data>() {
return new Foo() as TFoo<D>
}
}
type TFoo<D extends Data> = Foo & IFoo<D>
Here is how we can use it
var foo = Foo.new()
console.log(foo.test(new Data()))
Subclassing the Data class
class XData extends Data {
x = 421
}
@decorate
class Bar {
static new<D extends XData>() {
return new Bar() as TBar<D>
}
constructor(name: string) {
super()
this.name = name
}
}
type TBar<D extends XData> = Bar & IFoo<D>
and some usage
var bar = Bar.new()
console.log(bar.test(new XData('bar')))
console.log(foo.test(new XData('baz')))
Playgrounds
Typescript playground to add a property.
Typescript playground to add a method.