IN order to make it work, you should rewrite your get function. It should be as follow:
function get<TInput>(someClass: IType<TInput>) {
// stub
return someClass;
}
Why ?
This code is coompletely unsafe:
export interface IType<T = any> extends Function {
new(...args: any[]): T;
}
interface IFooBar { }
class FooBar implements IFooBar {
name = 'hello'
}
class Custom {
age: number = 42
}
function get<TInput = any, TResult = TInput>(someClass: IType<TInput>): TResult {
// stub
return someClass as any
}
const result = get<{}, Custom>(FooBar)
result.age // TS think it is number, but it is undefined
Playground
TypeScript allows you to get age property whereas it should be disallowed.
Working code:
export interface IType<T = any> extends Function {
new(...args: any[]): T;
}
interface IFooBar { }
class FooBar implements IFooBar {}
function get<TInput>(someClass: IType<TInput>) {
// stub
return someClass
}
class Baz {
constructor(public foo: IFooBar) {
}
}
export function main1(arg?: FooBar) {
const fooBar = get(FooBar);
arg = arg || fooBar;
return new Baz(arg); // ok
}
export function main1a(arg?: FooBar) {
arg = arg || get(FooBar);
return new Baz(arg); // ok
}
export function main2(arg?: FooBar) {
return new Baz(arg); // expected error
}
Playground
Try to avoid using generics as an explicit return types. It does not work in a way you expect in 80% of cases.
I'm not saying it always does not work, I'm just saying that from my experience - it is a very common error that developers are struggling with.
Let TypeScript do his job, it can infer 95% of return types without helping.
From my experience, you should use explicit return types when you are using recursion.
If you still want to use some generic as a return type, try to overload your function (docs). With overloading, you don't need to use as any type assertion. However there is a drawback.
Remember that I said it is unsafe? So it is true. Function overloads are bivariant. So it is up to you.
I still wonder why are const foobar = get(FooBar) and arg || get(FooBar) has different behaviours
This is your code example:
export function main1a(arg?: FooBar) {
arg = arg || get(FooBar);
return new Baz(arg);
}
Seems that arg variable some how affects second generic argument of get function.
Hover your mouse over get(FooBar), you will see function get<FooBar, FooBar | undefined>(someClass: IType<FooBar>): FooBar | undefined.
Try to write this expression:42 || get(FooBar) , you will get function get<FooBar, 42>(someClass: IType<FooBar>): 42.
Honestly, I'm unable to explain this behavior.