Home > OS >  In Typescript "keyof Record<string, string>" is not "string"
In Typescript "keyof Record<string, string>" is not "string"

Time:05-07

I'm hitting this wall since days now and I'm not sure anymore if I'm the problem or if typescript is broken...

I defined a generic class with the generic extending Record<string, string>:

class DbTable<ColumnDefinitions extends Record<string, string>> { /* ... */ }

At a later point I want to use the exact keys (e.g. 'id' | 'name' instead of just string), so I use the keyof operator on the generic, like this:

function selectFields(fields: (keyof ColumnDefinitions)[]) { /* ... * / }

But keyof ColumnDefinitions resolves to string | number | symbol instead of just being of type string.

What did I miss?!

Here is a TS playground link with an example.

CodePudding user response:

To my understanding, the built-in generic Record<T> adds on the symbol and number type. If you think about what happens in Javascript, which is what typescript compiles to, this makes sense, because you can index with number, string, or symbols.

Ex:

const map = {0: "hello"}
//both are valid
map[0] 
map["0"]

A simple type check to ensure fieldNames is equal to string should resolve your problem at the end of your sandbox.

 fields: fields.map(fieldName => {
  if(typeof fieldName === "string") return new DbField(fieldName)
  else return {}
}

Or you can do the following to ensure conversion into string:

    fields: fields.map(fieldName => new DbField(fieldName.toString())

CodePudding user response:

You can extract only strings from string | number | symbol in the method signature.

Instead of

fields: (keyof T)[]

you can write

fields: (Extract<keyof T, string>)[]

and the error goes away.

TypeScript Playground

  • Related