2

I need to build a function where I can send an enum:

enum myExample {
   value1,
   value2
}

and return an array like

[myExample.value1,myExample.value2] : myExample[]

I did this, the but the return type of the function is wrong let x: (typeof myExample)[].

 enum myExample {
    value1,
    value2
}

function getArrayWithNumberBaseEnumItems<T>(numberEnum: T): T[]{
    let arrayWithEnumItems: T[] = [];
    for (let item in numberEnum) {
        if (isNaN(Number(item))) {
            arrayWithEnumItems.push(numberEnum[item] as any);
            console.log(numberEnum[item]);
        }
    }
    return arrayWithEnumItems;
}

let x = getArrayWithNumberBaseEnumItems(myExample);
console.dir(x); // [0,1]

I am using Typescript 2.6

distante
  • 6,438
  • 6
  • 48
  • 90

2 Answers2

4

You are likely confusing the named type myExample with the named value myExample. They are not the same, despite the same name. The type myExample is the type of the enum values which are numbers. The value myExample has type typeof myExample, a map from keys value1 and value2 to those myExample enum values. That is, typeof myExample is something like {example1: myExample.example1, example2: myExample.example2}.

(For more of my ranting about the difference between named values and named types in TypeScript, see this answer)

Therefore when you pass in the value myExample to getArrayWithNumberBaseEnumItems(), you are passing in typeof myExample and want myExample[] to come out. The way to get from the former to the latter is to do a lookup from T (corresponding to typeof myExample) to T[keyof T] (meaning "the values of the properties of T").

Thus you should amend your types to the following:

function getArrayWithNumberBaseEnumItems<T>(numberEnum: T): T[keyof T][] {
  let arrayWithEnumItems: T[keyof T][] = [];
  for (let item in numberEnum) {
    if (isNaN(Number(item))) {
      arrayWithEnumItems.push(numberEnum[item]); // no type assertion here
      console.log(numberEnum[item]);
    }
  }
  return arrayWithEnumItems;
}

Note how I've removed your type assertion of numberEnum[item] to any. The error you were suppressing:

// Argument of type 'T[Extract<keyof T, string>]' 
// is not assignable to parameter of type 'T'.

(that's the error TS3.1 gives. TS2.6 probably gives a different-looking but similar error)

was trying to tell you that you were trying to push a property value onto an array of key-value maps, which was an error. Sometimes you need to assert to get things done, but this is not one of those times.

Okay, hope that helps. Good luck!

jcalz
  • 264,269
  • 27
  • 359
  • 360
  • I have to much to learn about Typescript... Thank you for your explanation here and in your other link! Just a quick question. Is there a reason why the returned type is `(myExample)[]` and not `myExample[] ` ? (with the parentesis) – distante Oct 18 '18 at 06:15
2

You can get the value type of the enum by using a type query:

enum myExample {
    value1,
    value2
}

function getArrayWithNumberBaseEnumItems<T>(numberEnum: T): T[keyof T][] {
    let arrayWithEnumItems: T[keyof T][] = [];
    for (let item in numberEnum) {
        if (isNaN(Number(item))) {
            arrayWithEnumItems.push(numberEnum[item]);
            console.log(numberEnum[item]);
        }
    }
    return arrayWithEnumItems;
}

let x = getArrayWithNumberBaseEnumItems(myExample);
console.dir(x); // [0,1]
Titian Cernicova-Dragomir
  • 230,986
  • 31
  • 415
  • 357