2

I'm making a query builder. I have data models that look like this:

export interface SalesOrder {
  id: number
  customerName: string;
  date: Date;
}

In my query builder, I want to have the columns look something like this:

{
  id: 'salesOrder.id',
  customerName: 'customer.name',
  date: 'salesOrder.date',
}

I'm trying to figure out how I can type the Columns object generically so that it will have all the keys from the model, but all of the types will be a string. So then I would be able to declare it like const columns: Columns<SalesOrder> and it would understand that the object must have all the same keys as in the SalesOrder model, but with all the values being string.

I tried this:

export interface Columns<T> {
  [alias: keyof T]: string;
}

But it complains about the keyof T part, saying "An index signature parameter type must be either 'string' or 'number'."

0

1 Answer 1

4

The end of that error message should have been:

Consider using a mapped object type instead.

See the documentation on mapped types.

Here's what that means:

export type Columns<T> = {
  [alias in keyof T]: string;
}

It needs to be a type, because this is actually a mapped type. It walks all possibilities of keyof T and evaluates each member of that union individually. This isn't really supported in interfaces.

And [key: MyType] is an index signature. It says that for any MyType there is a value of a consistent type. Where [key in MyType] define the keys of a mapped type.

Playground


Alternatively, you could try the Record type.

export type Columns<T> = Record<keyof T, string>

Playground

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks! Do you know if there is any effective difference between the two solutions? Or is it basically up to whatever my semantic preference is?
I'm fairly sure that the definition of Record is type Record<K extends string, T> = { [P in K]: T } so I think they are completely equivalent.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.