According to the information provided in the typescript documentation:
The keyof operator takes an object type and creates a union of its keys as string or numeric literals.
This means that if you have an object like this:
const obj = {a: 1, b: 2, c: 3}
and you do:
type K = keyof typeof obj;
K
will contain a union of all the keys present in the obj
, which in this case is:
"a" | "b" | "c"
.
In your example, you define a TableConfig<T>
and specify that the idName
will be of type keyof T
. However, it's important to note that without any constraint on T
, keyof
could refer to any type.
For instance:
interface TableConfig<T> {
idName: keyof T;
}
const ex1: TableConfig<string> = {
idName: ... // `idName` type: "anchor" | "blink" | ... | "trim" | "valueOf"
}
const ex2: TableConfig<{path: string, enabled: boolean}> = {
idName: ... // `idName` type: "enabled" | "path"
}
Therefore, it's essential to consider what you intend to achieve with keyof T
when T
is not restricted by any condition.
If you desire it to always be a string
, simply use idName: string
.
If you wish to pass an object, then you need to impose constraints on your generic.
A straightforward approach to ensure that the T
in the TableConfig
is always an object is:
interface TableConfig<T extends {}> {
idName: keyof T;
}
You can delve deeper into this topic in the provided documentation.
Furthermore, if you apply constraints, attempting to pass an arbitrary type T
to the constrained type T
in TableConfig<T extends ...>
while defining
tableConfig: TableConfig<T>
for a particular type
T
would result in an error. Thus, you may need to rectify this aspect as well.
Another avenue worth exploring is reconsidering the structure of the Table
interface. Is it necessary for the table's "config" to rely heavily on the data? One potential solution could involve utilizing multiple type parameters to handle the configuration type distinctively.
While these are suggestions on how to address the matter, the optimal resolution depends on your specific scenario. Based on your explanation, specifying idName
as string
might suffice.
Edit: Upon revisiting your query, I acknowledge that my initial response did not directly target the core issue. Therefore, here is additional clarification regarding your concern.
Typically, the keyof
operator generates a literal union (exceptions exist). There isn't a way to coerce the outcome of the keyof
operation into a plain string.
Even efforts to manipulate keyof
to yield a string will be futile:
interface O<T> {
data: keyof T;
}
// attempting to enforce string keys
const obj1: O<{[key: string]: any}> = {
data: ... // data will result in type: `string | number`
}
// trying to mandate number keys
const obj2: O<{[key: number]: any}> = {
data: ... // data will result in type: `number`
}
In the case of obj1
, even if you try to limit the object keys to strings, numerical values can still be accepted because JavaScript automatically coerces object keys into strings. Hence, accessing obj[0]
is equivalent to obj["0"]
.
A notable case arises in obj2
. If you specify keys other than string
, such as number
, the behavior aligns with expectations.
For further insights, visit https://www.typescriptlang.org/docs/handbook/2/keyof-types.html.
Hence, if you anticipate tableConfig.idName
being a string, avoiding keyof
is recommended.