【问题标题】:Typescript init object with generic具有泛型的打字稿初始化对象
【发布时间】:2021-07-24 13:35:20
【问题描述】:

我想要的是这样的:

class ThemeExample <T> {
    static theme : T;
    static init(theme : T){
        ThemeExample.theme = theme;
    }
};

//config it 
ThemeExample.init({
    hello : 'hi'
})
//in another file access properties with auto completion
ThemeExample.theme.hello

但这不起作用。 TS2302: Static members cannot reference class type parameters.

尝试了其他一些方法,例如在函数中创建类并返回它,并在类中使用泛型函数。 仍然找不到让我初始化此配置一次的方法。 然后稍后访问主题并自动完成其他文件中的所有属性。 我需要这样的东西,请帮帮我,我太沮丧了。

【问题讨论】:

  • 我怀疑你会得到比a factory function where you export what it returns 更好的东西。您不能轻易地改变单个变量的状态,即使您这样做了,也没有任何机制可以让这种状态更改跨文件边界持续存在;您需要导出const“配置版本”并将其导入不同的文件中。如果满足您的需求,我可以写一个答案;否则我只能想象一个“这是不可能的,对不起”的答案。
  • @jcalz 感谢您的快速响应,现在您在同一个文件中拥有函数和导出 const。是否可以这样做,但不是在同一个文件中创建和导出,而是在一个文件并导出到另一个文件中?因为这个类最终应该进入一个模块,但是初始化必须留在用户端。
  • ?是的,TS Playground 只支持一个文件。如果您想向我提供一个指向我可以 fork 和修改的 minimal reproducible example Web IDE 项目的链接,我会向您展示。您可以在模块中导出工厂函数,在另一个文件中可以导入工厂、创建类并导出该类,在第三个文件中您可以导入创建的类。但为了显示这一点,我需要一个多文件 IDE 链接
  • 谢谢! codesandbox.io 可以完成这项工作,但无论如何,如果您发布示例代码作为答案就足够了。

标签: typescript generics types typescript-generics


【解决方案1】:

class 的静态部分由类构造函数组成,并且只有一个这样的构造函数。它无法访问实例类型上的任何泛型参数,因为类构造函数需要能够为该泛型参数的任何可能规范创建实例。在您的ThemeExample&lt;T&gt; 代码中,类构造函数必须能够创建ThemeExample&lt;string&gt;ThemeExample&lt;number&gt; 的实例。如果您可以拥有T 类型的静态属性,则意味着它必须是bothstringnumber 以及T 的任何其他可能类型。它不起作用。

从概念上讲,您可以将ThemeExample 想象为具有名为themeunknown 类型的static 属性,然后当您调用ThemeExample.init("hello") 时,编译器将narrow ThemeExample 类构造函数使theme 现在是 string 类型,而不是 unknown。但是没有很好的机制来做到这一点。你可以想象写像assertion function 这样的东西会产生这样的效果:

interface ThemeExampleConstructor<T = unknown> {
  theme: T;
  init<T>(theme: T): asserts this is ThemeExampleConstructor<T>;
  new(): ThemeExample<T>;
}

declare const ThemeExample: ThemeExampleConstructor;

ThemeExample.theme.toUpperCase(); // error
ThemeExample.init("hello");
ThemeExample.theme.toUpperCase(); // okay
new ThemeExample().instanceProp.toUpperCase(); // okay

但是你不能用class 语法来做到这一点,即使你完成了这个,像这样的缩小也不能跨越文件边界甚至函数边界。没有任何机制可以让编译器看到一个文件中的ThemeExample.init("hello") 发生在另一个文件中的ThemeExample.theme.toUpperCase() 之前。所以我们根本做不到。


相反,我建议采用可生成新类构造函数的工厂函数。这个函数可能存在于某个库中,比如 Library/index.ts:

export function initThemeExample<T>(theme: T) {
    return class ThemeExample {
        static theme = theme;
    };
}

然后在像 userLib.ts 之类的另一个文件中,用户可以 import 从库中配置并重新导出:

import {initThemeExample} from './Library'

//config it 
export const ThemeExample = initThemeExample({
    hello: 'hi'
})

然后用户可以在所有其他文件中使用这个配置的构造函数:

import {ThemeExample} from './userLib'

//import your ThemeExaple in another file
document.write(ThemeExample.theme.hello.toUpperCase()); // HI

Link to StackBlitz code

【讨论】:

  • 很失望,经过一天的尝试,这是不可能的。但是您的替代方案非常出色且富有创意。我使用它是因为它是当前可用的最佳选择。非常感谢。
猜你喜欢
  • 1970-01-01
  • 2021-08-15
  • 1970-01-01
  • 2020-07-26
  • 1970-01-01
  • 1970-01-01
  • 2020-10-11
  • 2017-09-06
  • 2019-01-16
相关资源
最近更新 更多