1

Having an object for theme's colors:

themeColors = {
  primary: ['#fff', '#000'],
  secondary: ['#DDD', '#333'],
  error: ['#CCC', '#444']
}

We can assign type like this:


themeColor: Record<string, [string; string]>

How to add restriction first and second of tupel should be unique? I mean

themeColors = {
  primary: ['#fff', '#000'],
  secondary: ['#fff', '#333'],
}

or

themeColors = {
  primary: ['#fff', '#000'],
  secondary: ['#DDD', '#000']
}

shouldn't pass type checking, becouse have duplication ('#fff' in first case, '#000' in second case)

I belive it sould be close to this approach

Is there a way to define type for array with unique items in typescript?

  • The first and second tuples in your question are kind of unique. It just that they share a value. Is sharing a value what you want to avoid? – Tobias S. Oct 31 '22 at 10:00

1 Answers1

0

You'll need a helper function to the inference and validation:

const themeColors = colors({
  primary: ['#fff', '#000'],
  secondary: ['#DDD', '#333'],
  error: ['#CCC', '#444']
} as const);

Here's what that function would look like:

function colors<C extends Record<string, readonly [string, string]>>(c: {
    [K in keyof C]: C[K] extends readonly [infer A, infer B]
        ? readonly [A, B]
            & (A extends Omit<C, K>[keyof Omit<C, K>][0] ? never : {})
            & (B extends Omit<C, K>[keyof Omit<C, K>][1] ? never : {})
        : C[K];
}) { return c }

We go through each of the entries and check if the first or second string already exist in the other entries. If it is, we intersect the type with never, otherwise, an empty object type {} (which basically does nothing).

You won't get a super helpful error, but hey, it's still an error. If you moved the checks to before readonly [A, B], then TypeScript won't be able to infer the original type of C and everything will break. This is the only method I have found which retains inference while still validating the input correctly. See the linked playground below with some examples.

Playground

kelsny
  • 23,009
  • 3
  • 19
  • 48
  • The most unobvious thing - is why we should return empty object - not [A, B] cortege or something else. Can you advise me on the course or book for deep learning TS? Because I didn't find any article about this case and I fill I have gaps in my understanding of approaches. – Михаил Бобрышев Nov 06 '22 at 09:21
  • @МихаилБобрышев It's actually all in the documentation, but you have to put all the pieces together yourself. For a walkthrough of that, I recommend https://type-level-typescript.com/. Other than that, everything you need is in the docs - bugs and issues with TypeScript itself or its behavior/design are probably documented as GitHub issues. – kelsny Nov 06 '22 at 13:19