Home > Back-end >  Why keyof T doesn't work on a generic object
Why keyof T doesn't work on a generic object

Time:11-12

I'd like to have a function with a generic type T which must be an object and must have at least a property named code of type string, and then use keyof T to get all object keys.

Why is this invalid?

function TableView<T extends { code: string }>() {
    return forwardRef<HTMLDivElement, { data: T[], columns: Columns<keyof T> }>(
        (props, ref) => {

Error:

TS2344: Type 'keyof T' does not satisfy the constraint 'string'.
Type 'string | number | symbol' is not assignable to type 'string'.
Type 'number' is not assignable to type 'string'.

where:

export interface Column<T extends string> {
    field: T;
    label: string;
    align?: "left" | "center" | "right"
    format?: "number" | "integer" | "percent" | "timestamp" | "text"
    backgroundColor?: (row: any) => string
}

type Columns<T extends string> = Column<T>[]

export default Columns

CodePudding user response:

Theoretically T can have not only string keys (number | symbol are also allowed). If you want to pick only string keys - you can use Extract utility:

const e = Symbol();

type Foo = {
  a: string; // String-like name
  5: string; // Number-like name
  [e]: string; // Symbol-like name
};

type K1 = keyof Foo; // typeof e | "a" | 5
type K2 = Extract<keyof Foo, string>; // "a"

Playground


Extract<Type, Union> Constructs a type by extracting from Type all union members that are assignable to Union


Another option suggested by @kaya3 is using string & keyof T intersection.

CodePudding user response:

Just change extends string to extends any. It will give you the same result you're looking for. The problem is that keyof can be typed as more than just a string.

  • Related