Typescript: `extends enum` test for use in conditional types

Created on 21 May 2018  路  5Comments  路  Source: microsoft/TypeScript

Search Terms

enum isEnum mapped-types conditional-types enum-test extends-enum

Suggestion

Rephrased from : https://stackoverflow.com/questions/50452032/typescript-enum-type-check-for-conditional-types

Here's what I kinda want, but it is not syntactically valid:

enum Features {
  "A" = 1,
  "B" = 2,
  "C" = 2
}

type EnumOrString<T> = T extends enum
  ? T | keyof T
  : T

declare function getData(featureFilter: EnumOrString<typeof Features>[]): Features[]

getData takes an array of the enum values or the enum keys but returns only the enum values.

I also would want to extend this to mapped types similar to DeepPartial so that any nested enums all get this treatment - without having to have separate hierarchies of types partitioned by Request and Response. Example on Playground

Use Cases

I make calls to restful services that accept enum values as either the number value OR the string key, but the services always return just the number value. I could ignore the ability to send strings, but some code would have to be rewritten because it happens to cache values by these key names.

Checklist

My suggestion meets these guidelines:

  • [x] This wouldn't be a breaking change in existing TypeScript / JavaScript code
  • [x] This wouldn't change the runtime behavior of existing JavaScript code
  • [x] This could be implemented without emitting different JS based on the types of the expressions
  • [x] This isn't a runtime feature (e.g. new expression-level syntax)
Needs More Info

All 5 comments

I am not sure i understand what you mean by keyof T in the above example.. keyof Feature is the same as keyof number, which is a list of "toString" | "toExponential" ..., so i am guessing Feature | "toString" is not what you want from this type..

I think you meant keyof typeof Feature, and for that, you have to remember to pass in the value type at use site, and if you are doing this for enums, why need to use a conditional type?

Fixed that in the example. The case I'm going for is like this though:

export type DeepEnumish<T> =
    T extends any[] ? DeepEnumishArray<T[number]> :
    T extends enum ? T | keyof T :
    T extends object ? DeepEnumishObject<T> :
    T;

export interface DeepEnumishArray<T> extends Array<DeepEnumish<T>> {}

export type DeepEnumishObject<T> = {
    [P in keyof T]: DeepEnumish<T[P]>;
}

declare function roundtripPayload(criteria : DeepEnumish<Payload>): Payload

Where the nested enum members of Payload can be sent as numbers or strings, and will be returned from the server as numbers certainly.

this line still does not make much sense still.. T extends enum ? T | keyof T : if T is an enum type, keyof T is keyof number | keyof string.. which is not what you want if i understand correctly..

I am guessing you want keyof typeof e, but to do that, you need the value reference, which you do not have.

This is such a common confusion (class and enum introducing both a named type and a named value where the named value is not of the named type) that I wonder if there's any use for a feature request that lets people get from the named type to the type of the named value. Something like this for enums:

enum MyEnum {  A, B }

type TypeofMyEnum = EnumTypeof<MyEnum>; // typeof MyEnum

I'm thinking of something like the following synthetic enum code, which extends the type of each element of the enumeration with a design-time-only brand that points back to the enum object:

type EnumBrand<E> = { __enum: E };
type EnumTypeof<E extends EnumBrand<any>> = E['__enum'];

namespace MyEnum {   
    const brand = <T>(x: T) => x as T & EnumBrand<typeof MyEnum>;
    enum _MyEnum { A, B }
    // ignoring reverse mapping as well as bitwise craziness
    export const A = brand(_MyEnum.A);
    export type A = typeof A;
    export const B = brand(_MyEnum.B);
    export type B = typeof B;
}
type MyEnum = typeof MyEnum[keyof typeof MyEnum];

Then the following works:

type TypeofMyEnum = EnumTypeof<MyEnum>;

and checking for the brand would meet the "detect an enum" requirement if needed, also:

type IsEnum<T> = T extends EnumBrand<any> ? true : false

I don't like phantom properties that don't exist at runtime, so maybe some other way would work? Thoughts?

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Antony-Jones picture Antony-Jones  路  3Comments

weswigham picture weswigham  路  3Comments

zhuravlikjb picture zhuravlikjb  路  3Comments

kyasbal-1994 picture kyasbal-1994  路  3Comments

uber5001 picture uber5001  路  3Comments