I want to access my method property in object by called this.getMethod
just like below:
function defineObj(params) {
// `enhanced` will add `getMethod` to params.__prop__
return enhanced(params)
}
let obj = defineObj({
methods: {
foo: (num) => num,
bar() {
this.getMethod('foo', 1) // will call as `this.methods.foo(1)`
}
}
})
Now, I am plan to move those codes to Typescript and get type hinting.
when I call this.getMethod('foo', '1')
, I want IDE tell me, I should use number
instead of string
. And I just want to knew how to use Typescript's Type to complete it.
I wrote codes to do that, but failed.
I am trying to define this context of methods by use CtrlContext
. So I can use this.getMethod
in function body of methods.foo
.
type CtrlContext<M> = {
getMethod: <T extends keyof M>(name: T, ...params: M[T] extends (...arg: infer S2) => infer S1 ? S2 : any[] ) => any
}
type Methods<M> = {
[K in keyof M]: (this: CtrlContext<M>, ...params: any) => ReturnType<any>
}
type Ctrls<M> = {
methods: Methods<M>
}
function defineObj<M>(params: Ctrls<M>) {
return params
}
defineObj({
methods: {
foo: (num: number) => num,
foo2: (num1: number, str: string) => num,
bar() {
this.getMethod("foo", ) // Now, the second param is any, not number.
}
}
})
export default {}
Now, I can get type hinting of first parameter, but can't get function parameter hinting after I input first parameter. Is there some way to solve it ?
CodePudding user response:
I did not see definition of getMethod()
method in your code. For type parameters. You can simply define them in the method declaration itself.
getMethod(name: string, value: number) {
}
and then, If you will assign second param as a string while calling. Typescript will give the error.
this.getMethod('foo', '1') ❌
this.getMethod('foo', 1) ✅
CodePudding user response:
I'm not sure of TypeScript's capabilities to do what you want. Someone else might have a better idea.
However, you could always wrap these methods such that it appears as a regular TypeScript function, that way it looks nicer to the outside and can also incorporate type safety.
class Wrapper {
constructor(getMethod) {
this.getMethod = getMethod;
}
foo(n: number) {
this.getMethod("foo", n);
}
// include all other methods here
}
This is obviously a very tedious task and might not be suitable for your use-case, but I thought it was worth mentioning anyway.
You might be able to create a code generation tool for this as well.
CodePudding user response:
I solved by use ThisType
and this example.
type AnyFunction = (...args: any) => any
type CtrlContext<M> = {
getMethod:
<T extends keyof M>(name: T, ...params: Parameters<T extends keyof M ? M[T] extends AnyFunction ? M[T] : never : never>) =>
ReturnType<T extends keyof M ? M[T] extends AnyFunction ? M[T] : never : never>
}
type Methods<M> = {
[T in keyof M]: M[T]
}
type Ctrls<M> = {
methods: Methods<M> & ThisType<CtrlContext<M>>
}
function defineObj<M>(params: Ctrls<M>) {
return params
}
defineObj({
methods: {
foo(num: number) {
return num
},
bar() {
const val = this.getMethod("foo", 1)
// ^ (property) getMethod: <"foo">(name: "foo", num: number) => number
}
}
})