Does nearBindgen not support child classes? - nearprotocol

When I try to store an instance of a class that is the child of a different class in a PersistentVector or PersistentSet, when I read the same data, the instance fields have their default values.
The same doesn't happen when storing a regular class.
Is this a known issue?
Minimal reproduceable code:
// index.ts
import { PersistentVector } from "near-sdk-as";
#nearBindgen
class Base {
constructor(
public value1: u64
) { }
}
#nearBindgen
class Child extends Base {
constructor(
value1: u64,
public value2: u64
) {
super(value1);
}
}
#nearBindgen
export class Contract {
private pers: PersistentVector<Base> = new PersistentVector<Base>("vec");
add(value1: u64, value2: u64): Child {
const c = new Child(value1, value2);
this.pers.pushBack(c);
return c;
}
get(): Array<Base> {
const res: Array<Base> = [];
for (let i = 0; i < this.pers.length; i++) {
res.push(this.pers[i]);
}
return res;
}
}
// index.unit.spec.ts
import { Contract } from "../assembly/index";
let cont: Contract
beforeEach(() => {
cont = new Contract();
})
describe("Contract", () => {
it("add returns correct value", () => {
const result = cont.add(7, 3);
expect(result.value1).toBe(7);
expect(result.value2).toBe(3);
});
it("get after add returns 1 correct value", () => {
cont.add(7, 3);
const results = cont.get();
expect(results).toHaveLength(1);
const result = results[0];
expect(result.value1).toBe(7);
});
})
It produces the following test output:
[Describe]: Contract
[Success]: ✔ add returns correct value
[Fail]: ✖ get after add returns 1 correct value
[Actual]: 0 as u64
[Expected]: 7 as u64
[Stack]: RuntimeError: unreachable
at node_modules/#as-pect/assembly/assembly/internal/assert/assert (wasm://wasm/00023e6a:wasm-function[52]:0xde9)
at node_modules/#as-pect/assembly/assembly/internal/Expectation/Expectation<u64>#toBe (wasm://wasm/00023e6a:wasm-function[138]:0x2d9c)
at start:src/minimal/__tests__/index.unit.spec~anonymous|1~anonymous|1 (wasm://wasm/00023e6a:wasm-function[176]:0x466a)
at node_modules/#as-pect/assembly/assembly/internal/call/__call (wasm://wasm/00023e6a:wasm-function[179]:0x4690)
[File]: src/minimal/__tests__/index.unit.spec.ts
[Groups]: 2 pass, 2 total
[Result]: ✖ FAIL
[Snapshot]: 0 total, 0 added, 0 removed, 0 different
[Summary]: 1 pass, 1 fail, 2 total
[Time]: 2.333ms

Related

Unexpected error upon calling hook: Missing value for non-nullable field

I am trying to build a subgraph for Graph Protocol following the example here (the example with the more performant way). During compilation of my Graph protocol project using a testing tool matchstick-as ^0.5.0, I get the following error:
Igniting tests
nft-lottery
--------------------------------------------------
Enter the NFT Lottery:
thread 'main' panicked at '🆘 Unexpected error upon calling hook: Missing value for non-nullable field 'player' for an entity of type 'PlayerToLottery'.
wasm backtrace:
0: 0x331a - <unknown>!src/nft-lottery/handleNftLotteryEnter;
1: 0x3807 - <unknown>!start:tests/nft-lottery.test~anonymous|0~anonymous|0
Could anyone help me with this, please?
Here is my ./schema.graphql
type NftLottery #entity {
id: ID!
open: BigInt!
close: BigInt!
prize: BigInt!
players: [PlayerToLottery!]! #derivedFrom(field: "lottery")
requestId: BigInt
updatedAtTimestamp: BigInt
}
type Player #entity {
id: ID! # address
lotteries: [PlayerToLottery!]! #derivedFrom(field: "player")
}
type PlayerToLottery #entity {
id: ID! # Set playerAddress.toHexString() + lotteryId.toHexString()
player: Player!
lottery: NftLottery!
}
# events
type NftLotteryEnter #entity {
id: ID! # Set lotteryCounter + playerAddress
lotteryCounter: BigInt!
player: Bytes!
numberOfEntrances: [BigInt!]!
updatedAtTimestamp: BigInt
}
Here is my ./src/mapping.ts
function getIdFromEventAddressInt(par2: Address, par1: BigInt): string {
return par1.toHexString() + par2.toHexString()
}
export function handleNftLotteryEnter(event: NftLotteryEnterEvent): void {
/* if a PlayerToLottery does not exists, create it */
const playerLotteryId = getIdFromEventAddressInt(event.params.player, event.params.lotteryCounter)
let playerToLottery = PlayerToLottery.load(playerLotteryId)
if (!playerToLottery) {
playerToLottery = new PlayerToLottery(playerLotteryId)
playerToLottery.save()
}
/* if a player does not exist, create them */
const playerId = event.params.player.toHexString()
let player = Player.load(playerId)
if (!player) {
player = new Player(playerId)
player.save()
}
/* if a lottery does not exist, create it */
const itemId = event.params.lotteryCounter.toHexString()
let nftLottery = NftLottery.load(itemId)
if (!nftLottery) {
nftLottery = new NftLottery(itemId)
nftLottery.open = event.block.timestamp
nftLottery.close = BigInt.fromString("000000000000")
nftLottery.prize = BigInt.fromString(event.params.entranceFee.toString())
nftLottery.players = new Array<string>()
}
// update lottery data
nftLottery.prize.plus(event.params.entranceFee)
// update players
let arrPlayers = nftLottery.players
arrPlayers.push(event.params.player.toHexString())
nftLottery.players = arrPlayers
nftLottery.updatedAtTimestamp = event.block.timestamp
nftLottery.save()
}
Entity PlayerToLottery needs to have values assigned before saving it:
/* if a PlayerToLottery does not exists, create it */
const playerLotteryId = getIdFromEventAddressInt(event.params.player, event.params.lotteryCounter)
let playerToLottery = PlayerToLottery.load(playerLotteryId)
if (!playerToLottery) {
playerToLottery = new PlayerToLottery(playerLotteryId)
playerToLottery.player = event.params.player.toHexString()
playerToLottery.lottery = event.params.lotteryCounter.toHexString()
playerToLottery.save()
}
Credit goes to Maks#3349 at unit testing of Graph discord.

How to use EnumProcesses in node-ffi

I was trying to use EnumProcesses with node-ffi. I got code below:
import ffi from 'ffi'
export const psapi = ffi.Library('psapi', {
EnumProcesses: ['bool', ['ulong', 'ulong', 'uint16*']]
})
export class Win32ProcessManager {
public async getProcessList () {
let lpidProcess = ref.alloc('ulong*')
const cb = 1024
const lpcbNeeded = ref.alloc('uint16*')
const res = psapi.EnumProcesses(lpidProcess, cb, lpcbNeeded)
const ulongSize = (ref as any).sizeof.ulong
const totalBytesReturned = lpcbNeeded.readInt16LE()
const processCount = totalBytesReturned / ulongSize
console.log(`processCount: ${processCount}`)
// ??? How to get the value from the lpidProcess?
return lpidProcess
}
}
I tried with ref.get but I encountered errors:
let processId = ref.get(array, 0, ref.types.ulong)
console.log(processId)
const pointerSize = (ref as any).sizeof.pointer
console.log(pointerSize)
let processId2 = ref.get(array, (ref as any).sizeof.pointer, ref.types.ulong)
console.log(processId2)
Errors:
RangeError [ERR_BUFFER_OUT_OF_BOUNDS]: Attempt to write outside buffer bounds
Anyone knows how to use node-ffi read the array data from dll?
Thanks #DrakeWu-MSFT, I finally got my code works, here are how they looks finally:
import ffi from 'ffi';
import ref from 'ref';
import ArrayType from "ref-array";
export const psapi = ffi.Library('psapi', {
EnumProcesses: ['bool', ['ulong*', 'ulong', 'uint16*']],
});
export class Win32ProcessManager {
public getProcessIdList (): number[] {
const processIdLength = 1024;
const ulongSize = (ref as any).sizeof.ulong;
const cb = processIdLength * ulongSize;
let processIdArray = ArrayType('ulong', processIdLength);
let lpidProcess = ref.alloc(processIdArray);
const lpcbNeeded = ref.alloc('uint16*');
const res = psapi.EnumProcesses(lpidProcess, cb, lpcbNeeded);
if (res) {
const totalBytesReturned = lpcbNeeded.readInt16LE();
const processCount = totalBytesReturned / ulongSize;
const processArray = (lpidProcess as any).deref();
let resultProcessArray: number[] = [];
for (let i = 0; i < processCount; i++) {
resultProcessArray.push(processArray[i]);
}
return resultProcessArray;
} else {
console.error(`Get process list failed with result from EnumProcess: ${res}`);
return [];
}
}
}
I was struggled with getting array data from the pointer, and that was wrong, as #DrakeWu-MSFT said in the comment, because I didn't allocate enough spaces for the buffer, no data can be write into that. With ref-array and a pointer to the array, it works like a charm.

XUnit Test for ViewComponent returns null result?

I am trying to test my ViewComponent with XUnit.
When I debug through the component and set a break point right before it returns the Component View, the model is set.
Here is the simple model I am returning.
public class IntDashMakeRecAssgnmntsPoRespMngrVM
{
public IEnumerable<Audit> Audits { get; set; }
}
And I am trying to assert the Audits.Count() is greater than 0.
Here is my View Component:
public class IntDashMakeRecAssgnmntsPoRespMngrViewComponent : ViewComponent
{
private IAuditRepository _auditRepo;
private IExternalRepository _externalRepo;
public IntDashMakeRecAssgnmntsPoRespMngrViewComponent(IAuditRepository auditRepo,
IExternalRepository externalRepo)
{
_auditRepo = auditRepo;
_externalRepo = externalRepo;
}
public IViewComponentResult Invoke()
{
ClaimsPrincipal user = HttpContext.Request.HttpContext.User;
short staffId = short.Parse(user.Claims.Single(c => c.Type == "StaffId").Value);
// Get all Internal Audits that are not closed and not completed
var audits = _auditRepo.Audits
.Include(a => a.Findings).ThenInclude(f => f.Recommendations).ThenInclude(r => r.Assignments)
.Where(a => a.StatusID != 3 && a.StatusID != 11);
var external = _externalRepo.ExternalRecords;
audits = audits.Where(a => !external.Any(e => e.AuditID == a.AuditID));
if (User.IsInRole("PAG_SPEC") && !User.IsInRole("PAG_ADMIN_INT"))
{
audits = audits.Where(a =>
a.Assignments.Any(assn => assn.AssignmentAuditId == a.AuditID
&& assn.AssignmentRoleId == 2 && assn.AssignmentStaffId == staffId));
}
// Where audit has a recommendation without an assigned PO Authorizer
// OR without an assigned Responsible Manager (Rec Level).
List<Audit> auditsToAssign = new List<Audit>();
foreach (Audit audit in audits)
{
foreach (Finding finding in audit.Findings)
{
foreach (Recommendation rec in finding.Recommendations)
{
if (!rec.Assignments.Any(asgn => asgn.AssignmentRoleId == 15)
|| !rec.Assignments.Any(asgn => asgn.AssignmentRoleId == 26)
)
{
auditsToAssign.Add(rec.Finding.Audit);
break;
}
}
}
}
IntDashMakeRecAssgnmntsPoRespMngrVM intDashMakeRecAssgnmntsPoRespMngrVM =
new IntDashMakeRecAssgnmntsPoRespMngrVM
{
Audits = auditsToAssign
};
return View("/Views/InternalAudit/Components/Dashboard/IntDashMakeRecAssgnmntsPoRespMngr/Default.cshtml", intDashMakeRecAssgnmntsPoRespMngrVM);
}
}
When I get to this line in debugging and break to inspect, I have 1 Audit which I want:
return View("/Views/InternalAudit/Components/Dashboard/IntDashMakeRecAssgnmntsPoRespMngr/Default.cshtml", intDashMakeRecAssgnmntsPoRespMngrVM);
Now here is my Unit Test:
[Fact]
public void ReturnsAudit_1Finding_1Rec_1Asgn_PONeeded_RespMnrAssigned()
{
// Arrange
var audits = new Audit[]
{
new Audit { AuditID = 1 }
};
var findings = new Finding[]
{
new Finding{ Audit = audits[0], FindingId = 1 } // 1 Finding
};
var recommendations = new List<Recommendation>()
{
new Recommendation // 1 Rec
{
Finding = findings[0],
Assignments = new List<Assignment>()
{
// PO Authorizor
new Assignment { AssignmentRoleId = 15 }
// No Responsible Manager
}
}
};
audits[0].Findings = findings;
findings[0].Recommendations = recommendations;
Mock<IAuditRepository> mockAuditRepo = new Mock<IAuditRepository>();
mockAuditRepo.Setup(m => m.Audits).Returns(audits.AsQueryable());
Mock<IExternalRepository> mockExternalRepo = new Mock<IExternalRepository>();
mockExternalRepo.Setup(m => m.ExternalRecords).Returns(
new External[0].AsQueryable()
);
// Act
var component = new IntDashMakeRecAssgnmntsPoRespMngrViewComponent(
mockAuditRepo.Object, mockExternalRepo.Object);
component.ViewComponentContext = new ViewComponentContext();
component.ViewComponentContext.ViewContext.HttpContext = TestContext;
var result =
component.Invoke() as IntDashMakeRecAssgnmntsPoRespMngrVM;
int auditCount = (result).Audits.Count();
// Assert
Assert.Equal(1, auditCount);
}
Why is result null on this line?
var result =
component.Invoke() as IntDashMakeRecAssgnmntsPoRespMngrVM;
I also tried this first and it is still null:
ViewComponentResult result =
component.Invoke() as ViewComponentResult;
int auditCount =
((IntDashMakeRecAssgnmntsPoRespMngrVM)result.Model).Audits.Count();
I figured it out.
I wasn't casting the result to the right type.
I had this:
ViewComponentResult result =
component.Invoke() as ViewComponentResult;
int auditCount =
((IntDashMakeRecAssgnmntsPoRespMngrVM)result.Model).Audits.Count();
It should be this:
var result =
component.Invoke() as ViewViewComponentResult;
int auditCount =
((IntDashMakeRecAssgnmntsPoRespMngrVM)result.ViewData.Model).Audits.Count();
ViewViewComponentResult instead of ViewComponentResult.

Why the warning: "Constant <name> inferred to have type (), which may be unexpected"?

I'm attempting to sort an array of instantiated items of the class: "Employees".
However I'm getting the following error within my playground:
Here's the code as written in my playground:
class Employee {
var firstName:String = ""
var lastName:String = ""
init(fName:String, lName:String) {
self.firstName = fName
self.lastName = lName
}
}
var employees = [Employee]()
// 1)
var employee = Employee(fName: "Ric", lName: "Lee")
employees.append(employee)
// 2)
employee = Employee(fName: "Ralph", lName: "Knott")
employees.append(employee)
// 3)
employee = Employee(fName: "Joe", lName: "Smirf")
employees.append(employee)
// 4)
employee = Employee(fName: "Meredith", lName: "Lind")
employees.append(employee)
// 5)
employee = Employee(fName: "Aarnald", lName: "Zingerhost")
employees.append(employee)
let sortedEmployees = employees.sort { (e1:Employee, e2:Employee) -> Bool in
e1.lastName < e2.lastName
}
What am I missing here?
Why the warning?
The reason is that you are using the wrong function (former sortInPlace):
In Swift 3
sort() has been renamed to sorted()
sortInPlace() has been renamed to sort()
Therefore it's
let sortedEmployees = employees.sorted { (e1:Employee, e2:Employee) -> Bool in
e1.lastName < e2.lastName
}
Source: Swift Evolution: Apply API Guidelines to the Standard Library
try declaring
let sortedEmployees = employees.sort { (e1:Employee, e2:Employee) -> Bool in
e1.lastName < e2.lastName}
as
let sortedEmployees : () = employees.sort { (e1:Employee, e2:Employee) -> Bool in
e1.lastName < e2.lastName}

How to programmatically enumerate an enum type?

Say I have a TypeScript enum, MyEnum, as follows:
enum MyEnum {
First,
Second,
Third
}
What would be the best way in TypeScript 0.9.5 to produce an array of the enum values? Example:
var choices: MyEnum[]; // or Array<MyEnum>
choices = MyEnum.GetValues(); // plans for this?
choices = EnumEx.GetValues(MyEnum); // or, how to roll my own?
This is the JavaScript output of that enum:
var MyEnum;
(function (MyEnum) {
MyEnum[MyEnum["First"] = 0] = "First";
MyEnum[MyEnum["Second"] = 1] = "Second";
MyEnum[MyEnum["Third"] = 2] = "Third";
})(MyEnum || (MyEnum = {}));
Which is an object like this:
{
"0": "First",
"1": "Second",
"2": "Third",
"First": 0,
"Second": 1,
"Third": 2
}
Enum Members with String Values
TypeScript 2.4 added the ability for enums to possibly have string enum member values. So it's possible to end up with an enum that look like the following:
enum MyEnum {
First = "First",
Second = 2,
Other = "Second"
}
// compiles to
var MyEnum;
(function (MyEnum) {
MyEnum["First"] = "First";
MyEnum[MyEnum["Second"] = 2] = "Second";
MyEnum["Other"] = "Second";
})(MyEnum || (MyEnum = {}));
Getting Member Names
We can look at the example immediately above to try to figure out how to get the enum members:
{
"2": "Second",
"First": "First",
"Second": 2,
"Other": "Second"
}
Here's what I came up with:
const e = MyEnum as any;
const names = Object.keys(e).filter(k =>
typeof e[k] === "number"
|| e[k] === k
|| e[e[k]]?.toString() !== k
);
Member Values
Once, we have the names, we can loop over them to get the corresponding value by doing:
const values = names.map(k => MyEnum[k]);
Extension Class
I think the best way to do this is to create your own functions (ex. EnumEx.getNames(MyEnum)). You can't add a function to an enum.
class EnumEx {
private constructor() {
}
static getNamesAndValues(e: any) {
return EnumEx.getNames(e).map(n => ({ name: n, value: e[n] as string | number }));
}
static getNames(e: any) {
return Object.keys(e).filter(k =>
typeof e[k] === "number"
|| e[k] === k
|| e[e[k]]?.toString() !== k
);
}
static getValues(e: any) {
return EnumEx.getNames(e).map(n => e[n] as string | number);
}
}
With TypeScript >= 2.4 you can define string enums:
enum Color {
RED = 'Red',
ORANGE = 'Orange',
YELLOW = 'Yellow',
GREEN = 'Green',
BLUE = 'Blue',
INDIGO = 'Indigo',
VIOLET = 'Violet'
}
JavaScript ES5 output:
var Color;
(function (Color) {
Color["RED"] = "Red";
Color["ORANGE"] = "Orange";
Color["YELLOW"] = "Yellow";
Color["GREEN"] = "Green";
Color["BLUE"] = "Blue";
Color["INDIGO"] = "Indigo";
Color["VIOLET"] = "Violet";
})(Color || (Color = {}));
Which is an object like this:
const Color = {
"RED": "Red",
"ORANGE": "Orange",
"YELLOW": "Yellow",
"GREEN": "Green",
"BLUE": "Blue",
"INDIGO": "Indigo",
"VIOLET": "Violet"
}
Thus, in the case of string enums, no need to filter things,
Object.keys(Color) and Object.values(Color) are enough:
const colorKeys = Object.keys(Color) as (keyof typeof Color)[];
console.log('colorKeys =', colorKeys);
// ["RED", "ORANGE", "YELLOW", "GREEN", "BLUE", "INDIGO", "VIOLET"]
const colorValues = Object.values(Color);
console.log('colorValues =', colorValues);
// ["Red", "Orange", "Yellow", "Green", "Blue", "Indigo", "Violet"]
colorKeys.map(colorKey => {
console.log(`color key = ${colorKey}, value = ${Color[colorKey]}`);
});
/*
color key = RED, value = Red
color key = ORANGE, value = Orange
color key = YELLOW, value = Yellow
color key = GREEN, value = Green
color key = BLUE, value = Blue
color key = INDIGO, value = Indigo
color key = VIOLET, value = Violet
*/
See online example on TypeScript playground
There is no concept of RTTI (runtime type information) in TypeScript (think: reflection) so in order to do this, knowledge of the transpiled JavaScript is required. So, assuming TypeScript 0.95:
enum MyEnum {
First, Second, Third
}
becomes:
var MyEnum;
(function(MyEnum) {
MyEnum[MyEnum["First"] = 0] = "First";
MyEnum[MyEnum["Second"] = 1] = "Second";
MyEnum[MyEnum["Third"] = 2] = "Third";
}
So, this is modeled as a regular object in javascript, where MyEnum.0 == "First" and MyEnum.First == 0. So, to enumerate all of the enum names, you need to get all properties that belong to the object and that are also not numbers:
for (var prop in MyEnum) {
if (MyEnum.hasOwnProperty(prop) &&
(isNaN(parseInt(prop)))) {
console.log("name: " + prop);
}
}
Ok, so now I've told you how to do it, I'm allowed to tell you this is a bad idea. You're not writing a managed language, so you can't bring these habits. It's still just plain old JavaScript. If I wanted to use a structure in JavaScript to populate some kind of choices list, I would use a plain old array. An enum is not the right choice here, pun intended. The goal of TypeScript is to generate idiomatic, pretty JavaScript. Using enums in this way does not preserve this goal.
You can add functions to get the names and indices of the enum:
enum MyEnum {
First,
Second,
Third
}
namespace MyEnum {
function isIndex(key):boolean {
const n = ~~Number(key);
return String(n) === key && n >= 0;
}
const _names:string[] = Object
.keys(MyEnum)
.filter(key => !isIndex(key));
const _indices:number[] = Object
.keys(MyEnum)
.filter(key => isIndex(key))
.map(index => Number(index));
export function names():string[] {
return _names;
}
export function indices():number[] {
return _indices;
}
}
console.log("MyEnum names:", MyEnum.names());
// Prints: MyEnum names: ["First", "Second", "Third"]
console.log("MyEnum indices:", MyEnum.indices());
// Prints: MyEnum indices: [0, 1, 2]
Note that you could just export the _names and _indices consts rather than exposing them through an exported function, but because the exported members are members of the enum it is arguably clearer to have them as functions so they are not confused with the actual enum members.
It would be nice if TypeScript generated something like this automatically for all enums.
I used the solution proposed by David Sherret and wrote an npm library you can use named enum-values...
Git: enum-values
// Suppose we have an enum
enum SomeEnum {
VALUE1,
VALUE2,
VALUE3
}
// names will be equal to: ['VALUE1', 'VALUE2', 'VALUE3']
var names = EnumValues.getNames(SomeEnum);
// values will be equal to: [0, 1, 2]
var values = EnumValues.getValues(SomeEnum);
A one-liner to get a list of entries (key-value objects/pairs):
Object.keys(MyEnum).filter(a=>a.match(/^\D/)).map(name=>({name, value: MyEnum[name] as number}));
enum MyEnum {
First, Second, Third, NUM_OF_ENUMS
}
for(int i = 0; i < MyEnum.NUM_OF_ENUMS; ++i) {
// do whatever you need to do.
}
If you want to associate strings values to your enum these methods don't works. To have a generic function you can do :
function listEnum(enumClass) {
var values = [];
for (var key in enumClass) {
values.push(enum[key]);
}
values.length = values.length / 2;
return values;
}
It's works because TypeScript will add keys in first step, and values in second step.
In TypeScript it's:
var listEnums = <T> (enumClass: any): T[]=> {
var values: T[] = [];
for (var key in enumClass) {
values.push(enumClass[key]);
}
values.length = values.length / 2;
return values;
};
var myEnum: TYPE[] = listEnums<TYPE>(TYPE);
joe's answer just made me realize that is much more easier to rely on the first N numeric keys than making more complex testings:
function getEnumMembers(myEnum): string[]
{
let members = []
for(let i:number = 0; true; i++) {
if(myEnum[i] === undefined) break
members.push(myEnum[i])
}
return members
}
enum Colors {
Red, Green, Blue
}
console.log(getEnumMembers(myEnum))
Iterating over an enum
String Enums are best used for this. Here is an example:
// This is a string enum
enum MyEnum {
First = 'First',
Second = 'Second',
Third = 'Third',
}
// An enum is a TS concept
// However his MyEnum compiles to JS object:
// {
// "First": "First",
// "Second": "Second",
// "Third": "Third"
// }
// Therefore we can get the keys in the following manner:
const keysArray = Object.keys(MyEnum);
for (const key of keysArray) {
console.log(key)
}
// [LOG]: "First"
// [LOG]: "Second"
// [LOG]: "Third"
A type-safe solution could be as follows:
enum Color {
Blue = 'blue',
Green = 'green'
}
enum MoreColor {
Yellow,
Red
}
function getEnumValues<T, K extends keyof T>(enumType: T): Array<T[K]> {
return getEnumKeys<T, K>(enumType).map((x) => enumType[x]);
}
function getEnumKeys<T, K extends keyof T>(enumType: T): Array<K> {
return Object.keys(enumType)
.filter((x) => Number.isNaN(Number(x)))
.map((x) => x as K);
}
// return type is Color[]
const colorValues = getEnumValues(Color); // ["blue", "green"]
// return type is MoreColor[]
const moreColorValues = getEnumValues(MoreColor); // [0, 1]
// return type is Array<"Blue" | "Green">
const colorKeys = getEnumKeys(Color); // ["Blue", "Green"]
// return type is Array<"Yellow" | "Red">
const moreColorKeys = getEnumKeys(MoreColor); // ["Yellow", "Red"]
But keep in mind that this solution does not force you to pass just enums to the function.
for nodejs:
const { isNumber } = require('util');
Object.values(EnumObject)
.filter(val => isNumber(val))
.map(val => {
// do your stuff
})

Resources