Reputation: 948
I have the following javascript function that returns a function containing additional methods given in the object arguments.
I think the code will be more understandable than an explanation:
var scopeFunction = (object) => {
// create main function if it doesn't exist
if (!object.main) object.main = function(){
alert("No main function for this scopeFunction");
};
// add all object keys to the main function that will be return
_.each(object, function(d,i){ object.main[i] = d; });
// return main function
return object.main;
};
I want to define properly this code in typescript, this is what I did, but atom typescript (that's where I've tested it) throws errors when I try to access the keys of my returned function object. This is how my current code look:
// TYPES
namespace ScopeFunction {
export interface Function<Obj extends MakerObject, Key extends keyof Obj> {
(): Obj["main"];
[key: Key]: Obj[Key];
}
export interface MainFunction {
(...args:any[]) : any;
}
export interface MakerObject {
[key: string]: any;
main?: MainFunction;
}
export type Maker = <Obj extends MakerObject, Key extends keyof Obj>(object:Obj) => ScopeFunction.Function<Obj, Key>;
}
// FUNC
var scopeFunction:ScopeFunction.Maker = (object) => {
// create main function if it doesn't exist
if (!object.main) object.main = function(){
alert("No main function for this scopeFunction");
};
// add all object keys to the main function that will be return
_.each(object, function(d,i){ object.main[i] = d; });
// return main function
return object.main;
};
// TEST
var test = scopeFunction({
a: 1,
b: "3",
main: () => { console.log("testLog"); return 0; }
})
var test1 = test(); // WORKS OK
var test2 = test.main(); // ALERT: Property 'main' doesn't exist on type 'Function<{ a: number; b: string; main: () => number }, "main" | "a" | "b">'
var test3 = test.a; // ALERT: Property 'a' doesn't exist on type 'Function<{ a: number; b: string; main: () => number }, "main" | "a" | "b">'
Any idea where the problem is in my definition?
Upvotes: 1
Views: 971
Reputation: 249666
There are several problems with your code:
[key: Key]: Obj[Key]
is not valid, and indexer argument must be either number
or string
(those and only those types are valid). You need to use a mapped type instead.(): Obj["main"]
will not be a call signature of the same type as Obj["main"]
is will be a function that returns whatever the property of main
is.main
function is too generic, and it will not preserve any argument types. A solution that does what you expect might be:
namespace ScopeFunction {
export type Function<Obj extends MakerObject<(...args: any[]) => any>> = Obj['main'] & {
[P in keyof Obj]: Obj[P];
}
export interface MakerObject<TMain extends (...args: any[]) => any> {
main?: TMain;
}
export type Maker = <TObj extends MakerObject<(...args: any[]) => any>>(object: TObj) => ScopeFunction.Function<TObj>;
}
// FUNC
var scopeFunction: ScopeFunction.Maker = (object) => {
// create main function if it doesn't exist
if (!object.main) object.main = function () {
alert("No main function for this scopeFunction");
};
// return main function
return Object.assign(object.main, object);
};
// TEST
var test = scopeFunction({
a: 1,
b: "3",
main: (param: number) => { console.log("testLog"); return param; }
})
var test1 = test(10);
var test2 = test.main(10);
var test3 = test.a;
Upvotes: 2