Как в общем перебрать свойства произвольного объекта в TypeScript? - PullRequest
2 голосов
/ 26 января 2020

Это довольно распространенный шаблон JavaScript:

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
}

Я пытаюсь строго напечатать это в TypeScript, и я получил что-то вроде следующего:

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
}

К сожалению, это все еще выдает ошибки вроде:

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)
             ~~~~~~~~~~~~~~~~

Как я могу строго набрать generi c итерацию и обтекание членов объекта, например это?

1 Ответ

0 голосов
/ 26 января 2020

Я внес некоторые коррективы в исходный код и добавил комментарии для объяснения:

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
}
...