繁体   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