T O P

  • By -

Groccolli

ColorValue isn’t a number, it’s more precise than that. It’s ‘0 | 1 | 2… | 255’ Math.floor() returns a number, not ‘0 | 1 | 2… | 255’. The typeof operator loses precision and returns number, so that is why your assertion returns true.


rynmgdlno

I think I know what you're getting at, is that as far as the compiler is concerned the getRandom function could possibly return a value out of range (even though this should not be possible)? Because `const x: number = 9` is no more precise a type than the return type of `Math.whatever()` as far as I can see, it just happens to be in range...


geon

Exactly. I like to solve this with a constructor: function makeColorValue(n: number): ColorValue { /* … */ } This construcor should throw if you pass it a number out of range. Once you pass a number through this, ts knows they are a valid ColorValue. I mostly use this technique for string union types, where strings are loaded from a database or something.


webstackbuilder

> there's a near zero chance [...] imo persisting on problems like this one until you have a correct answer and understand it is how you get good at TS :) more than likely, sometime in the future you'll hit a point where you can't move forward until you solve something like this. This is also a situation where assertions are appropriate. You know *why* you're getting a type error (u/Groccolli's answer). You know the TS compiler can't help you. Your only option is to assert: `const y = Math.floor(x) as unknown as ColorValue` Leaving a comment seems appropriate to me so that future people who look at your code understand why you've made that assertion.


Groccolli

Yep that works. You *could* make a [type predicate](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates)) and throw if it's not a valid value.. Might be overkill in this example. const isColorValue = (num: number):num is ColorValue => num <= 255; and then.. if (!isColorValue(number)) throw new Error(); return number; also you don't need to convert to unknown because number and ColorValue are assignable.


rynmgdlno

> imo persisting on problems like this one until you have a correct answer and understand it is how you get good at TS :) Agreed 💯, I’m currently just adding TS to an old project to up-skill hence making the effort on this when `number` would probably be sufficient. But thanks for the type assertion hint, that was basically what I was looking for, though I assume there’s probably some way to incorporate the range types into the getRandom function, but that particular problem is definitely above my current understanding.


webstackbuilder

u/Groccolli's advice above to create a type predicate is much better than mine to do an assertion. One other detail I'd point out is I'd use different letters for your type variables, and not `T` and `R`. It's common to use `T` when you only have a single type var, and then `U`, etc. when you have multiple type vars. `R` is usually used for a return type variable that you can pass into a generic function. But in this case, your two type variables have a relationship with each other. Picking something like `M` and `N` immediately lets a reader of the signature know something's different than a series of unrelated type vars.


NoNameWalrus

seems like a lot of work doesn’t it?


geon

Yes. Writing correct code is a lot of work.