I am wondering if it is possible in TS to enforce the type of the properties for a generic. I would like to only allow passing a generic of an object type with \'string\' pr
If the object has a string index we can index the object by number as well, so there is no reason for the compiler to throw an error number keys. This is by design.
declare let skeys: { [key: string]: number }
let v1 = skeys[0] // number
let v2 = skeys["0"] // number
declare let nkeys: { [key: number]: number }
let v3 = nkeys[0] // number
let v4 = nkeys["0"] // error
declare let snkeys: {
[key: number]: number;
[key: string]: string | number // The string index has to contain any value reuned by the number index
}
let v5 = snkeys[0] // number
let v6 = snkeys["0"] // string| number
We can use a conditional type to force an error if the object contains any non-string keys. The error will not be very pretty, but it is readable and can get the job done:
class Test<T extends { [key: string]: any } & (keyof T extends string ? {} : "T must obnly have string keys") >{
private data!: T;
public getValue<K extends keyof T>(key: K): T[K] {
return this.data[key];
}
}
// the property is a string = ok
const okay = new Test<{ "aString": string }>();
// Error: Type '{ 0: string; }' does not satisfy the constraint '{ [key: string]: any; } & "T must only have string keys"'.
const shouldFail = new Test<{ 0: string }>();
Note
If you don't have any other constraints on the values of T
a simple object
type would work as well
class Test<T extends object & (keyof T extends string ? {} : "T must only have string keys") >{ }