簡體   English   中英

如何將原型分配給泛型類型

[英]How to assign prototype to generic type

我正在嘗試構建一個 sessionStorage 服務,該服務可以將對象字符串解析為通用類型。 但是在這個實現中,它只返回一個沒有原型的對象(沒有對象函數)。

這是代碼:

public static getObject<T>(key: string): T{

    return JSON.parse(sessionStorage.getItem(key)) as T;
}

問候!

JSON 不編碼函數、原型或構造函數。 解析后的 JSON 永遠不會為您創建任何類的實例。 它只是原始的結構化數據。 這對於您可能序列化 javascript 數據的大多數方式都是正確的。

沒有什么好的方法可以解決這個問題。 由您來構建您的代碼庫,以便在考慮到這一點的情況下工作。

對此有無數的解決方案,但例如,您可以在獲取此數據后將其傳遞給構造函數。

像這樣的東西:

const data = sessionStorageInstance.getObject<MyInterfaceHere>('some/key')
const instance = new MyClassHere(data)

我假設您使用的代碼有點像這樣?

class SessionStorageManager {
    public static getObject<T>(key: string): T{
        return JSON.parse(sessionStorage.getItem(key)) as T;
    }
}

const test = SessionStorageManager.getObject('test');

在這個例子中, T在定義getObject地方沒有設置默認值,而且我在調用getObject時也沒有設置類型,所以根據 TypeScript 的test類型是unknown

如果您想確保getObject返回的任何內容都是Object類型,那么您將需要在創建泛型函數時使用extends來向 TypeScript 解釋這一點。 例如:

public static getObject<T extends object>(key: string): T{
    return JSON.parse(sessionStorage.getItem(key)) as T;
}

但是,請注意這里的as的使用本質上是繞過類型檢查。 僅當您確信您將始終從JSON.parse獲取對象時才執行此操作。 請記住,在JSON.parse('2')JSON.parse("string")等情況下,您仍然可以從中獲取原語,如果 JSON 字符串格式錯誤,則JSON.parse將拋出錯誤。

如果你想要類型安全,那么你需要添加一個類型檢查來支持它as 類型斷言 根據您使用此代碼的方式,您可能需要在getObject函數之外執行此操作,因為您現在可能知道可能需要使用什么自定義類型保護來確定對象是否確實屬於給定類型的T

為了確保必須使用這些類型保護,您可能希望讓getObject返回類型unknown ,這意味着 TypeScript 不會讓您對類型做出任何假設,直到您完成某種類型縮小。 例如:

class SessionStorageManager {
    public static getObject(key: string): unknown {
        return JSON.parse(sessionStorage.getItem(key));
    }
}

const obj = SessionStorageManager.getObject('test'); // type unknown

if (typeof obj === 'object') {
    // type object
}

不幸的是,泛型類型僅作為某種模型設計輔助出現在 TypeScript 中。 它永遠不會被編譯成 JavaScript 文件。 以你的 sessionStorage 服務為例:

// SessionStorageService.ts
class SessionStorageService {
  public static getObject<T>(key: string): T{
    return JSON.parse(sessionStorage.getItem(key)) as T;
  }
}

如果我們使用tsc上面的 TypeScript 文件編譯成 JavaScript,我們可以發現T被完全刪除了:

// SessionStorageService.js
var SessionStorageService = /** @class */ (function () {
    function SessionStorageService() {
    }
    SessionStorageService.getObject = function (key) {
        return JSON.parse(sessionStorage.getItem(key));
    };
    return SessionStorageService;
}());

反序列化返回帶有類/原型的對象,需要在序列化時保存類/原型信息。 否則,這些類/原型信息將丟失。

我創建了一個名為esserializer的 npm 模塊來自動解決這個問題:在序列化期間以純 JSON 格式保存 JavaScript 類實例值及其類名信息。 稍后,在反序列化階段(可能在另一個進程或另一台機器上),esserializer 可以遞歸地反序列化對象實例,保留所有類/屬性/方法信息,使用相同的類定義。 對於您的 SessionStorageService,它看起來像:

// SessionStorageService.ts
const ESSerializer = require('esserializer');
ESSerializer.registerClasses([ClassA, ClassB]); //register all classes possibly involved in serialization.

class SessionStorageService {
  public static getObject(key: string){
    return ESSerializer.deserialize(sessionStorage.getItem(key));
  }
}

// Previously, set sessionStorage in another file
const ESSerializer = require('esserializer');
sessionStorage.setItem(key, ESSerializer.serialize(anObj));

您可以使用Object.create創建一個具有正確原型的空對象,並使用Object.assign添加來自 JSON 數據的所有屬性。

請注意,調用此函數時需要將cls作為參數傳遞,否則無法知道要將哪個類設置為原型。 如果調用代碼並不一定知道對象應該是哪一類,你能想出一些方法來在你的編碼這個key (例如,而不是someKey你可以把它YourClass-someKey ,然后有一個類的注冊表,以便您可以按名稱查找YourClass )。

type ClassOf<T> = new(...args: any[]) => T

function makeObject<T>(cls: ClassOf<T>, json: string): T {
    const obj: T = Object.create(cls.prototype);
    Object.assign(obj, JSON.parse(json));
    return obj;
}

用法示例:

class Kitten {
    constructor(public name: string) {}
    meow(): void { console.log(this.name + ' says meow!'); }
}

// const mittens: Kitten
const mittens = makeObject(Kitten, '{"name": "Mittens"}');
// Mittens says meow!
mittens.meow();

游樂場鏈接

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM