I have this bit of coffeescript for declaring a form element
validation = {}
if isRequired
validation.required =
params: true
message: "Please enter a year between #{minYear} & #{maxYear}."
#Year = new inputFormItem
validation: validation
Is it possible to do an inline declaration of this?
Something like this but what I've written doesn't compile
#Year = new inputFormItem
validation:
if isRequired then required: { params: true, message: "Please enter a year between #{minYear} & #{maxYear}." }
I don't know if it's possible to put inline conditionals in object declarations
If required is true then I need the object otherwise I don't want a required object.
Try this:
#Year = new inputFormItem
validation:
required: if isRequired then { params: true, message: "Please enter a year between #{minYear} & #{maxYear}." } else null
This will default to null (you could do undefined if you want). If you want it to default to false, then you can do this:
#Year = new inputFormItem
validation:
required: isRequired and { params: true, message: "Please enter a year between #{minYear} & #{maxYear}." }
Related
I have the following function of type (obj:any)=>boolean that determines whether an object is string or not.
I want the JsDoc to be "smart" so that whenever I use if(isString(x)){ ...block... }, the highlighter treats x inside the block as a string.
So far I tried this without success:
/** #type {((obj:string)=>true)|((obj:any)=>false)} */
function isString(obj) {
return Object.prototype.toString.call(obj) === "[object String]";
}
How can I do it properly?
The following should do the trick. It uses the Typescript's narrowing with type predicates.
/** #type {(obj: any) => obj is String} */
function isString(obj) {
return Object.prototype.toString.call(obj) === "[object String]";
}
I have a LangBuilder macro class; it's used to build a langObjects:Map<String, Dynamic> of texts in various languages at compile time, and inject this structure in classes via #:build macro. Every item of the Map has a field for every language supported. So the result is:
#:build(LangBuilder.build())
class Lang{}
trace(Lang.langObjects["hello-world"].en); //outputs "Hello World!"
trace(Lang.langObjects["hello-world"].it); //outputs "Ciao Mondo!"
This works perfectly, but I thought I could make a cleaner job hiding the langObjects structure using a function getLangText with arguments the id of the text (e.g. "hello-world") and the language code (e.g. "it").
So I'm trying to add this function to classes:
public static function getLangText(id:String, lang:String)
Its non-macro version could be expressed as:
public static function getLangText(id:String, lang:String){
var _langObj_id = langObjects[id];
switch(lang){
case "it":
return _langObj_id.it;
case "en":
return _langObj_id.en;
}
return "Missing Translation";
If i translate this function as a macro with this code:
var code = macro {
var _langObj_id = langObjects[$i{"id"}];
switch($i{"lang"}){
case "it":
return _langObj_id.it;
case "en":
return _langObj_id.en;
}
return "Missing translation";
};
var myFunc:Function = {
args: [{
name: "id",
type: TPath({name: "String", pack: []}),
meta: null
},
{
name: "lang",
type: TPath({name: "String", pack: []}),
meta: null
}],
ret: (macro:String),
expr: macro $code
};
fields.push({
pos: Context.currentPos(),
name: "getLangText",
meta: null,
kind: FieldType.FFun(myFunc),
doc: null,
access: [Access.APublic, Access.AStatic]
});
... it works without problems. However I would like to know how it could be written without the switch, to make it more flexible and to learn something about haxe macros. I have seen some examples where fields could be accessed in macros with $p{} or with object.$fieldName. However the haxe manual warns that the second form could be used only for simple identifiers; for example object.${fieldName} would not work.
So I try this code:
var code = macro {
var l:String = $i{"lang"};
var _langObj_id = langObjects[$i{"id"}];
return _langObj_id.$l;
};
The compiler gives an error
Unknown identifier : l
on the line containing return _langObj_id.$l;.
Then i tried to use the $p{} reification:
var code = macro {
var _langObj_id = langObjects[$i{"id"}];
return macro $p{["_langObj_id", $i{"lang"}]};
};
But the error is similar:
Unknown identifier : lang
I can surely change the langObjects structure to Map<String, Map<String, String>> and then change the code to:
var code = macro {
return macro langObjects[$i{"id"}][$i{"lang"}];
};
I think this would work, but now i'm trying to understand why both _langObj_id.$lang and $p{["_langObj_id", $i{"lang"}]} wouldn't work, and what would be the correct way to access a field in a situation like that.
The value of the lang parameter is not known at compile- / macro-time, so I don't see how you could generate a field access expression like langObjects["mytext"].en. At runtime when getLangText() is actually called, lang could be "en", or anything else. So that would still require a switch-case, if-else-chain or reflection to handle all the possible values.
If instead of using being created by a build macro, getLangText() was an expression macro / a macro function, the function call would be evaluated at compile-time, and be replaced with the expression it returns. That would allow you to generate the appropriate field access expression based on the parameters. It could look something like this:
class Macro {
public static var langObjects = ["mytext" => {en: "hello", de: "hallo"}];
public static macro function getLangText(id:String, lang:String) {
return macro {
var langObject = Macro.langObjects[$v{id}];
langObject.$lang;
}
}
}
class Main {
static function main() {
trace(Macro.getLangText("mytext", "en"));
trace(Macro.getLangText("mytext", "de"));
}
}
Compiles to the following on the JS target:
Main.main = function() {
var langObject = Macro.langObjects.get("mytext");
console.log("source/Main.hx:3:",langObject.en);
var langObject1 = Macro.langObjects.get("mytext");
console.log("source/Main.hx:4:",langObject1.de);
};
Perhaps that's what you're looking for? Hard to say without knowing what problem you're trying to solve.
I know this is a very fundamental question but answer to this will solve many of my doubts.
val new_parent = ParentDetails(intent.extras.getString("name"),
intent.extras.getString("email"),
intent.extras.getString("parent_relation"),
intent.extras.getString("locationdata"))
println(new_parent.tostring())
The code above doesn't print the various fields and their values present in the class.
The ParentDetails is a model I have created with some fields that are initialized. The ParentDetails model:
class ParentDetails {
var parent_id: Int = 0
var parent_name: String = ""
var parent_email: String = ""
var parent_relation: String = ""
var parent_location: String=""
constructor(parent_name: String, parent_email: String, parent_relation: String,parent_location:String) {
this.parent_name = parent_name
this.parent_email = parent_email
this.parent_relation = parent_relation
this.parent_location = parent_location
}
public fun getparentId(): Int {
return parent_id
}
fun ParentDetailsprintme() {
println(parent_name)
println(parent_email)
println(parent_relation)
println(parent_location)
}
}
In fact, it prints null and accessing individual fields, it prints empty string(the way it was initialized).
How do we explain this?
As I understand your problem is that calling println(new_parent.tostring()) does not print what you would like to print in function ParentDetailsprintme.
First of all, you have a typo, the correct call would be new_parent.toString().
Note that it could have been simplified as println(new_parent).
It does not print that you defined in the ParentDetailsprintme method, as the method is not called.
What println(new_parent.toString()) prints, is actually the hashcode of the object, as this is the default behaviour of every object.
To make it work call it like println(new_parent.ParentDetailsprintme()) or override the toString() method for example as:
override fun toString() = "$parent_name $parent_email $parent_relation $parent_location"
then the following
val new_parent = ParentDetails("myName", "myEmail", "myParent_relation", "myLocationdata")
println(new_parent)
should print
myName myEmail myParent_relation myLocationdata
Kotlin's println function simply calls System.out.println(message) under the hood which will call String.valueOf() (e.g. String.valueOf(Object object) for objects, which will call the toString() method of the passed object).
/** Prints the given message and newline to the standard output stream. */
#kotlin.internal.InlineOnly
public inline fun println(message: CharArray) {
System.out.println(message)
}
Update ("Using data class method also works"):
If you make the class to be a data class:
data class ParentDetails(
val parent_id: Int = 0,
val parent_name: String = "",
val parent_email: String = "",
val parent_relation: String = "",
val parent_location: String = ""
)
and then you execute
val new_parent = ParentDetails(0, "myName", "myEmail", "myParent_relation", "myLocationdata")
println(new_parent)
you will receive as result
ParentDetails(parent_id=0, parent_name=myName, parent_email=myEmail, parent_relation=myParent_relation, parent_location=myLocationdata)
This is because data classes override the toString() function:
The compiler automatically derives the following members from all
properties declared in the primary constructor:
equals()/hashCode() pair;
toString() of the form "User(name=John, age=42)";
Did you check that you receive valid data from your intent.extras?
Also I suggest you use data class for your models.
It will look something like this:
data class ParentDetails(
var parent_id: Int = 0,
var parent_name: String = "",
var parent_email: String = "",
var parent_relation: String = "",
var parent_location: String = ""
)
You will be able to use it like this :
val new_parent = ParentDetails(
parent_name = intent.extras.getString("name"),
parent_email = intent.extras.getString("email"),
parent_relation = intent.extras.getString("parent_relation"),
parent_location = intent.extras.getString("locationdata")
)
println(new_parent.tostring())
As already mentioned, you have a typo. toString returns the hashcode of an object unless it's overridden to return something else. Look up the original implementation for more details.
By overriding the toString method, you change what it returns, and through that, what is printed when you print(someClass). DVarga showed that in their answer.
Data classes auto-generate a toString method containing the content of the class. So creating a data class is a shortcut to getting output containing the data.
The reason the method you had didn't work is because you didn't call it. if you call it instead of toString, you would get the data printed.
Also, toString is explicitly called when you print a class. You don't need to call print(someInstance.toString()), print(someInstance) is enough.
And while I'm writing an answer, you don't need to use secondary constructors in Kotlin. Primary constructors would shorten your code significantly, whether it's a data class or not. Also, you should look into naming conventions.
Let's say I have an interface:
interface IUser {
email: string;
id: number;
phone: string;
};
Then I have a function that expects a subset (or complete match) of that type. Maybe it will pass an entire object, maybe it will just pass in {email: "t#g.com"}. I want the type checker to allow for both.
Example:
function updateUser(user: IUser) {
// Update a "subset" of user attributes:
$http.put("/users/update", user);
}
Does Typescript support this sort of behavior yet? I could find it very useful, particularly with paradigms like Redux.
To clarify, the goal is:
Avoid re-writing an interface and manually setting all attributes to optional.
Avoid assignment of unexpected attributes (such as spelling mistakes).
Avoid imperative logic such as if statements, which forfeit benefits of compile time type checking.
UPDATE: Typescript has announced support for mapped types which should solve this problem once published.
It's worth noting that Partial<T>, as suggested in the accepted answer, makes all fields optional, which is not necessarily what you need.
If you want to make some fields required (e.g. id and email), you need to combine it with Pick:
type UserWithOptionalPhone = Pick<IUser, 'id' | 'email'> & Partial<IUser>
Some explanation:
What Pick does is that it lets you specify a subset of the interface succinctly (without creating a whole new interface repeating the field types, as suggested by other answers), and then lets you use those, and only those fields.
function hello1(user: Pick<IUser, 'id' | 'email'>) {
}
hello1({email: '#', id: 1}); //OK
hello1({email: '#'}); //Not OK, id missing
hello1({email: '#', id: 1, phone: '123'}); //Not OK, phone not allowed
Now, this is not exactly what we need, as we want to allow, but not require phone. To do that, we "merge" the partial and the "picked" version of our type by creating an intersection type, which then will have id and email as required fields, and everything else as optional – exactly how we wanted it.
function hello2(user: Pick<IUser, 'id' | 'email'> & Partial<IUser>) {
}
hello2({email: '#', id: 1}); //OK
hello2({email: '#', id: 1, phone: '123'}); //OK
hello2({email: '#'}); //Not OK, id missing
Typescript now supports partial types.
The correct way to create a partial type is:
type PartialUser = Partial<IUser>;
What you want is this
type Subset<T extends U, U> = U;
this makes sure, that U is a subset of T and returns U as a new type. for example:
interface Foo {
name: string;
age: number;
}
type Bar = Subset<Foo, {
name: string;
}>;
you can not add new properties to Bar which are not part of Foo - and you can not alter types in a non-compatible way. this also works recursively on nested objects.
proper solution with mapped types:
updateUser<K extends keyof IUser>(userData: {[P in K]: IUser[P]}) {
...
}
You can declare some or all fields as optional fields.
interface IUser {
email: string; // not optional
id?: number; // optional
phone?: string; // optional
};
You can seperate it into different interfaces:
interface IUser {
id: number;
};
interface IUserEmail extends IUser {
email: string;
}
interface IUserPhone extends IUser {
phone: string;
}
Have your method receive the base IUser interface and then check for the fields you need:
function doit(user: IUser) {
if (user.email) {
} else if (user.phone) {
}
}
If I understand this question correctly, you want something like Flow's $Shape
So, in one place, you may have something that requires the type
interface IUser {
email: string;
id: number;
phone: string;
};
Then, in another place you want a the type with the same type as IUser just with all the fields now optional.
interface IUserOptional {
email?: string;
id?: number;
phone?: string;
};
You want a way to auto-generate IUserOptional based on IUser without having to write out the types again.
Now, I don't think this is possible in Typescript. Things may change in 2.0, but I don't think we're even close to something like this in Typescript yet.
You could look at a pre-compiler which would generate such code for you before typescript runs, but that doesn't sound like a trivial thing to do.
With this problem in mind, I can only suggest you try Flow instead. In flow you can just do $Shape<IUser> to generate the type you want programmatically. Of course, Flow differs from Typescript in many big and small ways, so keep that in mind. Flow is not a compiler, so you won't get things like Enums and class implementing interfactes
I have a c# method I am loading from a dll with optional string arguments that default to null. For example
public void foo(string path, string new_name = null, bool save_now = true)
{
if(name == null)
new_name = Path.GetFileNameWithoutExtension(path);
...
if(save_now)
Save();
}
I want to call this from within a powershell script and not supply a value for new_name but one for save_now. As per this seemingly very similar question I have tried
$default = [type]::Missing
$obj.foo($path, $default, $false)
but this results in new_name being set as "System.Reflection.Missing" within the function.
Additionally I tried
$obj.foo($path, $null, $false)
but this results in new_name being set to the empty string, still not null. I could set the default to the empty string, but I was wondering if there was any good way to actually have the default value be used.
No can do in PowerShell. It doesn't support C#/VB optional parameters. It is the duty of the language calling the method to provide the default values when the programmer doesn't and PowerShell just doesn't do that.
You can simply omit the optional parameters in the call. I modified your example to run it in PS. For example:
$c = #"
public static class Bar {
public static void foo(string path, string new_name = null, bool save_now = true)
{
System.Console.WriteLine(path);
System.Console.WriteLine(new_name);
System.Console.WriteLine(save_now);
}
}
"#
add-type -TypeDefinition $c
[Bar]::Foo("test",[System.Management.Automation.Language.NullString]::Value,$false)
This generates the following
test
False
Test was passed explicitly, null is null and had no output, and the save_now evaluated to the default of True.