Reputation: 2285
I want to be able to spread the array in to the argment of the function, but I am getting A spread argument must either have a tuple type or be passed to a rest parameter.ts(2556)
const currentColor: string = context.brandingPersonal.customColor; // rgba string
const numberColor: [number, number, number, number] = currentColor
.substring(5, currentColor.length - 1)
.replace(/ /g, '')
.split(',')
.map((item) => Number(item));
function getContrastColor(R: number, G: number, B: number, A?: number) {
const brightness = R * 0.299 + G * 0.587 + B * 0.114 + (1 - A || 0) * 255;
return brightness > 186 ? '#000000' : '#FFFFFF';
}
getContrastColor(...numberColor);
Upvotes: 1
Views: 2346
Reputation: 33041
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
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
Upvotes: 3
Reputation: 35
How about using tuples?
const numberColor = [255,50,50,0.1]
// Check RGBA
function getContrastColor([ R, G, B, A ]:[number, number, number, number]) {
const brightness = R * 0.299 + G * 0.587 + B * 0.114 + (1 - A) * 255;
return brightness > 186 ? '#000000' : '#FFFFFF';
}
// function call with spread operator
getContrastColor(numberColor);
Upvotes: 1
Reputation: 62638
split
makes no guarantees about how many pieces it'll output at runtime. Passing a string like "foo" would make your routine fail, because your function would not return a valid 4-tuple.
You could use something like this, which guarantees a proper tuple output:
function getNumberTuple(color: string) : [number, number, number, number?] {
const rgba = color.substring(5, color.length - 1).replace(/ /g, "")
const numbers = rgba.split(",").map((item) => Number(item));
return [numbers[0] || 0, numbers[1] || 0, numbers[2] || 0, numbers[3]]
}
function getContrastColor(R: number, G: number, B: number, A?: number) { /* ... */}
getContrastColor(...getNumberTuple("color 123,123,123"));
Upvotes: 1