Why this error appears ? Here you have an example:
class StringClass { val: string; constructor(val: string) { this.val = val; } } class NumberClass { val: number constructor(val: number) { this.val = val; } } type Check<A> = A extends string ? typeof StringClass : typeof NumberClass class test<A extends string | number, B extends Check<A>> { val: B; constructor(val: A, Class: B) { // Type 'string' is not assignable to type 'never' this.val = new Class(val); } } const result = new test('2', StringClass)
In above case val infered to never, because
multiple candidates for the same type variable in contra-variant positions causes an intersection type to be inferred.
Hence string & number === never
COnsider this example:
class StringClass { val: { string: string }; constructor(val: { string: string }) { this.val = val; } } class NumberClass { val: { number: number } constructor(val: { number: number }) { this.val = val; } } type Check<A> = A extends string ? typeof StringClass : typeof NumberClass class test<A extends string|number, B extends Check<A>> { val: B; constructor(val: A, Class: B) { // Type 'string' is not assignable to type '{ string: string; } & { number: number; }' this.val = new Class(val); } } const result = new test('2', StringClass)
val is { string: string; } & { number: number; }.
So, how to fix it?
You can get rid of generics and overload your constructor with appropriate restrictions
class StringClass { val: string; constructor(val: string) { this.val = val; } } class NumberClass { val: number constructor(val: number) { this.val = val; } } type Check<A> = A extends string ? typeof StringClass : typeof NumberClass interface Overloading { new(val: string): any new(val: number): any new(val: string | number): any } class test { val: StringClass | NumberClass constructor(val: number, Class: typeof NumberClass) constructor(val: string, Class: typeof StringClass) constructor(val: never, Class: typeof StringClass & typeof NumberClass) { this.val = new Class(val) } } const _ = new test('2', StringClass) // ok const __ = new test(2, StringClass) // expected error
Playground
In general, runtime values can't rely on generic conditions (see Check). It is not safe.
This is why here:
class StringClass { val: string; constructor(val: string) { this.val = val; } } class NumberClass { val: number constructor(val: number) { this.val = val; } } class test<A, B extends { 0: typeof StringClass, 1: typeof NumberClass, 2: never }[A extends string ? 0 : A extends number ? 1 : 2]> { val: B constructor(val: A, Class: B) { this.val = new Class(val) // error } }
you have an error.
YOu can always use type assertion as never in this case:
class StringClass { val: string; constructor(val: string) { this.val = val; } } class NumberClass { val: number constructor(val: number) { this.val = val; } } class test<A, B extends { 0: typeof StringClass, 1: typeof NumberClass, 2: never }[A extends string ? 0 : A extends number ? 1 : 2]> { val: StringClass | NumberClass constructor(val: A, Class: B) { this.val = new Class(val as never) // type asserion } } const _ = new test('2', StringClass) // ok const __ = new test(2, StringClass) // expected error
Is it safe to use type assertion here? I'm not sure. I think it is up to you. If this code is just for testing - use as never or never just like I did.
If this a production code - please use conditional statements