3

I have a standard router typing.

type Routes = '/' | '/achievements' | ... ;

This allows you to understand which routers are in the project. But now I have the task to make an array containing all the routers.

const allRoutes: Routes[] = ['/', '/achievements'];

If any router is missing, it should get an error. This array must always contain all routers. How can it be done?

I can't figure out how to require the array to contain the entire enumeration of routers.

UPDATE

My types are described in the file d.ts. So I cannot declare the construct

const ROUTES = ['/', '/achievements'] as const

and export it there

coolswood
  • 81
  • 5
  • Does this answer your question? [Enforce that an array is exhaustive over a union type](https://stackoverflow.com/questions/55265679/enforce-that-an-array-is-exhaustive-over-a-union-type) – Tobias S. Nov 16 '22 at 10:44
  • Wouldn't it be more natural to have an `allRoutes` array which you define with a const assertion, and generate `Routes` like so: `type Routes = (typeof allRoutes)[number]`? – geoffrey Nov 16 '22 at 10:57
  • @coolswood What's your framework? – Dimava Nov 16 '22 at 11:17
  • @geoffrey I can't do it, because I use d.ts file. I can't import there – coolswood Nov 16 '22 at 11:26
  • Try to look into TS satisfies operator, it might be the thing, if I understand your request correctly – Akxe Nov 16 '22 at 11:50

2 Answers2

2

You can do it this way:

const ROUTES = ['/', '/achievements'] as const

type RoutesTuple = typeof ROUTES

type Routes = RoutesTuple[number]

const allRoutes: RoutesTuple = ['/', '/achievements']
wjatek
  • 922
  • 7
  • 22
  • 1
    If you are not going to use `ROUTES` you should probably define `type RoutesTuple = readonly ['/', '/achievements']` directly. – geoffrey Nov 16 '22 at 12:07
0

You may use a function which enforces array to have every item of union type


type Routes = 'a' | 'b' | 'c'

const usedRoutes = ['a', 'b', 'd']

function ensureArrayHasEvery<T>() {
    return function <A extends readonly T[]>(a: NoInfer<ArrayWithEvery<A, T>> | A & [never]) {
        return a;
    }
}


let x = ensureArrayHasEvery<Routes>()
//  ^?
let y = x(['a', 'b', "c"])
//  ^? ['a']
let z = x(['a'])
//  ^? Argument of type '["a"]' is not assignable to parameter of type '["a", "b" | "c"]


type ArrayWithEvery<A extends readonly any[], V> = [V] extends [A[number]] ? A : [...A, Exclude<V, A[number]>]

type NoInfer<A> = [A][A extends any ? 0 : never]
Dimava
  • 7,654
  • 1
  • 9
  • 24