首页 > 解决方案 > 使用 TypeScript 描述在赋值时更改其属性类型的代理对象

问题描述

设置

事实证明,向 TypeScript 编译器描述代理对象的工作方式有点挑战性。以带有 setter 的 Proxy 的情况为例,该 setter 将输入中的任何对象也转换为递归代理它们,TypeScript 理解这一点并允许直接分配而不需要首先强制最终递归代理会非常好目的。这是一个示例,它也在TypeScript 操场上运行

interface SimpleObject {
  [index: string]: any
}

type ProxyObject<T> = {
  [K in keyof T]: T[K] extends SimpleObject ? ProxyObject<T[K]> : T[K]
} & {
    isProxy: true
}

function createProxy<T extends SimpleObject>(obj: T): ProxyObject<T> {
  const proxyObject = Object.create(obj, {})
  for (const key in obj) {
    proxyObject[key] = (typeof obj[key] === 'object') ? createProxy(obj[key]) : obj[key]
  }

  return new Proxy(proxyObject, {
    get (...args) {
      // some side effects here perhaps
      console.log(`fetchin value for ${args[2]}`)
      return Reflect.get(...args);
    },
    set (target, property, value) {
        const setTo = (typeof value === 'object' && !value.isProxy) ? createProxy(value) : value
        return Reflect.set(target, property, setTo)
    }
  })
}

const proxied = createProxy({
  text: 'hello world',
  nested: {
    money: 345
  }
})

// This works because it's initially declared as a string
proxied.text = 'Another value'
// This wont work because text is a string, this is good!
// proxied.text = 123

// This will work!
proxied.nested.money = 123

// This works
proxied.nested = createProxy({ money: 555 })

// This will not work because
// > Type '{ money: number; }' is not assignable to type 'ProxyObject<{ money: number; }>'
proxied.nested = { money: 555 }

问题

理想情况下,您将有一种方法可以通知 TypeScript 编译器proxied.nested将自动转换为正确的类型(即ProxyObject<{ money: number}>)。您可以在 ProxyObject 定义中使用联合运算符来允许这样做:

type ProxyObject<T> = {
  [K in keyof T]: T[K] extends SimpleObject ? ProxyObject<T[K]> | T[K] : T[K]
} & {
    isProxy: true
}

但这并不能真正解决proxied.nested 更改输入类型的问题,相反,所有对象ProxyObject都不再确定它们是 SimpleObject 还是 ProxyObject,因此您必须不断地嗅出它。

问题: 有什么方法可以告诉 TypeScript 这个属性可以分配一个联合(多种类型),但读取时它的值始终是单一类型?

标签: typescript

解决方案


推荐阅读