This video was the matrix "I know kungfu" moment of NoInfer for me. The name 'NoInfer' makes sense because in my head it reads as "don't use as source of truth if it could be multiple types".
Your second example is a great use of distributed conditionals. I am excited to take this to my own projects where I want to adjust parameters based on T.
It becomes any because types become distributive when the conditional type is given a union in the generic. So the type becomes (data:number, payload:string)=>void | (data:number,payload:number)=>void rather than the expected (data:number, payload:number|string)=>void. Because of this and because you dont specify which type it is when you assign the function, typescript can only infer the types as any. To fix this, you wrap the generic and the test type within the conditional type in square brackets, like so: type Test = [T] extends [never] ? ()=>void : (payload: T)=>void; And you will get the desired behavior
Cool video. It was fun seeing you catch the union distribution live like that. One approach to handling conditional types that comes with a very different set of trade offs is creating function overloads that each handle the distinct use cases you’re wanting to support. It can be kinda tricky to get right because you have to think about the order of the overloads a bit, but if you can find an order that feels intuitive, sometimes it works out better than you’d hoped Cheers
I have also used the strategy of returning a function that you immediately call, and it was also in order to pass types that can't be inferred from arguments but also utilizing inference for everything else. I now feel validated in that decision, thank you.
In the case of the type of your 'do' function, I find the second solution (the one you deleted), to be actually more elegant, because you're only determining the type of args, where in the solution you have, you have 2 completely different functions. The result is the same of course, but I'm a DRY kind of guy, so if I can specify the return type only once instead of twice, I wille.
This was such an awesome video. I will need to see it several times. That requirement to wrap things in array left me scratching my head. And the `any` thing is very annoying, it is usually when you have any intersection with an unknown (or was it a never?? 😅)
@@Danielo515 ts doesn't compare things like you as an exact match so you need the array to make sure it doesn't spread/distribute the union type BASIC = "hi" extends string ? true : false; // ^? type EXACT = ["hi"] extends [string] ? true : false; // ^? for example "hi" extends string so it'll be true but if you actually wanted to check the exact string you would need to wrap it in a tuple
Didn't like it as it was less "followable" then usually. Usually there is something to repeat after Andrew and here it was just a message that would get forgotten quickly.
You can just do `do?: (data: TData, payload: TPayload) => void;` I don't think you actually need to check the value since ts would complain if you tried to use never anyways
This video was the matrix "I know kungfu" moment of NoInfer for me. The name 'NoInfer' makes sense because in my head it reads as "don't use as source of truth if it could be multiple types".
Your second example is a great use of distributed conditionals. I am excited to take this to my own projects where I want to adjust parameters based on T.
Having to return a function just so your top level generics can be evaluated for inference has made top level constraints so annoying to work with.
It becomes any because types become distributive when the conditional type is given a union in the generic.
So the type becomes (data:number, payload:string)=>void | (data:number,payload:number)=>void rather than the expected (data:number, payload:number|string)=>void. Because of this and because you dont specify which type it is when you assign the function, typescript can only infer the types as any.
To fix this, you wrap the generic and the test type within the conditional type in square brackets, like so: type Test = [T] extends [never] ? ()=>void : (payload: T)=>void;
And you will get the desired behavior
Great as always! You should do some more live TS coding! Always cool to see super smart people like yourself learning things on the fly
Cool video. It was fun seeing you catch the union distribution live like that.
One approach to handling conditional types that comes with a very different set of trade offs is creating function overloads that each handle the distinct use cases you’re wanting to support.
It can be kinda tricky to get right because you have to think about the order of the overloads a bit, but if you can find an order that feels intuitive, sometimes it works out better than you’d hoped
Cheers
I have also used the strategy of returning a function that you immediately call, and it was also in order to pass types that can't be inferred from arguments but also utilizing inference for everything else. I now feel validated in that decision, thank you.
Was really cool watching you figure it out live
my goal is to write types like this
You’ll get there. Just keep going. Reading code above your skill level helps too.
In the case of the type of your 'do' function, I find the second solution (the one you deleted), to be actually more elegant, because you're only determining the type of args, where in the solution you have, you have 2 completely different functions. The result is the same of course, but I'm a DRY kind of guy, so if I can specify the return type only once instead of twice, I wille.
This was such an awesome video. I will need to see it several times. That requirement to wrap things in array left me scratching my head. And the `any` thing is very annoying, it is usually when you have any intersection with an unknown (or was it a never?? 😅)
Thanks Andrew!
New video, new mustache 😁
Here's a version of createMachine that doesn't need double dispatch to infer the type of the state machine tinyurl state-machine-strongly-typed
When did you change your type definition 😮
awesome video thanks a lot
was literally about to comment that you need to wrap the payload and never in array 😂
But... why?
@@Danielo515 ts doesn't compare things like you as an exact match so you need the array to make sure it doesn't spread/distribute the union
type BASIC = "hi" extends string ? true : false;
// ^?
type EXACT = ["hi"] extends [string] ? true : false;
// ^?
for example "hi" extends string so it'll be true but if you actually wanted to check the exact string you would need to wrap it in a tuple
@@obifortune that makes sense. Thank you for the explanation
Didn't like it as it was less "followable" then usually.
Usually there is something to repeat after Andrew and here it was just a message that would get forgotten quickly.
Good feedback, thanks!
Didn't get it...
it's clearer on second view
Good, but it's easy!!!
You can just do `do?: (data: TData, payload: TPayload) => void;` I don't think you actually need to check the value since ts would complain if you tried to use never anyways
But then, I think you would always need to provide a function that takes two args, even when you can't use the second one, right?
@@andrew-burgess No, you can leave the argument omitted (this is true for any function type). However, you will always have to pass a parameter
@@lengors7327 yesss. you don't actually have to "use/accept" the arg so it can actually be ignored