Reputation: 7325
This question is about .d.ts
as declarations for .js
files.
I'm trying to declare a function that has a generic inside a generic but I can't seem to get it right. The function is essentially an Array#forEach
function for objects ({[key: string]: E}
).
I tried the following, but VSCode intellisense doesn't seem to comprehend the types of the property values of the object.
Typings File:
export module Util {
export function forEach<K, T extends { [key: string]: K }>(obj: T, callbackfn: (value: K, key: string, object: T) => void): void;
}
JavaScript Call:
if (undefined) var { Util } = require("./globals");
/** @type {{ [key:string]: number }} */
var obj = {};
Util.forEach(obj, function (value, key, object) { });
Source code of forEach
:
function forEach(obj, callbackfn) {
if (!(obj instanceof Object)) throw Error("Util.forEach called on non object");
for (var key in obj) callbackfn.call(obj, obj[key], key, obj);
}
Upvotes: 2
Views: 307
Reputation: 5400
What you actually want to do is this:
export function forEach<T extends object>(obj: T, callbackfn: (value: T[keyof T], key: keyof T, object: T) => void): void {
if (!(obj instanceof Object)) throw Error("Util.forEach called on non object");
for (var key in obj) callbackfn.call(obj, obj[key], key, obj);
}
const a = {
test: 1,
test2: 2,
test3: 'me'
};
forEach(a, (value, key, obj) => {})
You need only 1 generic parameter! You can infer the rest of types from it
This works even with stricter types:
Update: add d.ts
file:
export module Util {
export function forEach<T extends object>(obj: T, callbackfn: (value: T[keyof T], key: keyof T, object: T) => void): void
}
Upvotes: 1
Reputation: 31835
I have no problems with your code. As you can see, the object properties types show correctly, when imported in a JS file and TS file as well:
Tested with Visual Studio Code 1.43.1 and Node.js 13.11.0
I have uploaded the code here: https://github.com/Guerric-P/typescript-test
Edit:
Now I understand your actual question, here is how you do it:
export module Util {
export function forEach<T, T1 extends keyof T, T2 extends T[keyof T]>(obj: T, callbackfn: (value: T2, key: T1, object: T) => void): void;
}
Which gives the following:
If you want more details on why the key type is string | number
instead of string
, please check this question: Keyof inferring string | number when key is only a string
Upvotes: 2