Reputation: 334
When a function is inherited by a subclass, I want the return type to be as if the function were defined directly on the subclass.
To be clear, the code works fine at run-time. But I want to take advantage of static type-checking. I'm getting red squiggly lines in VScode and warnings from Google-Closure-Compiler. I'm not sure if this is an issue with my ES6 code or with my type annotations.
My trivial example ES6 classes:
// @ts-check
"use strict";
export class db_row {
/**
* @param {number} id
*/
constructor(id) {
/** @type {number} */
this.id = id;
}
clone() {
return new db_row(this.id);
}
}
export class Channel_row extends db_row {
/**
* Constructor
* @param {*=} init
* @param {string=} name
* @param {string=} value
*/
constructor(init, name, value = '') {
let id = -1;
if (typeof init == 'object') {
id = init.id;
name = init.name;
value = init.value;
} else if (typeof init == 'number') {
id = init;
}
super(id);
this.name = name;
this.value = value;
}
clone() {
return new Channel_row(this.id, this.name, this.value);
}
}
export class db_table {
/**
* Constructor
* @param {Array<db_row>} table
*/
constructor(table) {
/**@type {Array<db_row>} */
this.table = table;
}
/**
*/
get_table_copy() { return this.table.map(item => item.clone()) }
/**
* @param {?number=} id
*/
get_row_by_id(id) {
const row = this.table.filter(item => item.id === id)[0];
if (row) return row.clone();
return null;
}
}
export class Channel_table extends db_table {
constructor() {
/**@type {Array<Channel_row>} */
let table = [];
super(table);
}
}
// Test code below:
/**
*
* @param {Channel_row} chan_row
*/
function print_chan_row(chan_row) {
console.log(chan_row.name);
}
let channel_table = new Channel_table();
let channel_row = channel_table.get_row_by_id(0); // hover reports that the type of channel_row is db_row, when it should be type Channel_row
print_chan_row(channel_row); // Red squiggly line error: Argument of type 'db_row' is not assignable to parameter of type 'Channel_row'. Type 'db_row' is missing the following properties from type 'Channel_row': name, valuets(2345)
console.log(channel_row.name); // Red squiggly line error: Property 'name' does not exist on type 'db_row'.ts(2339)
let channel_table_2 = channel_table.get_table_copy(); // hover reports that the type of channel_row is db_row[], when it should be type Channel_row[]
print_chan_row(channel_table_2[0]); // Red squiggly line error: Argument of type 'db_row' is not assignable to parameter of type 'Channel_row'.ts(2345)
Now, if I move or copy the get_row_by_id() and get_table_copy() functions into the subclass, the type errors go away. But I don't want to duplicate code unnecessarily.
How can I declare the functions in the parent class so it can be reused in child classes, but maintain the static type checking?
As a bonus, can I also generalize the clone() function so it doesn't need to be over-ridden in subclasses of db_row?
Upvotes: 1
Views: 54