in the following code, if I use T extends HealthyConfig
and create a Config
class instance using the generic T
I get an error of Type 'boolean' does not satisfy the constraint 'T[keyof T]'
, but when directly referencing HealthyConfig
no error is displayed. Surely because T
extends HealthyConfig
the types should be known as far as at least those defined in HealthyConfig
so why doesn't it work with using T
?
type Key = string | number;
type Val = Key | boolean | Obj;
type Obj = { [x: Key]: Val } | Array<Val>;
interface Config<T extends Obj> {
get<Tret extends T[keyof T]>(key: keyof T, defaultVal: Tret): Tret;
}
type EnabledConfig = {
enabled?: boolean;
}
class Enabled<T extends EnabledConfig> implements Config<T> {
public readonly c: T;
constructor(c?: T) {
this.c = c || {} as T;
}
get<Tret extends T[keyof T]>(key: keyof T, defaultVal: Tret): Tret {
const result: Tret = this.c[key] as Tret;
return (result === undefined) ? defaultVal : result;
}
}
type Status = 'yes' | 'no';
type HealthyConfig = EnabledConfig & {
healthy?: Status;
}
class Healthy {
public readonly config: Config<HealthyConfig>
constructor(config: Config<HealthyConfig>) {
this.config = config;
}
get enabled(): boolean {
return this.config.get<boolean>('enabled', true);
}
}
const en = new Enabled<HealthyConfig>({enabled: false});
const isEnabled: boolean = en.get<boolean>('enabled', true); // works
const isHealthy: Status = en.get<Status>('healthy', 'no'); // works
const h = new Healthy(new Enabled<HealthyConfig>({enabled: true, healthy: 'no'}));
const enabled: boolean = h.config.get<boolean>('enabled', true); // works
const healthy: Status = h.config.get<Status>('healthy', 'yes'); // works
// This generic "T extends HealthyConfig" causes a problem
// with the "this.config.get" showing an error on the "<boolean>"
// of "Type 'boolean' does not satisfy the constraint 'T[keyof T]'"
class UnHealthy<T extends HealthyConfig> {
public readonly config: Config<T>
constructor(config: Config<T>) {
this.config = config;
}
get enabled(): boolean {
return this.config.get<boolean>('enabled', true);
}
}
CodePudding user response:
I've made a little refactor of your code:
type Key = string | number;
type Val = Key | boolean | Obj;
type Obj = { [x: Key]: Val } | Array<Val>;
interface Config<T extends Obj> {
get<K extends keyof T, V extends T[K]>(key: K, defaultVal: V): V;
}
type EnabledConfig = {
enabled?: boolean;
}
class Enabled<T extends EnabledConfig> implements Config<T> {
public readonly c: T;
constructor(c?: T) {
this.c = c || {} as T;
}
get<K extends keyof T, V extends T[K]>(key: K, defaultVal: V): V {
const result: V = this.c[key] as V;
return (result === undefined) ? defaultVal : result;
}
}
type Status = 'yes' | 'no';
type HealthyConfig = EnabledConfig & {
healthy?: Status;
}
class Healthy {
public readonly config: Config<HealthyConfig>
constructor(config: Config<HealthyConfig>) {
this.config = config;
}
get enabled() {
return this.config.get('enabled', true);
}
}
const en = new Enabled<HealthyConfig>({enabled: false});
const isEnabled: boolean = en.get('enabled', true); // works
const isHealthy: Status = en.get('healthy', 'no'); // works
const h = new Healthy(new Enabled<HealthyConfig>({enabled: true, healthy: 'no'}));
const enabled: boolean = h.config.get('enabled', true); // works
const healthy: Status = h.config.get('healthy', 'yes'); // works
// This generic "T extends HealthyConfig" causes a problem
// with the "this.config.get" showing an error on the "<boolean>"
// of "Type 'boolean' does not satisfy the constraint 'T[keyof T]'"
class UnHealthy<T extends Config<HealthyConfig>> {
public readonly config: T
constructor(config: T) {
this.config = config;
}
get enabled() {
return this.config.get('enabled', true);
}
}
Is this what you need?
CodePudding user response:
Why do you need UnHealthy to be generic? Provide concrete config, HealthyConfig in your case, compiler will pick up T[keyof T], and you won't able to use type distinct from boolean | Status | undefined
as the defaultVal arg.
class UnHealthy {
public readonly config: Config<HealthyConfig>
constructor(config: Config<HealthyConfig>) {
this.config = config;
}
get enabled() {
console.log(this.config)
return this.config.get('enabled', false)
}
}