Closed vasilii-kovalev closed 4 years ago
Object.freeze
function will do what as const
does$TypeOf
magic type will do what typeof
does $Keys
magic type does what keyof
does but if you want values $Values
magic type will do thatconst FEATURES = Object.freeze({
SORTING: 'Sorting',
FILTERING: 'Filtering',
REORDERING: 'Reordering'
})
type Feature = $Values<$TypeOf<FEATURES>>
const features: Array<Feature> = []
void features.push('Filtering') // ok
void features.push(FEATURES.FILTERING) // ok
void features.push('foo') // error as expected
only down side would be that FEATURES
will be frozen on runtime as well (this may or may not be desired behavior)
if you don't like to freeze object on runtime you can create helper immutableId
function that will refine type but don't change value for runtime
const immutableId = <T>(v: T): $Immutable<T> => v
const FEATURES = immutableId({
SORTING: 'Sorting',
FILTERING: 'Filtering',
REORDERING: 'Reordering'
})
@thecotne, thanks for your examples. It makes the code a little bit easier, but the problem actually is in Feature
type definition repetition. I tried to apply immutableId
approach to Feature
, but failed of course :)
I tried to get something like this:
// without output type because have no ideas how to describe it
const makeUnion = (obj: object) => $Values<$TypeOf<obj>>;
type Feature = makeUnion(FEATURES);
but it seems that type definition doesn't work this way :)
this works perfectly fine you don't need to repeat anything ...
const immutableId = <T>(v: T): $Immutable<T> => v
const FEATURES = immutableId({
SORTING: 'Sorting',
FILTERING: 'Filtering',
REORDERING: 'Reordering'
})
type Feature = $Values<$TypeOf<FEATURES>>
const features: Array<Feature> = []
void features.push('Filtering') // ok
void features.push(FEATURES.FILTERING) // ok
void features.push('foo') // error as expected
you just need to use either Object.freeze
or immutableId
to achieve same result as as const
in typescript.
@thecotne, thanks for your reply!
Actually I found out the answer to my initial question (with your help and my own research). Here is code snippets with the solutions:
const FEATURES = Object.freeze({
SORTING: 'Sorting',
FILTERING: 'Filtering',
REORDERING: 'Reordering',
});
// "Array" should be outside
// type Values<T> = Array<$Values<T>>;
// 2. Define this type
// Array<'Filtering' | 'Reordering' | 'Sorting'>
const features: Array<$Values<$TypeOf<FEATURES>>> = [];
// OK
void features.push(FEATURES.FILTERING);
/*
Throws a type error
Argument of type '"foo"' is not assignable to parameter of type
'"Sorting" | "Filtering" | "Reordering"'.
*/
void features.push('foo');
// 1. Freeze the object
const FEATURES = {
SORTING: 'Sorting',
FILTERING: 'Filtering',
REORDERING: 'Reordering',
} as const;
type Values<T> = Array<T[keyof T]>;
// 2. Define this type
// ("Sorting" | "Filtering" | "Reordering")[]
const features: Values<typeof FEATURES> = [];
// OK
void features.push(FEATURES.FILTERING);
/*
Throws a type error
Argument of type '"foo"' is not assignable to parameter of type
'"Sorting" | "Filtering" | "Reordering"'.
*/
void features.push('foo');
The answer I tried to find out is Values
type implementation to reduce code repetitiveness.
For any reason Array<T>
can't be moved to Values
type in Hegel unlike TypeScript though.
If it is a known issue, I suppose this one can be closed.
@vasilii-kovalev i have reported Values
type issue separately https://github.com/JSMonk/hegel/issues/269
Let's say we have a dictionary:
It can be used like this:
I want to say to my
features
array that it can accept values of theFEATURES
object only. But in order to avoid creating a separate union type and support (possible) changes there and in FEATURES object, I'd like to collect values from the object and keep them as a union type. Thus if it is necessary to add/remove value, it can be done in only one place - in theFEATURES
object.To achieve this goal, in TypeScript it is necessary to define such monstrous type:
Are there any thoughts of optimizing this type generation to avoid repeating such boilerplate in Hegel? Like a help function or something.
And I'd like to know if it is a common thing to work with dictionaries this way or not. Because I'm new to strong type checking and maybe I think the wrong way here...
Thanks in advance.