簡體   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