Here you can find explanation of creating number range in typescript.
Here you can find my article about creating number range.
It is possible to achieve extra safety with static type analysis:
type MAXIMUM_ALLOWED_BOUNDARY = 256
type ComputeRange<
N extends number,
Result extends Array<unknown> = [],
> =
(Result['length'] extends N
? Result
: ComputeRange<N, [...Result, Result['length']]>
)
type Octal = ComputeRange<MAXIMUM_ALLOWED_BOUNDARY>[number]
type Digits = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
type AlphaChanel = `0.${Digits}` | '1.0'
type RGBA<Alpha extends number = 1.0> = [Octal, Octal, Octal, (`${Alpha}` extends AlphaChanel ? Alpha : never)?]
function getContrastColor<Alpha extends number>(...[R, G, B, a]: RGBA<Alpha>) {
const A = a || 1;
const brightness = R * 0.299 + G * 0.587 + B * 0.114 + (1 - A || 0) * 255;
return brightness > 186 ? '#000000' : '#FFFFFF';
}
getContrastColor(10, 20, 30, 0.2); // ok
getContrastColor(256, 20, 30, 0.2); // error, 256 is out of the range
getContrastColor(255, 20, 30, 0.22); // error, 0.22 should be 0.2
Playground
RGBA
utility type acts as a type validator and at the same time as a type for all valid RGB tuples. You can find more info here and here. If A
is allowed alpha channel value it returns A
, otherwise - never
.
AlphaChanel
- is a union of all allowed values. As you might have noticed I have allowed only one digit after 0.
. If you want to allow more, please use this:
type AlphaChanel = `0.${ComputeRange<999>[number]}` | '1.0'
If you are interested in hex
validation, you can check this article or this answer