0

I'm trying to accomplish something like following...I define a type for allowed values in array, but when I'm trying to add value to array, I get an error.
Here's the type definition:

export const SupportedFieldRules = {
    REQUIRED: 'required',
    NUMBER: 'number',
    BOOLEAN: 'boolean'
};

export type ValidationRule = keyof typeof SupportedFieldRules;

export class FieldModel {
    rules: ValidationRule[] = [];
}

And here how I want to use it:

const model = new FieldModel();
model.rules.push(SupportedFieldRules.REQUIRED);

I'm getting error for this:

Type 'string' is not assignable to type '"REQUIRED"'.

From what I understand, I have two issues here...One of them is that keys of SupportedFieldRules are uppercase and values are in lower, and I need to find out how to create type from values of SupportedFieldRules and not from keys (I do not want to depend on keys, only on values).
And the second issue, that I cannot push item into array, even if the keys and values of SupportedFieldRules are in the same case.

How I can solve it?
Thanks!

Alex Dn
  • 5,465
  • 7
  • 41
  • 79

1 Answers1

0

For the first issue, you want:

export type ValidationRule = (typeof SupportedFieldRules)[keyof typeof SupportedFieldRules];

For the second issue, you need to avoid the default "widening" of mutable object properties from string literals to strings. One way to do this is to run the object through an identity function that infers a type constrained by string for each property (compare to this answer):

function asLiterals<T extends string, U extends {[k: string]: T}>(obj: U): U { return obj; }
export const SupportedFieldRules = asLiterals({
    REQUIRED: 'required',
    NUMBER: 'number',
    BOOLEAN: 'boolean'
});

Another way is to use a namespace instead of an object:

export namespace SupportedFieldRules { 
  export const REQUIRED = 'required';
  export const NUMBER = 'number';
  export const BOOLEAN = 'boolean';
}
Matt McCutchen
  • 28,856
  • 2
  • 68
  • 75