0

Is this possible to do:

type Dessert = 'cake' | 'chocolate' | 'cookie'

const arrWithAllDessertTypes  = ['cake', 'chocolate'] // Want TS to complain that it does contain `cookie`

I have googled and searched SO for an answer but it always talks about doing as:

const desserts = ['cake' , 'chocolate' , 'cookie']

But I'm getting my Dessert type from an end-point response.

Sunil Kumar Das
  • 372
  • 1
  • 2
  • 12
Norfeldt
  • 8,272
  • 23
  • 96
  • 152
  • 1
    Can you clarify what you mean by "I getting my `Dessert` type from an end-point response"? TypeScript is compile-time not run-time, so I'm not sure how you could dynamically be retrieving a type from an API at run-time and have it do anything useful – nate-kumar Feb 03 '23 at 09:20
  • I'm not retrieving the type dynamically. They are being typed/generated. So if the end-point decides to include `muffin` to `Dessert` then I'll get a updated type file for this change. But I have some test that uses these types and I would then like to have TS complain that I do not test `muffin` – Norfeldt Feb 03 '23 at 09:37
  • It's a bit unclear, you want TypeScript to give an error if you try to enter something that does not belong to the `Desert` type? Also, you never did specify the array type for the `arrWithAllDessertTypes` variable. Try `const arrWithAllDessertTypes: Dessert[] = ['cake', 'chocolate', 'water']` – node_modules Feb 03 '23 at 09:40
  • Yes, I want it to error out if I enter something that is not a `Dessert` (`water`) AND if it is missing an dessert. It is intentional that I haven't specified the array type - this is what the question is about. How do specify a more strict type than `: Dessert[]` or `as Dessert[]` or `satisfies Dessert[]`? – Norfeldt Feb 03 '23 at 09:50
  • Does this answer your question? [String Union to string Array](https://stackoverflow.com/questions/44480644/string-union-to-string-array) --Hmm, the type is autogenerated though. So not sure if you can make it work – decorator-factory Feb 03 '23 at 10:31

2 Answers2

2

You can do this by using a const assertion

    const MyDessert = ['cake', 'chocolate', 'cookie'] as const;
    type Dessert = typeof MyDessert[number];  // "cake" | "chocolate" | "cookie" this defines the type you have on your first line

You can play with it in this TS playground

Norfeldt
  • 8,272
  • 23
  • 96
  • 152
Jorge Guerreiro
  • 682
  • 6
  • 22
  • Since `DesertT` will rarely be useful, you can also use `type Dessert = typeof Dessert[number];` – Clashsoft Feb 03 '23 at 10:32
  • Yeah, completely correct, just added it to cover the readonly bit :) – Jorge Guerreiro Feb 03 '23 at 10:33
  • this is going the wrong way :-) - This goes from an array to type but I want to go the other way around: From a type to a "strict" array. – Norfeldt Feb 03 '23 at 10:38
  • I don't think we can create an array from the type because we cannot assemble an array at runtime if types don't exist at runtime. But we can do the inverse which is what this is – Jorge Guerreiro Feb 03 '23 at 11:03
0

I wrote my example with Dessert to make it generic. I did not find the exact solution, but found a way that will make TS tell me check my test files when some of the types are updated:

    import { IsEqual } from 'type-fest'


    const negativeStatusCases = ['pending', 'failed', 'cancelled', 'expired', 'in_progress'] as const
    negativeStatusCases.forEach((negativeStatus) => {
      describe(negativeStatus, () => {
        it('shows message with variant of "warning"', async () => {
          ...
        })
      })
    })

    it('covers all non `succeeded` status cases', () => {
      type Status = Awaited<ReturnType<typeof api.fetchPaymentStatus>>['status']
      type NegativeStatus = Exclude<Status, 'succeeded'>
      const allStatusCasesAreCovered: IsEqual<NegativeStatus, (typeof negativeStatusCases)[number]> = true
      expect(allStatusCasesAreCovered).toBe(true)
    })

hope it helps others

Norfeldt
  • 8,272
  • 23
  • 96
  • 152