0

A third-party library exports a variable containing an enum instead of the enum itself:

enum Size { S, M, L }
export const sizeType: typeof Size = Size;

In my own code, how can I declare that something has type Size?

import { sizeType } from '...';

interface Props {
    size: /* What goes here that's equivalent to 'Size'? */
}

const props: Props = { size: sizeType.S };
props.size = sizeType.L;

Here are some declarations I tried that don't work:

size: typeof sizeType;
// error: Type 'Size' is not assignable to type 'typeof Size'.

size: typeof sizeType['S'];
// error: Type 'Size.L' is not assignable to type 'Size.S'.

size: InstanceType<typeof sizeType>;
// error: Type `typeof Size' does not satisfy the constraint 'abstract new (...args: any) => any`.
Michael Liu
  • 52,147
  • 13
  • 117
  • 150
  • Does [this approach](https://tsplay.dev/NDA8xm) work for you? If yes, I'll write an answer explaining; If not, what am I missing? – wonderflame Aug 22 '23 at 15:49
  • @wonderflame: I came up with the same solution, and was wondering whether there was a shorter alternative. But yes, that works. – Michael Liu Aug 22 '23 at 15:59
  • The [shorter version](https://www.typescriptlang.org/play?ts=5.0.4#code/KYOwrgtgBAyglgL2FA3gKCrANBqBZHTAGRwF801gAPABwHsAnAFygGM6QBnFzxYAFQCeNYAC4oTYcDoAzWHygBeeUgDcFSSKgA1AIYAbMMADyMgDz8AfEqj8A2gGtgg2bYC6FOCCbAGM3azIAAoMdDScqLi8SOJ6hibmmtJy0QJSlmjkaOxcLDSh4eIhYRHKKFCp4qlCIgB0MFCk6vkltak21VK1ROpAA). Should I write an answer then? – wonderflame Aug 22 '23 at 16:01
  • 1
    @wonderflame: Please write up both versions. I think some people may not want the extra type in one-off situations. – Michael Liu Aug 22 '23 at 16:04

2 Answers2

2

Since you don't have access to the enum, the only option will be to get the type of the values of the enum by using the sizeType:

// typeof Size
type Case1 = typeof sizeType
// typeof Size
type Case2 = typeof Size

Now, by using the indexed access let's get the type of every value in union format:

// Size
type Case3 = typeof sizeType[keyof typeof sizeType]
// Size
type Case4 = typeof Size[keyof typeof Size]

With the enums, the whole thing looks a bit weird and that's why I personally prefer to use const assertion + the satisfies operator on javascript object:

const SIZE =  {
  S :0,
  M: 1,
  L: 2,
} as const satisfies Record<string, number>

Anyway, moving back to the problem, let's test the type that we got in the last cases:

const case3A: Case3 = Size.S
const case3B: Case3 = 'error' // error

const case4A: Case4 = Size.S
const case4B: Case4 = 'error' // error

Looks good!

Usage:

interface Props {
  size: typeof sizeType[keyof typeof sizeType];
}

If you would like to shorten the code, you could use the ValueOf utility type described in this answer:

type ValueOf<T> = T[keyof T];

interface Props {
  size: ValueOf<typeof sizeType>;
}
wonderflame
  • 6,759
  • 1
  • 1
  • 17
0

Enums are kind of like classes: they define a type and a reified term-level value:

enum Foo {
  A,
  B,
  C
}

type F = Foo // See it's also a type, we can type alias it
type G = typeof Foo

const a: Foo = Foo.A // The type is the union of the members: 0 | 1 | 2 
const b: G = {
  A: 0,
  B: 1,
  C: 2
}

Playground

The type defined by the enum is the union of the members, and the type defined by typeof MyEnum is the type of the enum itself. This is similar to how class Foo would define a type Foo that represents instances of that class while typeof Foo would refer to the type of the class object itself.

So in your code, as far as I can tell from the question, you just want the interface property to be of type Size, i.e. one of the members of the enum:

interface Props {
    size: Size
}

const whatever: Props = {
    size: Size.S
}
Jared Smith
  • 19,721
  • 5
  • 45
  • 83
  • 1
    I can't reference the non-exported type `Size`. – Michael Liu Aug 22 '23 at 16:00
  • @MichaelLiu gotcha, misunderstood the question. You can use the `keyof typeof Size` approach from wonderflame then, just a word of caution: enums contain both the normal mapping *and* a reverse mapping (you can see this in the compiled output) so it's going to have the numbers 0, 1, 2 in that union as well as the strings S, M, L. – Jared Smith Aug 22 '23 at 16:03