Reputation: 5854
I want to implement the following pattern. A class where subclasses should have static defaults
property, an options
constructor argument, and an $options
instance property that holds the merged values of the former two values. All three should have the same shape. Something like this:
class Foo<T> {
static defaults: T
$options: T
constructor (options: T) {
this.$options = Object.assign({}, Foo.defaults, options)
}
}
However, I get the following error for static defaults: T
:
Static members cannot reference class type parameters.
Then, TypeScript sets the type of defaults
to any
. After that, if I do this:
interface BarOptions {
length: number
color: string
}
class Bar extends Foo<BarOptions> {
static defaults = {
length: 'oops' // should be number
}
}
let bar = new Bar({
length: 42,
color: 'green'
})
The object in new Bar({...})
is correctly type-checked, but defaults
isn't, as it's considered any
. This means I can mistakenly set length
to string
without an error. How to avoid this?
I read about the difference between static and instance sides of classes in the docs, but I still don't know how to solve this problem. Is there a solution at all?
It appears I can do it this way, although a bit clumsy:
class Foo<T> {
static defaults: any
$options: T
constructor (options: T) {
this.$options = Object.assign({}, Foo.defaults, options)
}
}
interface BarOptions {
length: number
color: string
}
class Bar extends Foo<BarOptions> {
static defaults: BarOptions = {
length: 12,
color: 'black'
}
}
let bar = new Bar({
length: 42,
color: 'green'
})
Is there a better way that doesn't require explicitly setting static defaults: BarOptions
in Bar
?
Upvotes: 1
Views: 2193
Reputation: 5534
You can't define static
properties using dynamic T
signature. What you could do is define abstract class
and force any subclass to implement getDefaults
method:
abstract class Foo<T> {
abstract getDefaults(): T
$options: T
constructor (options: T) {
this.$options = Object.assign({}, this.getDefaults(), options)
}
}
interface BarOptions {
length: number
color: string
}
class Bar extends Foo<BarOptions> {
getDefaults(): BarOptions {
return {
length: 3,
color: "red"
};
}
}
let bar = new Bar({
length: 42,
color: 'green'
})
Please see playground.
Upvotes: 2