If I have function definition like this:
type TestFunc<ARGS> = (args: ARGS) => ARGS;
then in the implementation I can implement it as following:
const testFunc: TestFunc<number> = (args) => {
return args;
};
But if I have that function definition inside interface like this:
interface TestInterface {
testFuncInObj: <ARGS>(args: ARGS) => ARGS;
}
How do I pass number instead of ARGS generic type in the below implementation?
const testObj: TestInterface = {
testFuncInObj: (args) => args,
};
If the type definition was like this:
interface TestInterface<ARGS> {
testFuncInObj: (args: ARGS) => ARGS;
}
I could implement is as following:
const testObj: TestInterface<number> = {
testFuncInObj: (args) => args,
};
But it is implemented like this one and I cannot change it.
interface TestInterface {
testFuncInObj: <ARGS>(args: ARGS) => ARGS;
}
If I have below implementation. TS will complain that data does not exist in args.
const testObj: TestInterface = {
testFuncInObj: (args) => {
return {
...args,
data: args.data,
}
},
};
There are two flavors of generics in TypeScript, which differe in the scope of the generic type parameter. There are generic types, like this:
type TestFunc<T> = (args: T) => T;
where the supplier of the value chooses the type argument, and there are generic functions, like this:
type TestFuncGen = <T>(args: T) => T;
where the caller of the function chooses the type argument. These are related but distinct types. Essentially TestFuncGen
acts like TestFunc<T>
for every possible T
. So a TestFuncGen
must be a valid TestFunc<string>
and a valid TestFunc<number>
and a valid TestFunc<{data: object}>
, etc., etc. Inversely, a TestFunc<T>
for any given T
is very unlikely to be a valid TestFuncGen
.
You cannot simply move the scope of the generic type parameter without fundamentally changing who is in control of the generic type argument.
Your TestInterface
is equivalent to
interface TestInterface {
testFuncInObj: TestFunc2;
}
and so you cannot simply supply a TestFunc<{data: object}>
there. You have to provide a function that accepts any possible input type and outputs the same type. The canonical implementation of that is just the identity function:
const testObj: TestInterface = {
testFuncInObj: (args) => args
};
If you want to change that implementation so it inspects args
and returns something else, you can do so, but TypeScript needs to believe that the return type is a subtype of the generic type of args
, like
const testObj2: TestInterface = {
testFuncInObj: (args) => args instanceof Object ?
{ ...args, anotherProp: 123 } : args
};