[英]How to properly type a variable that is a class in typescript
我有一個代表數據庫中記錄的 class 層次結構,每個表都有一個 class 。 我希望能夠正確鍵入檢查 class 對象(表示表)以及 class 的實例(表示數據庫中的各個行)。 我可以編譯我的代碼的唯一方法是通過將保存 class 的變量轉換為<any>
來禁用類型檢查。 下面是一個人為的示例,演示了我遇到的問題/問題。
我的主要問題是關於持有類的變量的類型。 我認為這三個問題的答案應該是相同的:
name
字典中的值賦予什么類型(在代碼的第 3 行定義)?cls
什么類型(在第 31 行定義)?maybeMakeInstance
中的cls
應該是什么類型獎金問題:
names
)永遠不會在子類中定義?tablename
成員(例如,表名和all
在第 6 行和第 7 行定義並在第 61 和 62 行重新定義)的正確方法是什么?class A {
// this should not be inheritable
static names:{ [name: string]: { new(key:string):A} } = {};
// next two need to be inherited in each subclass
static tablename:string = "Aclass";
static all: Record<string, A> = {};
id: string;
// create a new object and put into per class cache, `all`
constructor(key:string = null) {
this.id = key;
if (key != null) {
new.target.all[key] = this;
}
}
// return instance matching `key`. Will only search in exact class (not subclasses or superclasses)
static find(key:string):A|null {
if (key in this.all) {
return this.all[key];
}
console.log(`${key} not in ${this.tablename}`);
return null;
}
// pretty print info about instance
show():void {
// What is proper type for `this.constructor`? <{ new(key:string):A}> fails as does doing nothing.
const cls = <any>this.constructor;
console.log(`${this.id} from ${cls.tablename}`);
}
// pretty print info about instance
static showall():void {
for (let x in this.all) {
this.all[x].show();
}
}
static init(name:string):void {
this.names[name] = this;
}
static maybeMakeInstance(clsname:string, key:string):A {
if ( !(clsname in A.names) ) throw new Error(`unknown classname: ${clsname}`);
// what is proper type of `cls`?
let cls:any = A.names[clsname];
if (key in cls.all) {
console.log(`Reusing ${key} in class ${clsname}/${cls.tablename}`);
return cls.all[key];
}
return new cls(key);
}
};
A.init('classA');
class B extends A {
// is this proper way to override superclass static members?
static tablename:string = "Bclass";
static all: Record<string, B> = {};
}
B.init('classB');
// make sure above code is working.
function main() {
let a = new A('first');
A.showall();
A.find('first').show();
new A('second');
new B('one');
A.showall();
B.showall();
console.log(B.find('first'));
console.log(B.find('second'));
console.log(B.find('one'));
console.log(A.find('one'));
A.maybeMakeInstance('classA', 'third');
A.maybeMakeInstance('classB', 'two');
A.maybeMakeInstance('classB', 'two');
console.log('------ A');
A.showall();
console.log('------ B');
B.showall();
A.maybeMakeInstance('classA', 'two');
console.log('------ A');
A.showall();
}
main();
////////////////
// running this file will result in the following output:
////////////////
// first from Aclass
// first from Aclass
// first from Aclass
// second from Aclass
// one from Bclass
// first not in Bclass
// null
// second not in Bclass
// null
// B { id: 'one' }
// one not in Aclass
// null
// Reusing two in class classB/Bclass
// ------ A
// first from Aclass
// second from Aclass
// third from Aclass
// ------ B
// one from Bclass
// two from Bclass
// ------ A
// first from Aclass
// second from Aclass
// third from Aclass
// two from Aclass
////////////////
這兩個額外問題的答案基本上都是“你不能”,這對我來說表明這不是一個偉大的設計。 所以我基本上重寫了你的代碼,而不是回答你的打字問題,我希望這會有所幫助。
你的classA
試圖做的太多了。 它代表一個單獨的表 object,存儲它自己類型的所有實例,並存儲所有其他類型的名稱。 讓我們將這三種用途分解為它們自己的類。
每個 object 實例都是一個TableObject
。 它打印自己的唯一方法。
class TableObject {
id: string;
table: Table;
// TableObject stores a reference to the table that it is in
constructor (table: Table, id: string) {
this.id = id;
this.table = table;
}
// pretty print info about instance
show():void {
console.log(`${this.id} from ${this.table.tablename}`);
}
}
每個 class 名稱都是一個Table
。 該表存儲object實例,可以查找或創建對象,可以打印所有對象。
class Table {
tablename: string;
all: Record<string, TableObject> = {};
// construct a table from a name
constructor( tablename: string ) {
this.tablename = tablename;
}
// return TableObject instance matching `key`.
find(key:string): TableObject | null {
const obj = this.all[key];
if ( obj ) return obj;
console.log(`${key} not in ${this.tablename}`);
return null;
}
// create a new TableObject instance and put into per class cache, `all`
create(key:string): TableObject {
const obj = new TableObject(this, key);
this.all[key] = obj;
return obj;
}
// pretty print info about all TableObject instances in this table
showAll(): void {
for (let x in this.all) {
this.all[x].show();
}
}
}
不同的表存儲在Database
中。 數據庫可以執行之前class A
中的maybeMakeInstance
和showAll
函數。
class Database {
// store an object of Table classes keked by table name
tables: Record<string, Table> = {};
// can create an empty Database, or initialize with some Tables
constructor( tables: Table[] = [] ) {
tables.forEach(t => this.addTable(t));
}
// add an existing Table object to the Database
addTable( table: Table ): void {
this.tables[table.tablename] = table;
}
// create a new Table object and add it to the Database
createTable( tablename: string ): Table {
const table = new Table(tablename);
this.addTable(table);
return table;
}
// retrieve Table object by name
getTable(tablename: string): Table | null {
return this.tables[tablename] ?? null;
}
// find or create a TableObject based on the table name and object key
maybeMakeInstance(tablename:string, key:string): TableObject {
const table = this.getTable(tablename);
if ( ! table ) throw new Error(`unknown table name: ${tablename}`);
const existing = table.find(key);
if (existing) {
console.log(`Reusing ${key} in table ${tablename}`);
return existing;
}
return table.create(key);
}
// show all result from all tables
showAll(): void {
Object.values(this.tables).forEach(t => t.showAll());
}
}
您的代碼示例如下所示:
function main() {
const tableA = new Table('classA');
tableA.create('first');
tableA.showAll();
tableA.find('first')?.show();
tableA.create('second');
const database = new Database([tableA]);
const tableB = database.createTable('classB')
tableB.create('one');
tableA.showAll();
tableB.showAll();
console.log(tableB.find('first'));
console.log(tableB.find('second'));
console.log(tableB.find('one'));
console.log(tableA.find('one'));
database.maybeMakeInstance('classA', 'third');
database.maybeMakeInstance('classB', 'two');
database.maybeMakeInstance('classB', 'two');
console.log('------ A');
tableA.showAll();
console.log('------ B');
tableB.showAll();
database.maybeMakeInstance('classA', 'two');
console.log('------ A');
database.showAll();
}
main();
它現在能夠區分不同的表,最終打印 output:
"first from classA"
"second from classA"
"third from classA"
"two from classA"
"one from classB"
"two from classB"
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.