Reputation: 1551
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
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 {}
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