Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/typescript/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/file/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Typescript 使状态管理系统类型安全时的循环参考_Typescript_Circular Reference_State Management - Fatal编程技术网

Typescript 使状态管理系统类型安全时的循环参考

Typescript 使状态管理系统类型安全时的循环参考,typescript,circular-reference,state-management,Typescript,Circular Reference,State Management,我在一个旧项目中的状态管理系统使用MobX。最近我想让它与SSR一起工作(因为我在新项目中成功地使用了SSR) 这个想法是要有一个管理所有商店的商店经理,商店也可以访问该经理,以便能够读取和修改其他商店。这在JavaScript中运行良好,但TypeScript使其成为一个问题 我已设法将问题隔离到一个可复制的示例中。您可以在TypeScript游乐场中运行此命令来查看问题 /** * The manager holds all the stores in the application *

我在一个旧项目中的状态管理系统使用MobX。最近我想让它与SSR一起工作(因为我在新项目中成功地使用了SSR)

这个想法是要有一个管理所有商店的商店经理,商店也可以访问该经理,以便能够读取和修改其他商店。这在JavaScript中运行良好,但TypeScript使其成为一个问题

我已设法将问题隔离到一个可复制的示例中。您可以在TypeScript游乐场中运行此命令来查看问题

/**
 * The manager holds all the stores in the application
 */
class StoreManager<T extends Record<string, InitializableStore>> {
  public stores: T = {} as any

  constructor(
    public instantiators: { [K in keyof T]: (manager: any) => T[K] },
  ) {
    for (const [name, creator] of Object.entries(instantiators)) {
      this.stores[name as keyof T] = creator(this)
    }
  }

  public async init() {
    console.info("Initializing stores")
    await Promise.all(Object.values(this.stores).map((x) => x.init()))
  }
}

export type Manager = StoreManager<Stores>

/** 
 * This class represents a store which should have access to the manager
 */

class InitializableStore {
  constructor(protected manager: Manager) {}

  public init(): void | Promise<void> {}
}


/** 
 * Helper function for creating a store factory
 */
const createStoreFactory = <S extends InitializableStore>(
  storeClass: new (manager: Manager) => S,
) => (manager: Manager) => new storeClass(manager)


/**
 * Example store set up
 */

class StoreA extends InitializableStore {
  public init() {}

  public meow() {
    console.log("Meow")
  }
}

class StoreB extends InitializableStore {
  public init() {
    const { storeA } = this.manager.stores
    storeA.meow()
  }

  public woof() {
    console.log("Woof!")
  }
}

const storeA = createStoreFactory(StoreA)
const storeB = createStoreFactory(StoreB)

/**
 * Defining the stores for the manager here
 * */
const stores = { storeA, storeB }

export type StoreMapReturn<
  T extends Record<string, (manager: Manager) => InitializableStore>
> = {
  [K in keyof T]: ReturnType<T[K]>
}

/**
 * This errors, because there's a circular reference
 */
export type Stores = StoreMapReturn<typeof stores>
/**
*经理持有应用程序中的所有存储
*/
班级经理{
公共存储:T={}如有
建造师(
公共实例化器:{[K in keyof T]:(manager:any)=>T[K]},
) {
for(对象项(实例化器)的常量[名称,创建者]){
this.stores[name as keyof T]=创建者(this)
}
}
公共异步初始化(){
console.info(“初始化存储”)
wait Promise.all(Object.values(this.stores).map((x)=>x.init())
}
}
导出类型管理器=StoreManager
/** 
*此类表示应该可以访问manager的存储
*/
类初始化存储{
构造函数(受保护的管理器:管理器){}
public init():void | Promise{}
}
/** 
*用于创建存储工厂的帮助器函数
*/
常量createStoreFactory=(
storeClass:new(经理:经理)=>S,
)=>(经理:经理)=>new storeClass(经理)
/**
*示例商店设置
*/
类StoreA扩展了InitializableStore{
公共init(){}
公共喵喵{
控制台日志(“喵喵”)
}
}
类StoreB扩展了InitializableStore{
公共init(){
const{storeA}=this.manager.stores
猫咪
}
公网{
console.log(“Woof!”)
}
}
const storeA=createStoreFactory(storeA)
const storeB=createStoreFactory(storeB)
/**
*在此处为经理定义存储
* */
常量存储={storeA,storeB}
导出类型StoreMapReturn<
T扩展记录初始化存储>
> = {
[K in keyof T]:返回类型
}
/**
*这是一个错误,因为有一个循环引用
*/
导出类型存储=StoreMapReturn
由于商店需要访问管理器,因此类型非常复杂,实际上无法工作,因为存在循环引用。在完美的情况下,它的工作原理如下:

  • 可以在任何商店访问管理器
  • 管理器不是从文件导入的全局对象(因此可以动态创建并完全封装)
  • 从管理器访问时,这些存储是完全类型安全的

基本上,编译器必须推断
存储类型的以下链:

type Stores = typeof stores > createStoreFactory > Manager > StoreManager<Stores> > Stores 
//        ^                          circular                                      ↩
如果对每个商店来说,这太重复了,您可以用自顶向下的方法()首先定义
商店


将语言标记添加到要获取的问题attention@MannojTypeScript实际上是第一个标记。。。
type StoreFactory<T extends InitializableStore> = (manager: Manager) => T

const storeA: StoreFactory<StoreA> = createStoreFactory(StoreA)
const storeB: StoreFactory<StoreB> = createStoreFactory(StoreB)
export type Stores = {
  storeA: StoreA;
  storeB: StoreB;
}
export type StoreFactories = { [K in keyof Stores]: (manager: Manager) => Stores[K] }

const storeA = createStoreFactory(StoreA)
const storeB = createStoreFactory(StoreB)
const stores: StoreFactories = { storeA, storeB }