Reputation: 9437
I have the following piece of code:
interface DummyTableRow {
id: number;
name: string;
}
interface RowChange<Row extends object> {
newRow: Row | null;
oldRow: Row | null;
}
interface RowChangeBag {
Dummy: RowChangeList<DummyTableRow>;
}
type RowChangeList<Row extends object> = Array<RowChange<Row>>;
function add<
Row extends object,
T extends keyof RowChangeBag,
// error on next line: Type 'DummyTableRow' is not assignable to type 'Row'.
L extends RowChangeList<Row> = RowChangeBag[T]
>(
bag: RowChangeBag,
tableName: T,
newRow: Row | null,
oldRow: Row | null,
) {
bag[tableName].push({ newRow, oldRow });
}
Why does it complain? Why is DummyTableRow
not assignable to Row
?
Or could this be a 'bug' in typescript? (i use version 2.6.2 for this example)
Upvotes: 1
Views: 1308
Reputation: 3061
Your code is mixing two types with similar names but their are not compatibles:
DummyTableRow
is your abstraction of the row → okRow extends object
: object
is not compatible with DummyTableRow
: id
and name
properties are missing. → Just do Row extends DummyTableRow
.Other issues:
RowChangeBag
has to be generic too.RowChangeBag
is meant to store several RowChangeList
, cf. T extends keyof RowChangeBag
and bag[tableName]
is a RowChangeList
. In the code below, I've split it into 2 interfaces, so that it will be easier to add tables in it.L
generic type constraint in the add
method is not used.My advices to enhance the code readibility:
T
: Row
→TRow
DummyTableRow
name can be simplified: just Row
null
; use only undefined
.T[]
rather than Array<T>
because it's shorter and more JavaScript idiomatic.Fix proposal (tested on the TypeScript Playground):
interface Row {
id: number;
name: string;
}
interface RowChange<TRow extends Row> {
newRow?: TRow;
oldRow?: TRow;
}
type RowChangeList<TRow extends Row> = RowChange<TRow>[];
interface TableGroup<T> {
Table1: T;
Table2: T;
}
interface RowChangeBag<TRow extends Row>
extends TableGroup<RowChangeList<TRow>> { }
function add<
TRow extends Row,
TKey extends keyof RowChangeBag<TRow>
>(
bag: RowChangeBag<TRow>,
tableName: TKey,
newRow: TRow,
oldRow: TRow,
) {
bag[tableName].push({ newRow, oldRow });
}
Upvotes: 1
Reputation: 15619
This is not a TypeScript bug.
The behavior you saw can be reduced to the following:
interface DummyTableRow {
id: number;
name: string;
}
function doing<Row>() {
let a: DummyTableRow
let b: Row
// error on next line: Type 'DummyTableRow' is not assignable to type 'Row'.
b = a
}
The issue here is that since Row
is a generic type, there is no way the compiler can tell whether a
can be assigned to b
, because b
can be any type defined at usage, e.g.
doing<{ a: string }>()
Upvotes: 2