简体   繁体   中英

Typescript: allow a generic type to only be an object with 'string' properties

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' properties. Raise an error if for instance the passed generic interface would contain number or symbols properties.

Here is a POC of what I tried and commented the behaviour I am looking for:

class Test<T extends {[key: string]: any}>{
    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 }>();

// the property is a number = should raise an error
const shouldFail = new Test<{ 0: string }>();

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") >{ }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM