Colliot
Colliot

Reputation: 1551

How to express "the types of the values under all existing keys are of some type" in generic constraints in TypeScript?

The most natural idea seems to be

interface Generic<T extends {[key: string]: string | undefined}> {

}

interface Simple {
    id?: string;
}

function dummy(param: Generic<Simple>): any {}

But this does not work. It is understandable however, since an object a: Simple can possibly have other fields, whose keys may not be of string type.

One possible case where this is useful would be that "query parameters are all strings", so we want the type of req.query to be {[key: string]: string | undefined}. There is no problem in doing this with an object. But if we want to extend the definition, say, from "express", and use it in generic constraints:

export interface RequestWithQuery<T extends {[key: string]: string | undefined}> extends Request {
    /**
     * The custom typed query.
     */
    query: T;
}

interface DummyQueries { input?: string; }

function dummyController(req: RequestWithQuery<Dummy>, res: Response) {}

The following error would arise:

error TS2344: Type 'DummyQueries' does not satisfy the constraint '{ [key: string]: string | undefined; }'. Index signature is missing in type 'DummyQueries'.

The problem actually boils down to expressing "having exactly these fields", instead of "having at least these fields", I think. But is it possible in TypeScript?

Upvotes: 0

Views: 137

Answers (1)

Nitzan Tomer
Nitzan Tomer

Reputation: 164357

You can do something which is close enough:

interface Base {
    [key: string]: string;
}

interface Generic<T extends Base> {}

interface Ok extends Base {
    id?: string;
}

interface NotOk extends Base {
    id?: string;
    num?: number; // error: Property 'num' of type 'number' is not assignable to to string index type `string`
}

function dummy(param: Generic<Ok>): any {}

(code in playground)

The type checking fails in the definition of the interface instead of when using it as the generic type of Generic but the outcome is the same, if I understand what you want to achieve.

Upvotes: 2

Related Questions