How do I generically iterate over the properties of an arbitrary object in TypeScript?

对着背影说爱祢 提交于 2021-01-27 17:10:14

问题


This is a pretty common JavaScript pattern:

function mapThruWrapper(module) {
   const replacement = {}

   Object.getOwnPropertyNames(module).forEach(function(key) {
      const val = module[key]

      if (val instanceof Function) {
         replacement[key] = wrapperFunc.bind(null, val)
      } else {
         replacement[key] = val
      }
   })

   return replacement
}

I'm trying to strongly type this in TypeScript, and I've gotten as far as something like the following:

function mapThruWrapper<M extends { [X: string]: unknown }>(module: M): M {
   const replacement: M = {}

   Object.getOwnPropertyNames(module).forEach(function(key) {
      const val = module[key]

      if (val instanceof Function) {
         replacement[key] = wrapperFunc.bind(null, val)
      } else {
         replacement[key] = val
      }
   })

   return replacement
}

Unfortunately, that's still producing errors like:

src/excmd.ts:186:10 - error TS2322: Type '{}' is not assignable to type 'M'.
  '{}' is assignable to the constraint of type 'M', but 'M' could be instantiated with a different subtype of constraint '{ [X: string]: unknown; }'.

186    const replacement: M = {}
             ~~~~~~~~~~~

src/excmd.ts:192:10 - error TS2536: Type 'string' cannot be used to index type 'M'.

192          replacement[key] = buckleScriptErrorTrampoline.bind(null, $val)
             ~~~~~~~~~~~~~~~~

How can I strongly type generic iteration over, and wrapping, of the members of an object like this?


回答1:


I made some adjustments to the original code and added comments to explain:

function mapThruWrapper<M extends { [X: string]: unknown }>(module: M): M {
    // Add "as M" so that the compiler allows us to assign an
    // empty object (which is okay since we're populating all the
    // object's properties before the function returns anyway).
    const replacement: M = {} as M

    // Use "for in" so that the compiler is able to infer
    // that the variable "key" isn't just a string, but is
    // actually a key in module's type.
    for (const key in module) {
        if (module.hasOwnProperty(key)) {
            const val = module[key]

            if (val instanceof Function) {
                // Use "as typeof val" to let the compiler know that the
                // bound function has the same signature as the original
                // function. I'm assuming that's the case here.
                replacement[key] = wrapperFunc.bind(null, val) as typeof val
            } else {
                replacement[key] = module[key]
            }
        }
    }

    return replacement
}


来源:https://stackoverflow.com/questions/59914385/how-do-i-generically-iterate-over-the-properties-of-an-arbitrary-object-in-types

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!