1

Currently I have the following setup.

When I pass foo as Column.key, I want the type of the argument value for Column.formatter to be string. For bar it should be number.

As is; Typescript expects the type to be all possible types on T. So string or number.

Is it possible to set the type based on the given key?

interface ColumnData {
    foo: string;
    bar: number;
};

type ColumnDataFormatter<T> = (value: T[keyof T]) => string;

class Column<T> {   
    constructor(
        public key: keyof T,
        public formatter: ColumnDataFormatter<T>
    ) {}
}
new Column<ColumnData>('foo', (value: string) => value);
new Column<ColumnData>('bar', (value: number) => value.toString());

-------------------------------------------------------------------
ERROR: TS2345
Types of parameters 'value' and 'value' are incompatible.    
Type 'string | number' is not assignable to type 'string'.
yluijten
  • 123
  • 2
  • 8

1 Answers1

1

Not really sure if this is the most terse solution, but you can do a couple of things:

interface ColumnData {
    foo: string;
    bar: number;
};

type ColumnDataFormatter<T, K extends keyof T> = (value: T[K]) => string;

class Column<T, K extends keyof T> {   
    constructor(
        public key: K,
        public formatter: ColumnDataFormatter<T, K>
    ) {}
}

new Column<ColumnData, 'foo'>('foo', (value: string) => value);
new Column<ColumnData, 'bar'>('bar', (value: number) => value.toString());

OR

interface ColumnData {
    foo: string;
    bar: number;
};



type ColumnDataFormatter<T> = (value: T[keyof T]) => string;

class Column<T> {   
    constructor(
        public key: keyof T,
        public formatter: ColumnDataFormatter<T>
    ) {}
}
new Column<ColumnData>('foo', (value: string | number) => typeof value === 'string' ? value: value.toString());
new Column<ColumnData>('bar', (value: string | number) => typeof value === 'string' ? value: value.toString());

TS Playground link: https://tsplay.dev/w8KGMW

Nishant
  • 54,584
  • 13
  • 112
  • 127