[英]Typescript Using 'this' type in constructors
用例:我想要 model 一个通用系统,用于查找特定 class(一个Model
类)实例的实例(一个Table
类)。
我想做的一个最小的例子:
// Generic part
abstract class Table<T extends Model> {
instances: Map<number, T> = new Map();
}
abstract class Model {
constructor(
public readonly id: number,
public table: Table<this> // Error
) {
table.instances.set(id, this);
}
}
// Example: a table of Person objects
class Person extends Model {
constructor(
id: number,
table: Table<this>, // Error
public name: string
) {
super(id, table);
}
}
class PersonTable extends Table<Person> {}
const personTable = new PersonTable();
const person = new Person(0, personTable, 'John Doe');
// Note: the idea of using `this` as generic type argument is to guarantee
// that other models cannot be added to a table of persons, e.g. this would fail:
// class SomeModel extends Model { prop = 0; }
// const someModel = new SomeModel(1, person.table);
// ^^^^^^^^^^^^
不幸的是,TypeScript 在构造函数中抱怨this
类型。 为什么不允许这样做? 有一个更好的方法吗?
现在我正在使用以下不安全的替代方案。
// Generic part
abstract class Table<T extends Model> {
instances: Map<number, T> = new Map();
}
abstract class Model {
public table: Table<this>;
constructor(
public readonly id: number,
table: Table<Model>
) {
table.instances.set(id, this);
this.table = table as Table<this>;
}
}
// Example: a table of Person objects
class Person extends Model {
constructor(
id: number,
table: Table<Person>,
public name: string
) {
super(id, table);
}
}
class PersonTable extends Table<Person> {}
回答 Liam 的评论: this
类型的一个非常简单的安全示例。
class A {
someInstances: this[] = [];
}
class B extends A {
someProp = 0;
}
const a = new A();
const b = new B();
a.someInstances.push(b);
// This isn't allowed: b.someInstances.push(a);
我想我能够为您的问题提出解决方案。 不幸的是,由于语言限制,它可能不是很优雅,但也不错。
不幸的是,正如其他答案所述,关键字“ this ”不能用作类型,因此不能在 generics 中使用。 在你的情况下,你可以重写你的代码,而不是“ this ”,只是使用当前类型,但是,这不会是你的 Table 中的对象将是相同类型的“保证”,这就是你所描述的有必要的。
不幸的是,在 JavaScript/TypeScript 中,你不能保证任何泛型集合中的对象都是“通过类型化”的相同类型,因为 TypeScript 不提供协变、逆变和不变等工具。 您必须使用代码和检查来确保它。 这是一个已知问题,例如在 Promise 中,您可以返回不应该返回的类型。 (至少这是我现在所知道和发现的,不是 100% 确定的)
要创建一个所有成员都属于同一类型的不变表,我们必须检查每个输入的元素。 我提出了一种可能的 model,其中每个表都接受一个用户定义的 function,它检查哪些类型可以被允许,哪些类型被禁止:
interface TypeGuard<T>
{
(inputObject: T): boolean;
}
// Generic part
class SingleTypeTable<T>
{
private typeGuard: TypeGuard<T>;
constructor(typeGuard: TypeGuard<T>)
{
this.typeGuard = typeGuard;
}
Add(item: T)
{
//Check the type
if (!this.typeGuard(item))
throw new Error("I do not like this type");
//...
}
}
人员守卫的工作方式如下:
const personGuard: TypeGuard<Person> = function (person: Person): boolean
{
return person instanceof Person;
}
personGuard(new Person(...)); //true
personGuard("string" as any as Person); //false
现在您可以按如下方式创建模型和人物:
// Some abstract model
abstract class Model<T>
{
constructor(
public readonly id: number,
public table: SingleTypeTable<T> //Table of models
)
{
}
}
// Example: a table of Person objects
class Person extends Model<Person>
{
constructor(
id: number,
table: SingleTypeTable<Person>,
public name: string
)
{
super(id, table);
}
}
//Usage
const personTable = new Table<Person>(personGuard);
const person = new Person(0, personTable , 'John Doe');
我知道我可能稍微改变了你的 model 结构,但我不知道你的整体情况,我相信如果你喜欢这个解决方案,你可以根据自己的喜好进行更改,这只是一个原型。
我希望这是你需要的。
我的这部分答案试图解释我为什么不能在构造函数中使用“this”关键字作为参数类型的理论。
首先,您不能在 function 中使用“this”作为类型。 你不能做这个:
function foo(a: this) {} //What is "this"? -> Error
在我进一步解释之前,我们需要回到简单的 JavaScript。 创建 object 的方法之一是“实例化函数”,如下所示:
function Animal(name) { this.name = name; }
var dog = new Animal("doggo");
这几乎正是 TypeScript 类编译成的内容。 你看,这个 Animal object 是一个 function,但也是 Animal object 的构造函数。
那么,为什么不能在 TypeScript 构造函数中使用“this”关键字呢? 如果你看上面,构造函数被编译成function,构造函数参数只是function的一些参数,这些不能有“this”的类型。
但是,即使是构造函数参数,TypeScript 编译器也可能能够找出“this”类型。 对于 TypeScript 团队来说,这无疑是一个很好的建议。
Type<ContentType>
注释仅用于类型,意味着Table<this>
无效,因为this
始终引用实例而不是类/类型/接口等。意味着:将this
作为参数传递是有效的在table.instances.set(id, this);
,但Table<this>
不是,您应该将其更改为Table<Model>
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.