If I have a code like this:
class SomeClass
constructor: ->
#someAttr = false
someFunction: ->
process.nextTick ->
#someAttr = true
obj = new SomeClass
obj.someFunction()
obj.someAttr # Would still be false, because the # (this) is in the process context
it won't work, because process.nextTick brings us into a different context, in which #someAttr isn't defined. How can I work around this (also when I want to call methods of SomeClass)?
The usual way around this is to store a reference to this in a local variable which will be available within the anonymous function. In JavaScript:
function someFunction() {
var self = this;
process.nextTick(function() {
self.someAttr = true;
});
}
CoffeeScript has a special syntax to help with this; the "fat arrow":
class SomeClass:
someFunction: ->
process.nextTick =>
#someAttr = true
Use => instead of -> to preserve this variable.
class SomeClass
constructor: =>
#someAttr = false
someFunction: ->
process.nextTick =>
#someAttr = true
Related
I just came across a data class as follows.
class A{
}
sealed class B
data class C(val isNext: Boolean = false, val builder: () -> A): A
class D: A{
}
To create an instance of the class, the developer used the following declaration.
C{D()}
My understanding of lambda, () -> A is that its a function without name that takes no arguments and creates an instance of A, however given that A's constructor is called, can I presume that its a function?
A constructor is a type of function, but you aren't passing it as a function here, because you have wrapped it by calling it in a lambda function. So you have passed a function that internally calls the constructor.
You can pass the constructor directly as a function, like this:
val c = C(::D)
I tidied up your setup:
open class A
class C(val isNext: Boolean = false, val builder: () -> A) : A()
class D : A()
With that in place the following are all equivalent:
val bldr: () -> A = { D() }
C { D() }
// is the same as:
C(isNext = false, builder = { D() })
// is the same as:
C(isNext = false, builder = bldr)
So the lambda is just an instance variable named builder of C just like isNext is. If it gets invoked in will create a new D that happens to be a subclass of A
some background:
val (name, age) = person
This syntax is called a destructuring declaration. It creates multiple variables (correction, creates multiple values) at at the same time.
Destructuring declarations also work in for-loops: when you say:
for ((a, b) in collection) { ... }
Lets take a look at a list item i have:
#Parcelize
data class MyModel(
var name: String = "",
var is_locked: Boolean = true,
var is_one_size: Boolean = false,
) : Parcelable
and now i have obtained a list of "MyModel" classes and i am trying to loop over them like this:
private fun initMyModelList(model: MutableList<MyModel>) {
//i want to access is_locked from here with destruction but i cant ? IDE telling me the type is an int but its clearly defined as a Boolean
for((is_locked) in model){
//what i want to do in here is access the is_locked var of the model list and change all of them in a loop. im trying to use Destructuring in loop as a conveience. why is it not working ?
//how can i make the call signature look like this--- > is_locked = true instad of model.is_locked =true
}
}
all i want to do is be able to call is_locked = true instead of model.is_locked = true within the loop. how can this be done ?
This syntax is called a destructuring declaration. It creates multiple variables at at the same time.
It doesn't create multiple variables, it captures multiple values. You're working with values, not references, as your source tells further:
A destructuring declaration is compiled down to the following code:
val name = person.component1()
val age = person.component2()
Closest to what you want would be this custom extension function:
inline fun <E> Iterable<E>.withEach(block: E.() -> Unit) {
forEach {
it.block()
}
}
Use like so:
model.withEach {
is_locked = true
}
Before you ask the obligatory question "why isn't this included in stdlib?" consider that functional style programming typically is about transforming immutable types. Basically, what I did here was encourage a bad habit.
Basically, it isn't possible, cause your code is compiled to something like:
for (m in models) {
val is_locked = m.component1()
...
}
Which means that you create a local property which cannot be reassigned. But you can do something like this:
for (m in model) {
with(m) {
is_locked = true
}
}
Yep, it isn't perfect, but it can be improved with extension methods:
fun <T> List<T>.forEachApply(block: T.() -> Unit) {
forEach(block)
}
private fun initMyModelList(model: MutableList<MyModel>) {
model.forEachApply {
is_locked = true
}
}
You can use destructuring in a loop just fine as read-only values.
data class Stuff(val name: String, val other: String)
fun doStuff() {
val stuff = Stuff("happy", "day")
val stuffs = listOf(stuff)
for ((name) in stuffs) {
println(name)
}
}
Running that method prints "happy" to the console. Baeldung shows an example of using it here.
It's best practice for data classes to be immutable, so I would try to rewrite your data class to be immutable. The .copy function will let you copy your data class but with new, different values.
I am using a custom library, and for some types I have to write:
import * as pipeline from 'custom-pipeline';
import {MapTransform} from 'custom-pipeline';
export const createTransform = (appConfig: IAppConfig):
MapTransform<ISomeData, IFoo> |
MapTransform<ISomeData, IBar> => {
switch(appConfig.createType) {
case 'foo':
return new pipeline.MapTransform<ISomeData, IFoo>((data: ISomeData) => {...});
case 'bar':
return new pipeline.MapTransform<ISomeData, IBar>((data: ISomeData) => {...});
}
}
Especially the verbose constructor is irking me. I can I alias the types alright:
type FooTransform = MapTransform<ISomeData, IFoo>;
type BarTransform = MapTransform<ISomeData, IBar>;
Yet I cannot do:
new FooTransform((data: ISomeData) => {...});
new BarTransform((data: ISomeData) => {...});
throwing an error like:
error TS2304: Cannot find name 'FooTransform'.
I assume it's because I only have a type and not a class? Yet how can I alias the constructor in a way that I can do new FooTransform as above?
The definition of MapTransform looks like:
export declare class MapTransform<A, B> extends BaseTransform<A, B> {
constructor(private mapFunc: (val: A) => B);
}
I can reduce the constructor to this:
fooMapFunction = (data: ISomeData): IFoo => {...};
new MapTransform<ISomeData, IFoo>(mapFunction);
Albeit it's not en par with a new FooTransform(fooMapFunction).
Your assumption is correct, type declarations are compile-time only, thus you can't perform any operations on them (including instantiation with new).
Solution 1: Type Assertion
Let's assume the superclass looks like this:
class MapTransform<T1> {
constructor(public readonly data: T1) { /* ... */ }
}
To create a specialised type alias, you can do this:
type TransformA = MapTransform<number>;
From the perspective of the superclass MapTransform, there's no difference between MapTransform<A>, MapTransform<B> and so on (that's the point of generics), so we can safely assign the constructor function of class MapTransform to a constant TransformA. Calling new TransformA() is, at runtime, identical to calling new MapTransform<number>():
const TransformA = <{ new (data: number): TransformA; }>MapTransform;
Notice the type assertion? This tells the TypeScript compiler to treat the assigned value MapTransform as an object which constructs an object of type TransformA when instantiating it with new. We can now write:
const a = new TransformA(123);
a.data.toExponential(); // works
BTW, what the TypeScript compiler actually sees is this ...
const TransformA = <{ new (data: number): MapTransform<number>; }>MapTransform;
... because type TransformA ≡ MapTransform<number>.
Be aware that all of these will evaluate true:
new TransformA(123) instanceof TransformA
new TransformA(123) instanceof MapTransform
new MapTransform<number>(123) instanceof TransformA
new MapTransform<number>(123) instanceof MapTransform
Here's the example in the TypeScript Playground.
Solution 2: Subclassing
Again, let's assume the superclass looks like this:
class MapTransform<T1> {
constructor(public readonly data: T1) { /* ... */ }
}
With subclassing, the solution is simple: Extend the superclass and pass the desired type parameters along in the extends clause.
class TransformA extends MapTransform<number> { }
Et voilà, you now have a constructor that works at runtime as well as a type that works at compile-time.
Unlike the first solution, the following 2 expressions will evaluate true ...
new TransformA(123) instanceof TransformA
new TransformA(123) instanceof MapTransform
... while these will evaluate false:
new MapTransform<number>(123) instanceof TransformA
new MapTransform<number>(123) instanceof MapTransform
Solution 2.1: Anonymous Subclassing
If you only need the constructor alias but not the type alias, this might come in handy:
const TransformA = class extends MapTransform<number> { };
This is called a class expression and can be used like every other expression, for example:
class App {
private static TransformA = class extends MapTransform<number> { };
public doStuff() {
return new App.TransformA(123);
}
}
More on Types
If you're interested, here's a few more links on the topic:
Named Types in TypeScript Spec
Type Assertions
TypeScript Handbook: Interfaces, specifically section "Difference between the static and instance sides of classes"
Class Expressions
EDIT
You wrote you've had issues applying these solutions to a class that expects a function as a parameter, so here's another example in the TypeScript playground.
I have a question about typescript properties: Is it possible to get the setter and getter of a typescript property or to declare a function argument to be of a property of X type?
The reason is to get some sort of "reference" to a variable which is not possible in plain JS without writing getter/setter wrappers or access the variable via parent object itself (obj["varname"]).
For example (with some working code and other parts speculative):
//A sample class with a property
class DataClass<T> {
private T val;
public get value(): T {
return this.val;
}
public set value(value: T) {
this.val = value;
}
}
//Different ways of modifing a member "by reference"
class ModifyRef {
public static void DoSomethingByGetterAndSetter(getter: () => string, setter: (val: string) => void) {
var oldValue = getter();
setter("new value by DoSomethingByGetterAndSetter");
}
public static void DoSomethingByObject(obj: Object, name: string) {
var oldValue = obj[name];
obj[name] = "new value by DoSomethingByObject";
}
//Is something like this possible?
public static void DoSomethingByProperty(somePropery: property<string>) {
var oldVlaue = someProperty;
someProperty = "new value by DoSomethingByProperty";
}
}
var inst = new DataClass<string>();
//Calling the DoSomethingByProperty if possible
ModifyRef.DoSomethingByProperty(inst.value);
//Or if not is something like this possible
ModifyRef.DoSomethingByGetterAndSetter(inst.value.get, inst.value.set);
The simplest way to do this would be to provide methods, rather than a property:
//A sample class with a property
class DataClass<T> {
private val: T;
public getValue(): T {
return this.val;
}
public setValue(value: T) {
this.val = value;
}
}
class ModifyRef {
public static DoSomethingByGetterAndSetter(getter: () => string, setter: (val: string) => void) {
var oldValue = getter();
setter("new value by DoSomethingByGetterAndSetter");
}
}
var inst = new DataClass<string>();
//Or if not is something like this possible
ModifyRef.DoSomethingByGetterAndSetter(inst.getValue, inst.setValue);
I've long found it very surprising that languages with properties don't include a convenient way to make a reference to a property, and have daydreamed about having this feature in C#. It ought to work on local variables as well.
A popular pattern for this kind of first-class or reified property is a single function that can be called in two ways:
no arguments: returns current value.
one argument: sets value, returns undefined.
Or in TypeScript terms:
interface Property<T> {
(): T;
(newVal: T): void;
}
The methods of jQuery objects often work like this. An example of this pattern in modelling pure data is in Knockout, in which such properties also support change subscriptions, and there's a rather elegant pattern for defining computed properties that automatically recompute when their dependencies change.
We have a typical getter in one of our classes, lets say
class Employee implements IEmployee {
private _fullName: string;
get fullName(): string {
return this._fullName;
}
}
and an interface to work with it
interface IEmployee{
fullName: string;
}
When working with an instance via this interface the compiler will not warn us about absence of a setter if we try to assign to fullName, and the the JS runtime simply swallows any assignment and does not throw an error. Is there any way to mark interface member as having only getter or only setter?
I've seen this post, but it is quite old, i want to know, if anything improved.
Properties in typescript can now have 'readonly' modifier, which achieves the desired restult.
interface IEmployee{
readonly fullName: string;
}
This is an interesting question. The concept of a readonly property is subtly different in TypeScript to other languages.
In many languages a property with a getter (but no setter) would raise a compiler error if you attempted to set the property, but TypeScript doesn't.
The property is still readonly, because it makes no difference if you attempt to set it; the set will fail silently.
Here is an example without any interfaces:
class Example {
get name() {
return 'Steve';
}
}
var y = new Example();
y.name = 'Example 2';
alert(y.name);
There is no compiler warning when I use x.name = 'Example 2';.
If there was a compiler warning, I would subsequently expect there to be a way of specifying the readonly-ness of a property within an interface. As you'd expect though, given the above information, you can't set a readonly property on an interface.
interface Test {
name: string;
}
class Example {
get name() {
return 'Steve';
}
}
var x: Test = new Example();
x.name = 'Example 1';
alert(x.name);
var y = new Example();
x.name = 'Example 2';
alert(x.name);
This means you can only enforce readonly-ness by having a method to get the value of the property (and obviously no method that allows it to be set).
interface Test {
getName: () => string;
}
class Example {
getName() {
return 'Steve';
}
}
var x: Test = new Example();
//x.getName('Obviously not');
//x.getName() = 'Obviously not';
alert(x.getName());
var y = new Example();
//y.getName('Obviously not');
//y.getName() = 'Obviously not';
alert(y.getName());