mobx-state-tree convert optional type to non-optional - mobx-state-tree

How to convert optional type to non-optional
from types.optional(types.string) to types.optional
As far as I know this works:
const t = optional(types.string);
delete t.defaultValue
But this seems very wrong. Is there a better way?

You could use .named(name) method, which clones the given type, gives it a new name and provides a way for you to "extend" it with additional properties, views, actions, or perhaps, overrides of those declared in the original type.
Example:
const Square = types
.model("Square",
{
width: types.number
}
)
.views(self => ({
surface() {
return self.width * self.width
}
}))
// create a new type, based on Square
const Box = Square
.named("Box")
.views(self => {
// save the base implementation of surface
const superSurface = self.surface
return {
// super contrived override example!
surface() {
return superSurface() * 1
},
volume() {
return self.surface * self.width
}
}
}))

Related

Prae:Wrapper: need to use iter_mut of interior Vec but Prae:Wrapper only provides immutable access

I'm using the prae crate for validation and the following function gives me errors:
fn advance_rotors(&mut self) {
self.rotors.get()[0].rotate();
let mut iterhandle = self.rotors.iter_mut().peekable(); // Error at iter_mut() #0599
while let Some(el) = iterhandle.next() {
match iterhandle.peek_mut() {
Some(next_rotor) => match el.should_advance_next() {
true => {
next_rotor.rotate(); // This line requires mutable access to next_rotor
}
false => (),
},
None => (),
}
}
}
and the definition of my struct here:
pub struct Enigma {
reflector: Reflector,
rotors: RotorConfig, // Only mutable via getter and setter functions
}
the struct of interest here is RotorConfig which is generated using the define! macro from prae. Here's the code:
prae::define! {
#[derive(Debug)]
RotorConfig: Vec<Rotor>; // I need to be able to call the rotate method of each rotor in this vec. This requires mutability
validate(RotorConfigError) |config| {
match config.len(){
3..=4 => (),
_ => return Err(RotorConfigError::Size)
}
match config.iter().unique().count(){
3..=4 =>(),
_ => return Err(RotorConfigError::Duplicate)
}
Ok(())
};
}
the issue stems from the fact that prae only allows for immutable access to the internal representation via getter and setter functions so as to ensure the validity of the values inside. As you can see in my advance_rotors function I wrote before implementing validation I'm getting an error because I need to call rotor.rotate mutably. I'm at a loss as to how to accomplish this
After posting this I realized that I can simply provide interior mutability by using the following impl block
impl RotorConfig{
fn advance_rotors(&mut self)
{
self.0[0].rotate();
let mut iterhandle = self.0.iter_mut().peekable();
while let Some(el) = iterhandle.next() {
match iterhandle.peek_mut() {
Some(next_rotor) => match el.should_advance_next() {
true => {
next_rotor.rotate();
}
false => (),
},
None => (),
}
}
}
}
As you can see the function largely remains unchanged except that we replace self.rotors with self.0

Enum as Parameter in TypeScript

Isn't it possible to set the type of a parameter to an Enum? Like this:
private getRandomElementOfEnum(e : enum):string{
var length:number = Object.keys(e).length;
return e[Math.floor((Math.random() * length)+1)];
}
Following error is thrown:
Argument expression expected.(1135)
With any obviously everything is alright:
private getRandomElementOfEnum(e : any):string{
var length:number = Object.keys(e).length;
return e[Math.floor((Math.random() * length)+1)];
}
Is there a possibility or a little workaround to define an enum as a parameter?
It's not possible to ensure the parameter is an enum, because enumerations in TS don't inherit from a common ancestor or interface.
TypeScript brings static analysis. Your code uses dynamic programming with Object.keys and e[dynamicKey]. For dynamic codes, the type any is convenient.
Your code is buggy: length() doesn't exists, e[Math.floor((Math.random() * length)+1)] returns a string or an integer, and the enumeration values can be manually set…
Here is a suggestion:
function getRandomElementOfEnum<E>(e: any): E {
var keys = Object.keys(e),
index = Math.floor(Math.random() * keys.length),
k = keys[index];
if (typeof e[k] === 'number')
return <any>e[k];
return <any>parseInt(k, 10);
}
function display(a: Color) {
console.log(a);
}
enum Color { Blue, Green };
display(getRandomElementOfEnum<Color>(Color));
Ideally, the parameter type any should be replaced by typeof E but the compiler (TS 1.5) can't understand this syntax.
You can do better than any:
enum E1 {
A, B, C
}
enum E2 {
X, Y, Z
}
function getRandomElementOfEnum(e: { [s: number]: string }): number {
/* insert working implementation here */
return undefined;
}
// OK
var x: E1 = getRandomElementOfEnum(E1);
// Error
var y: E2 = getRandomElementOfEnum(window);
// Error
var z: string = getRandomElementOfEnum(E2);
I agree with #Tarh. Enums in TypeScript are just Javascript objects without a common interface or prototype (and if they are const enum, then they are not even objects), so you cannot restrict types to "any enum".
The closest I could get is something like the following:
enum E1 {
A, B, C
}
enum E2 {
X, Y, Z
}
// make up your own interface to match TypeScript enums
// as closely as possible (not perfect, though)
interface Enum {
[id: number]: string
}
function getRandomElementOfEnum(e: Enum): string {
let length = Object.keys(e).length / 2;
return e[Math.floor((Math.random() * length))];
}
This works for all enums (without custom initializers), but it would also accept other arrays as input (and then fail because the method body relies on the very specific key structure that TypeScript enums have).
So unless you have a real need for such a "generic" function, make typesafe functions for the individual enum types (or a union type like E1|E2|E3) that you actually need.
And if you do have this need (and this might very well be an X-Y-problem that can be solved in a better, completely different way given more context), use any, because you have left typesafe territory anyway.
Summing up the previous answers with some new syntax - a generic typesafe function, which works with numeric enums as well as string enums:
function getRandomElementOfEnum<T extends {[key: number]: string | number}>(e: T): T[keyof T] {
const keys = Object.keys(e);
const randomKeyIndex = Math.floor(Math.random() * keys.length);
const randomKey = keys[randomKeyIndex];
// Numeric enums members also get a reverse mapping from enum values to enum names.
// So, if a key is a number, actually it's a value of a numeric enum.
// see https://www.typescriptlang.org/docs/handbook/enums.html#reverse-mappings
const randomKeyNumber = Number(randomKey);
return isNaN(randomKeyNumber)
? e[randomKey as keyof T]
: randomKeyNumber as unknown as T[keyof T];
}
Another possible option not mentioned above is using the actual values. This is however possible only when you know all the options. This, in my opinion is definitely better than any.
doSomething(a: string, b: 'this'|'can'|'work'): void {
//do something
}
Tested on TypeScript 3.9.7
Solution
type EnumTypeString<TEnum extends string> =
{ [key in string]: TEnum | string; }
type EnumTypeNumber<TEnum extends number> =
{ [key in string]: TEnum | number; }
| { [key in number]: string; }
type EnumType<TEnum extends string | number> =
(TEnum extends string ? EnumTypeString<TEnum> : never)
| (TEnum extends number ? EnumTypeNumber<TEnum> : never)
type EnumOf<TEnumType> = TEnumType extends EnumType<infer U>
? U
: never
Usage
EnumType:
function forEachEnum<TEnum extends string | number>(
enumType: EnumType<TEnum>,
callback: (value: TEnum, key: string) => boolean|void,
) {
for (let key in enumType) {
if (Object.prototype.hasOwnProperty.call(enumType, key) && isNaN(Number(key))) {
const value = enumType[key] as any
if (callback(value, key)) {
return
}
}
}
}
EnumOf:
function forEachEnum2<TEnumType>(
enumType: TEnumType,
callback: (value: EnumOf<TEnumType>, key: string) => boolean|void,
) {
for (let key in enumType) {
if (Object.prototype.hasOwnProperty.call(enumType, key) && isNaN(Number(key))) {
const value = enumType[key] as any
if (callback(value, key)) {
return
}
}
}
}
Tests
enum EnumAsString {
Value1 = 'value 1',
Value2 = 'value 2',
}
enum EnumAsNumber {
Value1 = 1,
Value2 = 2,
}
// Error
let sn: EnumType<string> = EnumAsNumber
// Correct
let ns: EnumType<number> = EnumAsString // I have not found a solution for the error here
let nn: EnumType<number> = EnumAsNumber
let Nn: EnumType<EnumAsNumber> = EnumAsNumber
let ss: EnumType<string> = EnumAsString
let Ss: EnumType<EnumAsString> = EnumAsString
forEachEnum(EnumAsString, value => {
let e: EnumAsString = value
let s: string = value
let n: number = value // Error
})
forEachEnum(EnumAsNumber, value => {
let e: EnumAsNumber = value
let s: string = value // Error
let n: number = value
})
forEachEnum2(EnumAsString, value => {
let e: EnumAsString = value
let s: string = value
let n: number = value // Error
})
forEachEnum2(EnumAsNumber, value => {
let e: EnumAsNumber = value
let s: string = value // Error
let n: number = value
})
May be this trick will fit:
enum AbstractEnum { // put somewhere in hidden scope
}
private getRandomElementOfEnum(e: typeof AbstractEnum) {
...
}
#selinathat's solution is great only if you have few types. but what if we have more ? for example :
doSomething(a: string, b: 'this'|'can'|'work'|'test1'|'test2'|'test3'): void {
//do something
}
its pretty ugly hah !?
i prefer to use keyof :
interface Items {
'this',
'can',
'work',
'test1',
'test2',
'test3',
}
doSomething(a: string, b: keyof Items): void {
//do something
}
Here is an example that allows passing an enum with a typechecked value of that enum using a generic. It's really a response to a slightly different question here that was marked as a duplicate: Typescript how to pass enum as Parameter
enum Color {
blue,
};
enum Car {
cadillac,
};
enum Shape {
square,
}
type SupportedEnums = typeof Color | typeof Car;
type InvertTypeOf<T> = T extends typeof Color ? Color :
T extends typeof Car ? Car : never;
function getText<T extends SupportedEnums>(enumValue: InvertTypeOf<T>, typeEnum: T) string | undefined {
if (typeEnum[enumValue]) {
return `${enumValue}(${typeEnum[enumValue]})`;
}
}
console.log(getText(Car.cadillac, Car)); // 0(cadillac)
console.log(getText(0, Color)); // 0(red)
console.log(getText(4, Color)); // undefined
// #ts-expect-error Color is not Car
console.log(getText(Color.blue, Car));
// #ts-expect-error Car is not a Color
console.log(getText(Car.toyota, Color));
// #ts-expect-error Shape is not in SupportedEnums
console.log(getText(5, Shape));
// #ts-expect-error Shape is not in SupportedEnums
console.log(getText(Shape.square, Shape));
I had the same kind of problem, and i did this
private getOptionsFromEnum(OptionEnum: Record<string, string>): Array<SelectOption> {
return Object.keys(OptionEnum).map((value) => {
return {
name: OptionEnum[value],
value,
} as SelectOption;
});
}
I made a helper type to accept any enum as a paramaeter, then you can handle whatever you need next with Object or by calling an index of the Enum.
type Enum = Record<string | number, string | number>
Now use it to accept any enum as parameter:
function enumValues<T extends Enum>(enum: T, filter?: "string"): string[];
function enumValues<T extends Enum>(enum: T, filter?: "number"): number[];
function enumValues<T extends Enum>(enum: T, filter?: undefined): (string|number)[];
function enumValues<T extends Enum>(enum: T, filter?: "string" | "number") {
return Object.values(enum).filter(x => !filter || typeof x === filter);
}
enum color {
red,
green,
blue
}
console.log(enumValues(color,"string"));
// output ['red','green','blue']
console.log(enumValues(color,"number"));
// output [0,1,2]

Add functions to an Enum

Is it possible to add functions to an Enum type in TypeScript?
for example:
enum Mode {
landscape,
portrait,
// the dream...
toString() { console.log(this); }
}
Or:
class ModeExtension {
public toString = () => console.log(this);
}
enum Mode extends ModeExtension {
landscape,
portrait,
}
Of course the toString() function would contain something like a switch But a use-case would flow along the lines of:
class Device {
constructor(public mode:Mode) {
console.log(this.mode.toString());
}
}
I understand why extending an enum might be a strange thing, just wondering if it is possible.
You can either have a class that is separate to the Enum and use it to get things you want, or you can merge a namespace into the Enum and get it all in what looks like the same place.
Mode Utility Class
So this isn't exactly what you are after, but this allows you to encapsulate the "Mode to string" behaviour using a static method.
class ModeUtil {
public static toString(mode: Mode) {
return Mode[mode];
}
}
You can use it like this:
const mode = Mode.portrait;
const x = ModeUtil.toString(mode);
console.log(x);
Mode Enum/Namespace Merge
You can merge a namespace with the Enum in order to create what looks like an Enum with additional methods:
enum Mode {
X,
Y
}
namespace Mode {
export function toString(mode: Mode): string {
return Mode[mode];
}
export function parse(mode: string): Mode {
return Mode[mode];
}
}
const mode = Mode.X;
const str = Mode.toString(mode);
alert(str);
const m = Mode.parse(str);
alert(m);
You can get the string value of an non-const enum by using square brackets:
class Device {
constructor(public mode:Mode) {
console.log(Mode[this.mode]);
}
}
You can also put some enum-specific util functions into the enum, but that's just like static class members:
enum Mode {
landscape,
portrait
}
namespace Mode {
export function doSomething(mode:Mode) {
// your code here
}
}
Convert your enum to the enum pattern. I find this is a better practice in general for many languages, since otherwise you restrict encapsulation options for your type. The tendency is to switch on the enum value, when really any data or functionality that is dependent on the particular enum value should just go into each instance of the enum. I've added some more code to demonstrate.
This might not work if you are particularly dependent on the underlying enum values. In that case you would need to add a member for the old values and convert places that need it to use the new property.
class Mode {
public static landscape = new Mode(1920, 1080);
public static portrait = new Mode(1080, 1920);
public get Width(): number { return this.mWidth; }
public get Height(): number { return this.mHeight; }
// private constructor if possible in a future version of TS
constructor(
private mWidth: number,
private mHeight: number
) {
}
public GetAspectRatio() {
return this.mWidth / this.mHeight;
}
}
An addition to Fenton's solution.
If you want to use this enumerator in another class, you need to export both the enum and the namespace. It would look like this:
export enum Mode {
landscape,
portrait
}
export namespace Mode {
export function toString(mode: Mode): string {
return Mode[mode];
}
}
Then you just import the mode.enum.ts file in your class and use it.
can make enum like by private constructor and static get return object
export class HomeSlideEnum{
public static get friendList(): HomeSlideEnum {
return new HomeSlideEnum(0, "friendList");
}
public static getByOrdinal(ordinal){
switch(ordinal){
case 0:
return HomeSlideEnum.friendList;
}
}
public ordinal:number;
public key:string;
private constructor(ordinal, key){
this.ordinal = ordinal;
this.key = key;
}
public getTitle(){
switch(this.ordinal){
case 0:
return "Friend List"
default :
return "DChat"
}
}
}
then later can use like this
HomeSlideEnum.friendList.getTitle();
ExtendedEnum Class
I always loved the associated types in Swift and I was looking to extend Typescript's enum basic functionality. The idea behind this approach was to keep Typescript enums while we add more properties to an enum entry. The proposed class intends to associate an object with an enum entry while the basic enum structure stays the same.
If you are looking for a way to keep vanilla Typescript enums while you can add more properties to each entry, this approach might be helpful.
Input
enum testClass {
foo = "bar",
anotherFooBar = "barbarbar"
}
Output
{
entries: [
{
title: 'Title for Foo',
description: 'A simple description for entry foo...',
key: 'foo',
value: 'bar'
},
{
title: 'anotherFooBar',
description: 'Title here falls back to the key which is: anotherFooBar.',
key: 'anotherFooBar',
value: 'barbarbar'
}
]
}
Implementation
export class ExtendedEnum {
entries: ExtendedEnumEntry[] = []
/**
* Creates an instance of ExtendedEnum based on the given enum class and associated descriptors.
*
* #static
* #template T
* #param {T} enumCls
* #param {{ [key in keyof T]?: EnumEntryDescriptor }} descriptor
* #return {*} {ExtendedEnum}
* #memberof ExtendedEnum
*/
static from<T extends Object>(enumCls: T, descriptor: { [key in keyof T]?: EnumEntryDescriptor }): ExtendedEnum {
const result = new ExtendedEnum()
for (const anEnumKey of Object.keys(enumCls)) {
if (isNaN(+anEnumKey)) { // skip numerical keys generated by js.
const enumValue = enumCls[anEnumKey]
let enumKeyDesc = descriptor[anEnumKey] as EnumEntryDescriptor
if (!enumKeyDesc) {
enumKeyDesc = {
title: enumValue
}
}
result.entries.push(ExtendedEnumEntry.fromEnumEntryDescriptor(enumKeyDesc, anEnumKey, enumValue))
}
}
return result
}
}
export interface EnumEntryDescriptor {
title?: string
description?: string
}
export class ExtendedEnumEntry {
title?: string
description?: string
key: string
value: string | number
constructor(title: string = null, key: string, value: string | number, description?: string) {
this.title = title ?? key // if title is not provided fallback to key.
this.description = description
this.key = key
this.value = value
}
static fromEnumEntryDescriptor(e: EnumEntryDescriptor, key: string, value: string | number) {
return new ExtendedEnumEntry(e.title, key, value, e.description)
}
}
Usage
enum testClass {
foo = "bar",
anotherFooBar = "barbarbar"
}
const extendedTestClass = ExtendedEnum.from(testClass, {
foo: {
title: "Title for Foo",
description: "A simple description for entry foo..."
},
anotherFooBar: {
description: "Title here falls back to the key which is: anotherFooBar."
}
})
Advantages
No refactor needed, keep the basic enum structures as is.
You get code suggestions for each enum entry (vs code).
You get an error if you have a typo in declaring the enum descriptors aka associated types.
Get the benefit of associated types and stay on top of OOP paradigms.
It is optional to extend an enum entry, if you don't, this class generates the default entry for it.
Disadvantages
This class does not support enums with numerical keys (which shouldn't be annoying, because we usually use enums for human readability and we barely use numbers for enum keys)
When you assign a numerical value to a key; Typescript double-binds the enum key-values. To prevent duplicate entries, this class only considers string keys.

Swift getter code repetition

I'm trying to have variables in swift that are critical app-wide user settings so they must be persisted to disk after every change. There is a small amount of these variables and I'm content with the first read happening from disk after the app starts.
I have code that looks similar to this:
var _myEnumMember:MyEnum?
var myEnumMember:MyEnum {
get {
if let value = _myEnumMember { // in memory
return value
}
var c:Cache = Cache()
var storedValue:MyEnum? = c.get("SomeStorageKey");
if let value = storedValue { // exists on disk
self.myEnumMember = value // call setter to persist
return self.myEnumMember // call getter again with value set
}
self.myEnumMember = .DefaultValue // assign via setter
return self.rankingDuration // call getter after `set`
}
set (newValue){
self._myEnumMember = newValue // assign to memory
var c:Cache = Cache()
c.put("SomeStorageKey", data: ser) // store in disk
}
I have about 5-6 properties that need to do this - I don't want to repeat myself over and over - is there any way to DRY this code up so I won't have to repeat this logic in several places?
(Note: Asking here and not CR.SE because I'd like answers to explain how to DRY getters/setters in these situations rather than receive critique on a particular piece of code)
I was working on something similar recently - this may be your best bet. I used this as a nested struct, but it doesn't strictly need to be nested.
First, define a LocalCache type that will handle the persistence of your properties:
struct LocalCache {
// set up keys as constants
// these could live in your class instead
static let EnumKey = "EnumKey"
static let IntKey = "IntKey"
static let StringKey = "StringKey"
// use a single cache variable, hopefully?
var cache = Cache()
// in-memory values go in a Dictionary
var localValues: [String: Any] = [:]
// fetch from local storage or from disk
// note that the default value also sets the return type
mutating func fetch<T>(key: String, defaultValue: T) -> T {
if localValues[key] == nil {
localValues[key] = cache.get(key) ?? defaultValue
}
return localValues[key]! as T
}
// save in both local storage and to disk
mutating func store(key: String, _ value: Any) {
localValues[key] = value
cache.put(key, data: value)
}
}
Then add a LocalCache instance to your class, and you can have much simpler getter/setters.
class Test {
// instance of the local cache
var localCache = LocalCache()
var enumPropery: MyEnum {
get { return localCache.fetch(LocalCache.EnumKey, defaultValue: MyEnum.DefaultValue) }
set { localCache.store(LocalCache.EnumKey, newValue) }
}
var intProperty: Int {
get { return localCache.fetch(LocalCache.IntKey, defaultValue: 0) }
set { localCache.store(LocalCache.IntKey, newValue) }
}
var stringProperty: String {
get { return localCache.fetch(LocalCache.StringKey, defaultValue: "---") }
set { localCache.store(LocalCache.StringKey, newValue) }
}
}
If you're using swift in an iOS or OS X context then NSUserDefaults are ABSOLUTELY the right way to do this.
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSUserDefaults_Class/index.html
http://www.codingexplorer.com/nsuserdefaults-a-swift-introduction/

Enums with data in swift

I would like to use java-like enums, where you can have enum instances with custom data. For instance:
enum Country {
case Moldova(capital: "Chișinău", flagColors: [Color.Blue, Color.Yellow, Color.Red]);
case Botswana(capital: "Gaborone", flagColors: [Color.Blue, Color.White, Color.Black]);
}
I could later write:
Country.Moldova.capital;
It seems that I can indicate the variables, but not the values, and I can only assign the values when using the enum, not declaring. Which would be the best way to mimic this behaviour?
you can do something like this, which may be helpful: (that is a very generic example only)
enum Country : Int {
case Moldova, Botwana;
//
func capital() -> String {
switch (self) {
case .Moldova:
return "Chișinău"
case .Botwana:
return "Gaborone"
default:
return ""
}
}
//
func flagColours() -> Array<UIColor> {
switch (self) {
case .Moldova:
return [UIColor.blueColor(), UIColor.yellowColor(), UIColor.redColor()]
case .Botwana:
return [UIColor.blueColor(), UIColor.whiteColor(), UIColor.blackColor()]
default:
return []
}
}
//
func all() -> (capital: String, flagColours: Array<UIColor>) {
return (capital(), flagColours())
}
//
var capitolName: String {
get {
return capital()
}
}
//
var flagColoursArray: Array<UIColor> {
get {
return flagColours()
}
}
}
then you can access to the details like this:
let country: Country = Country.Botwana
get the capital
that way:
let capital: String = country.capital()
or another:
let capital: String = country.all().capital
or a third one:
let capital: String = country.capitolName
get the flag's colours:
that way:
let flagColours: Array<UIColor> = country.flagColours()
or another:
let flagColours: Array<UIColor> = country.all().flagColours
or a third one:
let flagColours: Array<UIColor> = country.flagColoursArray
Here is another generic example that is similar to the one posted by holex, but a bit closer to how it looks in Java. (I like it because all of the custom data is in one place). Instead of switching on 'self' in separate methods, I simply create a static dictionary that maps each case to a tuple containing the appropriate data. Then, I have convenience var's to get at the data easily.
enum TestEnum {
case One
case Two
case Three
private static let associatedValues = [
One: (int: 1, double: 1.0, string: "One"),
Two: (int: 2, double: 2.0, string: "Two"),
Three: (int: 3, double: 3.0, string: "Three")
]
var int: Int {
return TestEnum.associatedValues[self]!.int;
}
var double: Double {
return TestEnum.associatedValues[self]!.double;
}
var string: String {
return TestEnum.associatedValues[self]!.string;
}
}
You can access the custom data like so:
println(TestEnum.One.int) // 1
println(TestEnum.Two.double) // 2.0
println(TestEnum.Three.string) // Three
Unfortunately it seems like enums with raw values are limited to literal values. You may want to file a bug.
As an alternative, you could do something like this:
let Country = (
Moldova: (capital: "Chișinău", flagColors: [Color.Blue, Color.Yellow, Color.Red]),
Botswana: (capital: "Gaborone", flagColors: [Color.Blue, Color.White, Color.Black])
)
or this:
struct Country {
let capital: String
let flagColors: [Color]
}
let Countries = (
Moldova: Country(capital: "Chișinău", flagColors: [.Blue, .Yellow, .Red]),
Botswana: Country(capital: "Gaborone", flagColors: [.Blue, .White, .Black])
)

Resources