问题
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