Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

string enum to union #30655

Open
lovetingyuan opened this issue Nov 3, 2023 · 10 comments · May be fixed by #30656
Open

string enum to union #30655

lovetingyuan opened this issue Nov 3, 2023 · 10 comments · May be fixed by #30656
Labels
new-challenge Propose a new challenge, a PR will be auto generated

Comments

@lovetingyuan
Copy link

lovetingyuan commented Nov 3, 2023

Info

Transform a string enum to union type.

difficulty: medium
title: string enum to union
tags: union, enum, string

Question

Transform a string enum to union type.

Example:

enum A {
  a = 'aa',
  b = 'bb',
  c = 'cc'
}

type Result = EnumToUnion<A>; // expected to be "aa" | "bb" | "cc"

Template

This is the template for challengers to start the coding. Basically, you just need to change the name of your generic/function and leave to implementation any.

enum YourEnum {}
type EnumToUnion<T> = any; // write your anwser here.
type UnionType = EnumToUnion<YourEnum>; // should be a string union.

Test Cases

Provide some test cases for your challenge, you can use some utils from @type-challenges/utils for asserting.

import type { Equal, Expect } from '@type-challenges/utils'
import { ExpectFalse, NotEqual } from '@type-challenges/utils'

enum A {
  a = 'aa',
  b = 'bb',
  c = 'cc'
}

enum B {
  a = 'aa',
}

enum C {
  a, b
}

enum D {
  No,
  Yes = "YES",
}

type cases = [
  Expect<Equal<EnumToUnion<A>, 'aa' | 'bb' | 'cc'>>,
  Expect<Equal<EnumToUnion<B>, 'aa'>>,
  // @ts-expect-error
  EnumToUnion<C>,
  // @ts-expect-error
  EnumToUnion<D>
]
@lovetingyuan lovetingyuan added the new-challenge Propose a new challenge, a PR will be auto generated label Nov 3, 2023
@github-actions github-actions bot linked a pull request Nov 3, 2023 that will close this issue
Copy link
Contributor

github-actions bot commented Nov 3, 2023

#30656 - Pull Request updated.

2023-11-05T06:33:30.105Z Preview in Playground

@jiangshanmeta
Copy link
Member

Hi @lovetingyuan would you mind helping provide an answer here?

@lovetingyuan
Copy link
Author

Hi @lovetingyuan would you mind helping provide an answer here?

yes, here is an answer:

type EnumToUnion<T extends string> = `${T}`

I just found the answer, but I don't really know how it works.

@E0SelmY4V
Copy link
Contributor

Hi @lovetingyuan would you mind helping provide an answer here?

yes, here is an answer:

type EnumToUnion<T extends string> = `${T}`

I just found the answer, but I don't really know how it works.

Because in the test cases, you give the enum as a type to EnumToUnion instead of the type of the enum.
When you use a enum as a type, it equal to a union type of its value.
If you want to give a enum itself to EnumToUnion, you can add a typeof in front of it, like EnumToUnion<typeof SomeEnum>.

@E0SelmY4V
Copy link
Contributor

才发现这楼是不是都会说中文,,

@lovetingyuan
Copy link
Author

哦哦 确实 enum 和 typeof enum 是不一样的

@Jvp2001
Copy link

Jvp2001 commented Feb 20, 2024

Hi @lovetingyuan would you mind helping provide an answer here?

yes, here is an answer:

type EnumToUnion<T extends string> = `${T}`

I just found the answer, but I don't really know how it works.

Because in the test cases, you give the enum as a type to EnumToUnion instead of the type of the enum. When you use a enum as a type, it equal to a union type of its value. If you want to give a enum itself to EnumToUnion, you can add a typeof in front of it, like EnumToUnion<typeof SomeEnum>.

I just wanted to say, I came to the same answer. This was a fun challenge. I do not usually use enums; nevertheless, this was an entertaining and informative challenge.

@Offroaders123
Copy link

Offroaders123 commented Mar 5, 2024

I'm curious if anyone knows if there's a way to do something similar for number types as well? Say like you have a numerical enum, is there a way to turn that into a union of the number values? Is there something in TypeScript like

type Demo = `${}`;

but for Number(), essentially?

@E0SelmY4V
Copy link
Contributor

E0SelmY4V commented Mar 6, 2024

I'm curious if anyone knows if there's a way to do something similar for number types as well? Say like you have a numerical enum, is there a way to turn that into a union of the number values? Is there something in TypeScript like

type Demo = `${}`;

but for Number(), essentially?

enum Enum {
  a,
  b,
  c,
}

type EnumValue = `${Enum}` extends `${infer N extends number}` ? N : never;
// ^? 0 | 1 | 2

Is this what you are looking for?
type EnumValue is 0 | 1 | 2 instead of Enum .

@Offroaders123
Copy link

I'm curious if anyone knows if there's a way to do something similar for number types as well? Say like you have a numerical enum, is there a way to turn that into a union of the number values? Is there something in TypeScript like

type Demo = `${}`;

but for Number(), essentially?

enum Enum {
  a,
  b,
  c,
}

type EnumValue = `${Enum}` extends `${infer N extends number}` ? N : never;
// ^? 0 | 1 | 2

Is this what you are looking for? type EnumValue is 0 | 1 | 2 instead of Enum .

Yes, works great! Thank you 🚀

Offroaders123 added a commit to Offroaders123/NBTify that referenced this issue Mar 7, 2024
Working on making the typings for declarating tag types a little simpler to setup/use

So rather than `StringTag<Hey>` being just the plain enum value, it will accept either using the enum by it's properties, or by passing in the plain strings itself
I like it in that you can declare things neatly with enums (which will later down the line also allow for mod-ability of your type definitions!), but still use the raw values themselves, without needing to import a reference to the enum by way of importing it
So it's the ability to write type-safe, validated code, but without needing to statically import the types library itself, it's only for the IDE/hinting/type-checking step
Say like you're writing a script that validates with Region-Types, you wouldn't have to import `EntityResource` from `region-types/java/entity`, it would just type-check the shape for you, by way of the type of the parent shape that you are accessing

```ts
import type { Entity } from "region-types/java/entity";

declare const entity: Entity;
entity.id = // you get autocomplete string values right here
```

Sorry my demo code names are so off the wall, it seems like I choose random funny ones 😂

```ts
() => {

enum Hey {
  Nice = "nice",
  Hah = "hah"
}

type Hi = StringTag<Hey>;

let hi: Hi;
hi = "nice";
hi = Hey.Hah;

enum Hag {
  Fart,
  Giggity,
  Poop,
  MooveOver
}

type Ooga = ByteTag<Hag>;

let booga: Ooga;
booga = new Int8(0);

const v: ListTag<DoubleTag<Hag>> = [];
v.push(0);

}
```

Woah, this is actually a really cool unexpected concept!
(Right here was a custom demo using my new `NumberLike` type, but to convert a `bigint` primitive type to a `number`-based one)
You can use the same type inference to convert between `bigint` and `number` value types

I wish there was a way to make `bigint`-based enums too, looks like it can only be with `string` or `number` values.

I started making the demo for this implementation/concept a day or two ago, here was the file for that:

```ts
// type-challenges/type-challenges#30655
// https://www.designcise.com/web/tutorial/how-to-create-a-union-type-from-enum-keys-in-typescript
// https://stackoverflow.com/questions/68458593/how-to-create-an-enum-like-object-with-bigint-initializers-in-typescript
// https://www.reddit.com/r/NoStupidQuestions/comments/1b7r28y/can_a_pizza_be_cut_into_one_slice/

class Double<T extends number = number> extends Number {
  constructor(value: T) {
    super(value);
  }

  override valueOf(): T {
    return super.valueOf() as T;
  }

  get [Symbol.toStringTag]() {
    return "Double" as const;
  }
}

type DoubleTag<T extends number = number> = Double<T>;

type StringTag<T extends string = string> = T;

enum Enum {
  a,
  b,
  c,
}

type EnumValue = `${Enum}` extends `${infer N extends number}` ? N : never;
  // ^?

type NumberMaker<T extends number> = `${T}` extends `${infer N extends number}` ? N : never;

type haha = `${Enum}`;
  // ^?

type EnumUnion = NumberMaker<Enum>;
  // ^?

interface Demo {
  Hi: DoubleTag<NumberMaker<Enum>>;
  Hey: StringTag<`${5}`>;
}
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
new-challenge Propose a new challenge, a PR will be auto generated
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants