TypeScript Generics Priority
UPDATE: The issue is a limitation of TypeScript, the issue is on Typescripts GitHub now: https://github.com/microsoft/TypeScript/issues/47440
I currently have the following code:
class Table<T,> {
constructor(records: T[], columns: Column<T>[]) { ... }
}
class Column<T> {
constructor(name: string, transformer: (item: T) => string) { ... }
addTooltip(transformer: (item: T) => string): this { ... }
}
class Building {
constructor (public name: string) {}
}
const buildings = [
new Building("test"),
new Building("station")
];
I want to create a table, without having to specify that T
is of type Building
.
This works so far, because typescript can infer the type from the buildings array and it works. Even in the transformer
of the Column, I still get type suggestions.
const table = new Table(buildings, [
new Column("Name", building => building.name)
]);
But when I use addTooltip
, typescript can't figure out what T
is and completely freaks out, because the Column
assumes that T
is unknown
.
const table = new Table(buildings, [
new Column("Name", building => building.name),
new Column("Name", building => building.name).addTooltip(t => t.name)
]);
Is there a way to tell TypeScript to use the type of its records
constructor parameter as a source for T, ignoring what the columns think T
is - without using new Table<Building>(...)
? I know that we could use columns: Column<any>[]
, but then the type checking in the Columns would not work.
Playground link
I can't tell you how to do what you actually want to do (and what I'd actually want to do), but I can give you a couple of options for workarounds.
- You could have a static method on
Column
that does the combined operation of creating theColumn
and adding the tooltip. - You could have a static method on
Column
that accepts aColumn
and adds a tooltip to it. - You could have a standalone function that does what the static method in #2 does.
When you do that, the inference works.
#1
static withTooltip<T>(
name: string,
transformer: (item: T) => string,
tooltipTransformer: (item: T) => string,
): Column<T> {
const col = new Column(name, transformer);
col.addTooltip(tooltipTransformer);
return col;
}
Then building the array is:
const table = new Table(buildings, [
new Column("Name", building => building.name),
Column.withTooltip("Name", building => building.name, t => t.name),
]);
#2
static plusTooltip<T>(col: Column<T>, transformer: (item: T) => string): Column<T> {
col.addTooltip(transformer);
return col;
}
Then building the array is:
const table = new Table(buildings, [
new Column("Name", building => building.name),
Column.plusTooltip(new Column("Name", building => building.name), t => t.name),
]);
#3
...is just #2 but as a function rather than static method.
Playground link