1

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);

5
  • 1
    TS doesn't know that numberColor's type is a "tuple" (a fixed size array in TS) because tuples need to be defined as const. Add as const after [255,50,50,0.1] to narrow the type from Array<Number> to [Number, Number, Number, Number] Commented Jan 14, 2022 at 23:47
  • The problem is that the function has a fixed number of parameters, an array does not. You can turn it into a tuple, make the array const, or use rest (which IMO maybe don't). Searching the web for the error message may provide other options, too. Commented Jan 14, 2022 at 23:47
  • @HKG what if the value is rgb only how to set the last parameter optional in the argument ? Commented Jan 14, 2022 at 23:49
  • 1
    Simply by doing A?: number Commented Jan 14, 2022 at 23:50
  • @HKG I will update my code to show you what is going on Commented Jan 14, 2022 at 23:54

3 Answers 3

3

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

Sign up to request clarification or add additional context in comments.

Comments

1

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"));

Comments

1

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);

3 Comments

The problem is i want it to support both rgba and rgb so i could have 3 number or 4 numbers
Argument of type 'number[]' is not assignable to parameter of type '[number, number, number, number]'. Target requires 4 element(s) but source may have fewer.ts(2345)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.