I have the following function
function fun<T extends boolean>(param: T): void {
param = true
}
When I assign param
to true
, the compiler gives me the following error:
Type 'boolean' is not assignable to type 'T'.
'boolean' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'boolean'.ts(2322)
Why can't I assign it? T
is (or at least extends) boolean
, so it should work.
There is a workaround to add <T>
before true
or as T
after true
, but that's just annoying to do all the time.
It's also more annoying when you try to add a default value to param
:
function fun<T extends boolean>(param: T = true): void
Now there is an error on param: T = true
which is the same exact one as the previous error.
Why can't I just assign to param
without explicitly type-asserting (adding <T>
or as T
) the value?
I have the following function
function fun<T extends boolean>(param: T): void {
param = true
}
When I assign param
to true
, the compiler gives me the following error:
Type 'boolean' is not assignable to type 'T'.
'boolean' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'boolean'.ts(2322)
Why can't I assign it? T
is (or at least extends) boolean
, so it should work.
There is a workaround to add <T>
before true
or as T
after true
, but that's just annoying to do all the time.
It's also more annoying when you try to add a default value to param
:
function fun<T extends boolean>(param: T = true): void
Now there is an error on param: T = true
which is the same exact one as the previous error.
Why can't I just assign to param
without explicitly type-asserting (adding <T>
or as T
) the value?
1 Answer
Reset to default 1Basically, just because you have two different sub types does not mean you can assign one to other.
true
and false
are subtypes, but if a value explicitly expects the value false, you cannot assign true right.
It is a little difficult to find practical examples with primitives, but with objects it does make a lot of sense. And, in JS anything can be an object :D.
I hope the below snippet is helpful. Here we have both kinds of example. Please check the comments too:
function fun<T extends boolean>(param: T): void {
param = true //The error
}
const val: false = false; //val can only be false
type PrimitiveWithRandomFunc = false & { func? : () => void };
type ExtendsWrong = PrimitiveWithRandomFunc extends boolean ? true : false;
let m :false & { func? : () => void } = false; //m can only be false and have a func in addition
m.func = () => {
console.log("yolo");
}
fun(val) //no error, although we are assigning true to val, which should be false
fun(m) //no error
If one is to create a type like PrimitiveWithRandomFunc
, which is possible in JavaScipt, then the above error makes sense.
ExtendsWrong
is true
type, which means the extension constraint is match. But just because it is matched does not mean that we can assign a different subtype to param
.
Notice how fun(m)
does not give any error. I am using func
as optional property to get rid of some TS errors, but I hope it gets the point across. Basically you would be assigning true to something that only expects fale and some other things.
And for val
I hope it is clearly apparent, we are simply able to assign true
to something that should be false
.
Playground
fun<false>()
, and then you've assignedtrue
tofalse
. You're presumably looking for ms/TS#58977 and since that feature doesn't exist, there are only workarounds, none of which are perfect. You can usetrue as T
and just assume nobody will explicitly instantiatefalse
as the type argument, or use overloads. Does this fully address the question? If so I'll write an answer or find a duplicate. If not, what's missing? – jcalz Commented Feb 16 at 14:28