reasonml record vs JS object - ocaml

Say I define the following types:
type queueParams = {
durable: bool
};
class type amqpChannelT = [#bs] {
pub assertQueue: string => queueParams => Js.Promise.t(unit);
};
Then calling the following:
channel##assertQueue("exampleQueue", {"durable": bool});
Results in:
This has type:
{. "durable": bool}
But somewhere wanted:
queueParams (defined as
How can I pass the right type of thing? Why is the thing I'm passing not a record? And what is the meaning of that dot notation?

ReasonML interprets these two differently:
let jsObject = {"durable": true};
let reasonRecord = {durable: true};
Basically, wrapping keys with double-quotes is a short-hand notation for the special Javascript object type Js.t('a) - which is currently deprecated.
You can play around with an example here. Note how the two types are treated differently when converted to Javascript.
Read more about the deprecated syntax here:
https://bucklescript.github.io/docs/en/object-deprecated

Related

How to write reasonml binding for a union type

I am trying to write bindings for https://github.com/oblador/react-native-keychain/blob/master/typings/react-native-keychain.d.ts#L76
getGenericPassword returns false if an error, else an object (credentials). I am not sure this union type can be represented in reason, but a better API would be the result an option (option(credentials)). But, how can I convert Promise<boolean | credentials> -> Js.Promise.t(option(credentials)) in the binding file. Below is a template.
Thanks for your help.
[#bs.deriving abstract]
type credentials = {
service: string,
username: string,
password: string,
};
/* TODO convert the actual return value
Js.Promise.t(option(credentials)) to more reason type
Js.Promise.t(option(credentials)) */
[#bs.module "react-native-keychain"] [#bs.scope "default"]
external getGenericPassword: unit => Js.Promise.t(option(credentials)) = "";
You can use Js.Types.classify to get the runtime type of a value.
type maybeCredentials;
[#bs.module "react-native-keychain"] [#bs.scope "default"]
external getGenericPassword: unit => Js.Promise.t(maybeCredentials) = "";
let getGenericPassword: unit => Js.Promise.t(option(credentials)) =
() =>
Js.Promise.(
getGenericPassword()
|> then_(maybeCredentials =>
switch (Js.Types.classify(maybeCredentials)) {
| JSObject(obj) => resolve(Some(obj |> Obj.magic))
| _ => resolve(None)
}
)
);
Here maybeCredentials is defined and used as an intermediate type.
We then define a function with the same name as the binding, which will "shadow" the name and prevent the binding from being used directly in favour of our "override". However, within the override we're still able to use the binding.
We then call Js.Types.classify to get the runtime type of the returned value. If it is an object we use Obj.magic to cast the abstract obj_type to our credentials type (inferred from the return type of the function), and wrap it in an option. For any other type we return None.
By the way, this kind of "type" is called an untagged union. I've written down a few examples using different strategies for dealing with these, as both a producer and a consumer, in bucklescript-cookbook.

Angular/Typescript RegExp enum

Looking to create an enum for some regex to store in my application to allow for code reuse.
Eg:
export enum Regex {
ONE_DANK_REGEX = /^[dank]+$/g,
FIVE_OUT_OF_SEVEN = /^[meme]{5,7}$/g
}
But:
By not using string literals, I get the TS90010 error: Type RegExpis not assignable to type Regex.
By using string literals, I can't use these in a pattern attribute on my inputs
Eg.
<input ngModel="..." pattern="{{FIVE_OUT_OF_SEVEN}}" .../>
Is this the right way of going about doing this?
You cannot assign a RegExp to an enum, an enum can either be numeric or string-based.
So when it comes to storing the RegExp you have two options:
Option A - store the RegExp as string in an enum
enum RegExpEnum {
ONE_DANK_REGEX = "^[dank]+$",
FIVE_OUT_OF_SEVEN = "^[meme]{5,7}$"
}
Option B - store the RegExp in a class/variable
class RegexClass {
public static readonly ONE_DANK_REGEX = /^[dank]+$/g;
public static readonly FIVE_OUT_OF_SEVEN = /^[meme]{5,7}$/g;
}
In both ways you need to get a reference to the containing enum or class in your component. Which can be achieved by assigning it to a local variable.
#Component({ ... })
public class MyComponent {
availableRegex = RegExpEnum; // or RegexClass
}
Then you can access it in the html part of your component.
<input [(ngModel)]="name" [pattern]="availableRegex.ONE_DANK_REGEX"/>
DEMO
Enum will support only strings and Number.
If you wanted to store regular expressions, you need to use static class
export class RegularExpression
{
public static ALPHA_CHARACTERS: RegExp = /([^A-Za-z])+/g;
}
Usage
'Your_* Name'.replace(RegularExpression.ALPHA_CHARACTERS, '');
Prefer the option A mentioned on the currently accepted answer and use it as below the code:
export enum Regex {
ONE_DANK_REGEX = "^[dank]+$",
FIVE_OUT_OF_SEVEN = "^[meme]{5,7}$",
}
public readonly regOne = new RegExp(Regex.ONE_DANK_REGEX, "g");
This gives some flexibility when create the regular expression object.

How to override KeyValuePair<TKey, TValue> in C#?

I want to override the default structure of KeyValuePair in C#, so that I can make a KeyValuePair to accept a 'var' types.
Something like this :
List<KeyValuePair<string, var>> kvpList = new List<KeyValuePair<string, var>>()
{
new KeyValuePair<string, var>("Key1", 000),
new KeyValuePair<string, var>("Key2", "value2"),
new KeyValuePair<string, var>("Key3", 25.45),
};
Even if its possible for dictionary, then also it will solve my problem.
You could use object as your type, and then cast to/from object to desired outcomes. However, it's important to note that this is very much the opposite of object oriented programming, and generally indicates an error in your design and architecture.
Hmm I am wondering if this might help you: To have a list as you want, it is really possible BUT the "var" type (as you named it) must be the same for all KeyValuePair instances. For having whatever type you must use object or dynamic (use Haney's answer).
So considering that you want a single type for all KeyValuePair instances, here is a solution:
Firstly, create this helper class:
public static class KeyValuePairExtentions
{
public static List<KeyValuePair<string, T>> GetNewListOfType<T>(Expression<Func<T>> type)
{
return new List<KeyValuePair<string, T>>();
}
public static void AddNewKeyValuePair<T>(this List<KeyValuePair<string, T>> #this, string key, T element)
{
#this.Add(new KeyValuePair<string, T>(key, element));
}
}
To consume these functions, here is an example:
var lst = KeyValuePairExtentions.GetNewListOfType(() => new {Id = default (int), Name = default (string)});
lst.AddNewKeyValuePair("test1", new {Id = 3, Name = "Keith"});
The ideea is to rely on the powerfull type inference feature that we have in C#.
Some notes:
1) if T is anonymous and you create a new instance of a list in an assembly and consume it in another assembly it is VERY possible that this will NOT work due to the fact that an anonymous type is compiled per assembly (in other words, if you have a variable var x = new { X = 3 } in an assembly and in another var y = new { X = 3 } then x.GetType () != y.GeTType () but in the same assembly types are the same.)
2) If you are wondering whether an instance it's created or not by calling GetNewListOfType, the answer is NO because it is an expression tree function and the function is not even compiled. Even with a Func will work because I am not calling the function in my code. I am using the function just for type inference.

Build a function object with properties in TypeScript

I want to create a function object, which also has some properties held on it. For example in JavaScript I would do:
var f = function() { }
f.someValue = 3;
Now in TypeScript I can describe the type of this as:
var f: { (): any; someValue: number; };
However I can't actually build it, without requiring a cast. Such as:
var f: { (): any; someValue: number; } =
<{ (): any; someValue: number; }>(
function() { }
);
f.someValue = 3;
How would you build this without a cast?
Update: This answer was the best solution in earlier versions of TypeScript, but there are better options available in newer versions (see other answers).
The accepted answer works and might be required in some situations, but have the downside of providing no type safety for building up the object. This technique will at least throw a type error if you attempt to add an undefined property.
interface F { (): any; someValue: number; }
var f = <F>function () { }
f.someValue = 3
// type error
f.notDeclard = 3
This is easily achievable now (typescript 2.x) with Object.assign(target, source)
example:
The magic here is that Object.assign<T, U>(t: T, u: U) is typed to return the intersection T & U.
Enforcing that this resolves to a known interface is also straight-forward. For example:
interface Foo {
(a: number, b: string): string[];
foo: string;
}
let method: Foo = Object.assign(
(a: number, b: string) => { return a * a; },
{ foo: 10 }
);
which errors due to incompatible typing:
Error: foo:number not assignable to foo:string
Error: number not assignable to string[] (return type)
caveat: you may need to polyfill Object.assign if targeting older browsers.
TypeScript is designed to handle this case through declaration merging:
you may also be familiar with JavaScript practice of creating a function and then extending the function further by adding properties onto the function. TypeScript uses declaration merging to build up definitions like this in a type-safe way.
Declaration merging lets us say that something is both a function and a namespace (internal module):
function f() { }
namespace f {
export var someValue = 3;
}
This preserves typing and lets us write both f() and f.someValue. When writing a .d.ts file for existing JavaScript code, use declare:
declare function f(): void;
declare namespace f {
export var someValue: number;
}
Adding properties to functions is often a confusing or unexpected pattern in TypeScript, so try to avoid it, but it can be necessary when using or converting older JS code. This is one of the only times it would be appropriate to mix internal modules (namespaces) with external.
So if the requirement is to simply build and assign that function to "f" without a cast, here is a possible solution:
var f: { (): any; someValue: number; };
f = (() => {
var _f : any = function () { };
_f.someValue = 3;
return _f;
})();
Essentially, it uses a self executing function literal to "construct" an object that will match that signature before the assignment is done. The only weirdness is that the inner declaration of the function needs to be of type 'any', otherwise the compiler cries that you're assigning to a property which does not exist on the object yet.
EDIT: Simplified the code a bit.
Old question, but for versions of TypeScript starting with 3.1, you can simply do the property assignment as you would in plain JS, as long as you use a function declaration or the const keyword for your variable:
function f () {}
f.someValue = 3; // fine
const g = function () {};
g.someValue = 3; // also fine
var h = function () {};
h.someValue = 3; // Error: "Property 'someValue' does not exist on type '() => void'"
Reference and online example.
As a shortcut, you can dynamically assign the object value using the ['property'] accessor:
var f = function() { }
f['someValue'] = 3;
This bypasses the type checking. However, it is pretty safe because you have to intentionally access the property the same way:
var val = f.someValue; // This won't work
var val = f['someValue']; // Yeah, I meant to do that
However, if you really want the type checking for the property value, this won't work.
I can't say that it's very straightforward but it's definitely possible:
interface Optional {
<T>(value?: T): OptionalMonad<T>;
empty(): OptionalMonad<any>;
}
const Optional = (<T>(value?: T) => OptionalCreator(value)) as Optional;
Optional.empty = () => OptionalCreator();
if you got curious this is from a gist of mine with the TypeScript/JavaScript version of Optional
An updated answer: since the addition of intersection types via &, it is possible to "merge" two inferred types on the fly.
Here's a general helper that reads the properties of some object from and copies them over an object onto. It returns the same object onto but with a new type that includes both sets of properties, so correctly describing the runtime behaviour:
function merge<T1, T2>(onto: T1, from: T2): T1 & T2 {
Object.keys(from).forEach(key => onto[key] = from[key]);
return onto as T1 & T2;
}
This low-level helper does still perform a type-assertion, but it is type-safe by design. With this helper in place, we have an operator that we can use to solve the OP's problem with full type safety:
interface Foo {
(message: string): void;
bar(count: number): void;
}
const foo: Foo = merge(
(message: string) => console.log(`message is ${message}`), {
bar(count: number) {
console.log(`bar was passed ${count}`)
}
}
);
Click here to try it out in the TypeScript Playground. Note that we have constrained foo to be of type Foo, so the result of merge has to be a complete Foo. So if you rename bar to bad then you get a type error.
NB There is still one type hole here, however. TypeScript doesn't provide a way to constrain a type parameter to be "not a function". So you could get confused and pass your function as the second argument to merge, and that wouldn't work. So until this can be declared, we have to catch it at runtime:
function merge<T1, T2>(onto: T1, from: T2): T1 & T2 {
if (typeof from !== "object" || from instanceof Array) {
throw new Error("merge: 'from' must be an ordinary object");
}
Object.keys(from).forEach(key => onto[key] = from[key]);
return onto as T1 & T2;
}
This departs from strong typing, but you can do
var f: any = function() { }
f.someValue = 3;
if you are trying to get around oppressive strong typing like I was when I found this question. Sadly this is a case TypeScript fails on perfectly valid JavaScript so you have to you tell TypeScript to back off.
"You JavaScript is perfectly valid TypeScript" evaluates to false. (Note: using 0.95)

Why aren't type parameters allowed in Scala auxiliary constructors?

Say I'm defining a simple 2D point class in Scala, and I want to be able to construct it with various types:
class Point(x:Float, y:Float) {
this(x:Double, y:Double) = this(x.toFloat, y.toFloat)
this(x:Int, y:Int) = this(x.toFloat, y.toFloat)
// etc...
}
I want to boil this down using a template, such as:
class Point(x:Float, y:Float) {
this[T](x:T, y:T) = this(x.toFloat, y.toFloat)
}
I know this won't work anyway, since T could be a type for which toFloat isn't defined, but the compiler error I get is:
no type parameters allowed here
Is this just unsupported in Scala? If so, why, and is there any simple way to get around this?
Scala's class constructors (unlike Java's) can't take type parameters, only the class itself can. As to why Scala made this design choice, I assume the main reason is simplicity.
If you want a secondary "builder" method that is generic, the natural thing to do is define it on the companion object. For example,
object Point {
def build[T : Numeric](x: T, y: T) = {
val n = implicitly[Numeric[T]]
new Point(n.toFloat(x), n.toFloat(y))
}
}
class Point(val x:Float, val y:Float)
val p = Point.build(1, 2) // Companion object's builder
p.x + p.y
Here I've used the Numeric typeclass to get a generic toFloat method.
I played with this for awhile, getting as "close" as...
class Point(x:Float, y:Float) {
def this[T <: Any { def toFloat: Float }](x:T, y:T) = this(x.toFloat, y.toFloat)
}
...which results in "error: no type parameters allowed here" (just as per the post) and then I realized...
If the initializer could take type parameters it would be ambiguous with the class parameters, if any. Not that this couldn't be worked about in the language specification... but it is a more complex case at the very least. There might also be Java interoperability issues.
Imagine:
class Foo[T](x: T) {
def this[X](z: X) = ...
}
new Foo[Int](42) // T is Int? X is ...? Or ...?
Personally I wish Scala followed an Eiffel-like pattern (only named constructors or "factory methods"), but alas, that would not be Scala.
Happy coding.