繁体   English   中英

Typescript 在构造函数中使用“this”类型

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM