As you may know, functions in JavaScript can have properties as any object. For example (taken from the excellent JavaScript: The Definitive Guide, 6th ed, p. 178) computes a factorial using the function as memoization array:
function factorial(n: number): number {
if (isFinite(n) && n > 0 && n == Math.round(n)) {
if (!(n in factorial))
factorial[n] = n * factorial(n - 1);
return factorial[n];
}
else
return NaN;
}
factorial[1] = 1;
I tried defining the following interface:
interface Factorial {
(n: number) : number;
[ index: number ]: number;
}
But the compiler is telling me that Type '(n: number) => number' is not assignable to type 'Factorial'. Index signature is missing in type '(n: number) => number'.
I can't do the obvious thing and just define private index: number; inside the function, I'm stumped.
What you have is an example of hybrid types. You have to use type assertion to make sure the function complies with the interface:
let factorial = function (n: number): number {
if (isFinite(n) && n > 0 && n == Math.round(n)) {
if (!(n in factorial))
factorial[n] = n * factorial(n - 1);
return factorial[n];
}
else
return NaN;
} as Factorial;
factorial[1] = 1;
Related
playground
I have a bunch of interfaces, at least 2-3 levels nested, where some of the leafs are numbers/strings, etc, but others are (numeric) enums.
I don't want to change this.
Now I want to "serialize" objects that implements my interfaces as JSON. Using JSON.stringify is good for almost all cases, but the enums, that are serialized with their (numerical) value.
I know that it's possible to pass a replacer function to JSON.stringify, but I'm stuck, as I'm not sure how to write a function that detect the structure of my object and replace the enum values with the appropriate names.
example:
enum E { X = 0, Y = 1, Z = 2 }
enum D { ALPHA = 1, BETA = 2, GAMMA = 3 }
interface C { e: E; }
interface B { c?: C; d?: D; }
interface A { b?: B; }
function replacer(this: any, key: string, value: any): any {
return value;
}
function stringify(obj: A): string {
return JSON.stringify(obj, replacer);
}
const expected = '{"b":{"c":{"e":"Y"},"d":"ALPHA"}}';
const recieved = stringify({ b: { c: { e: E.Y }, d: D.ALPHA } });
console.log(expected);
console.log(recieved);
console.log(expected === recieved);
It's not possible to automatically find out which enum was assigned to a field, not even with typescript's emitDecoratorMetadata option. That option can only tell you it's a Number and it will only be emitted on class fields that have other decorators on them.
The best solution you have is to manually add you own metadata. You can do that using reflect-metadata node module.
You'd have to find all enum fields on all of your classes and add metadata saying which enum should be used for serializing that field.
import 'reflect-metadata';
enum E
{
ALPHA = 1,
BETA = 2,
GAMMA = 3,
}
class C
{
// flag what to transform during serialization
#Reflect.metadata('serialization:type', E)
enumField: E;
// the rest will not be affected
number: number;
text: string;
}
This metadata could be added automatically if you can write an additonal step for your compiler, but that is not simple to do.
Then in your replacer you'll be able to check if the field was flagged with this matadata and if it is then you can replace the numeric value with the enum key.
const c = new C();
c.enumField= E.ALPHA;
c.number = 1;
c.text = 'Lorem ipsum';
function replacer(this: any, key: string, value: any): any
{
const enumForSerialization = Reflect.getMetadata('serialization:type', this, key);
return enumForSerialization ? enumForSerialization[value] ?? value : value;
}
function stringify(obj: any)
{
return JSON.stringify(obj, replacer);
}
console.log(stringify(c)); // {"enumField":"ALPHA","number":1,"text":"Lorem ipsum"}
This only works with classes, so you will have to replace your interfaces with classes and replace your plain objects with class instances, otherwise it will not be possible for you to know which interface/class the object represents.
If that is not possible for you then I have a much less reliable solution.
You still need to list all of the enum types for all of the fields of all of your interfaces.
This part could be automated by parsing your typescript source code and extracting the enum types for those enum fields and then saving it in a json file that you can load in runtime.
Then in the replacer you can guess the interface of an object by checking what are all of the fields on the this object and if they match an interface then you can apply enum types that you have listed for that interface.
Did you want something like this? It was the best I could think without using any reflection.
enum E { X = 0, Y = 1, Z = 2 }
enum D { ALPHA = 1, BETA = 2, GAMMA = 3 }
interface C { e: E; }
interface B { c?: C; d?: D; }
interface A { b?: B; }
function replacer(this: any, key: string, value: any): any {
switch(key) {
case 'e':
return E[value];
case 'd':
return D[value];
default:
return value;
}
}
function stringify(obj: A): string {
return JSON.stringify(obj, replacer);
}
const expected = '{"b":{"c":{"e":"Y"},"d":"ALPHA"}}';
const recieved = stringify({ b: { c: { e: E.Y }, d: D.ALPHA } });
console.log(expected);
console.log(recieved);
console.log(expected === recieved);
This solution assumes you know the structure of the object, just as you gave in the example.
I'm trying to implement classes DistanceCM and DistanceMM, and I want these to be summable interchangeably, as long as they both inherit from Distance.
However, I get this error:
"Error:(46, 76) ceylon: type parameter 'Other' of declaration
'Summable' has argument 'Distance' which is not assignable to upper
bound 'Summable' of 'Other'"
...which I can't decipher... The error message refers to this line in the code below:
shared actual Distance plus(Distance other)
This is the current code:
abstract class Distance() of DistanceMM | DistanceCM {
shared formal Distance multiplyScalar(Float scalar);
}
class DistanceMM(variable Float val) extends Distance() satisfies Summable<Distance>
{
shared Float distanceInMillimeters;
shared Float distanceInCentimeters;
switch (unit)
case (millimeter) {
distanceInMillimeters => val;
distanceInCentimeters => val / 10;
}
case (centimeter) {
distanceInMillimeters => val * 10;
distanceInCentimeters => val;
}
shared actual DistanceMM multiplyScalar(Float scalar) {
val = val * scalar;
return this;
}
shared actual Distance plus(Distance other) {
switch (other)
case (DistanceMM) {
return DistanceMM(val + other.distanceInMillimeters(), unit);
}
case (DistanceCM) {
return DistanceMM(val + other.distanceInCentimeters(), unit);
}
}
}
class DistanceCM(variable Float val) extends Distance() satisfies Summable<Distance>
{
shared Float distanceInMillimeters;
shared Float distanceInCentimeters;
switch (unit)
case (millimeter) {
distanceInMillimeters => val;
distanceInCentimeters => val / 10;
}
case (centimeter) {
distanceInMillimeters => val * 10;
distanceInCentimeters => val;
}
shared actual DistanceCM multiplyScalar(Float scalar) {
val = val * scalar;
return this;
}
// implementation missing
}
interface Summable<Other> of Other​ given Other satisfies Summable<Other>
Notice the constraint (the given clause). You're claiming that DistanceMM satisfies Summable<Distance>, but Distance doesn't satisfy the constraint on Other (Distance doesn't satisfy Summable<Distance>). Try this:
interface Distance of Centimeter | Millimeter satisfies Summable<Distance> {}
class Centimeter() satisfies Distance {
shared actual Distance plus(Distance other) => nothing;
}
class Millimeter() satisfies Distance {
shared actual Distance plus(Distance other) => nothing;
}
I'm trying to create an interceptor function. In my specific case, a throttle function.
Consider the following example:
function throttle(func: Function, wait: number = 0): Function {
let previous: {
args: any[];
timestamp: number;
result: any;
} = {
args: [],
timestamp: 0,
result: null
};
return (...currentArgs: any[]): any => {
const now = Date.now();
const remaining = wait && (wait - (now - previous.timestamp));
const argumentsChanged = JSON.stringify(currentArgs) !== JSON.stringify(previous.args);
if (argumentsChanged || (wait && (remaining <= 0 ||remaining > wait))) {
previous = {
args: currentArgs,
timestamp: now,
result: func.apply(this, currentArgs)
};
}
return previous.result;
};
}
This function will initially call the function passed by argument and will not call it again until the specified wait time has been reached or the target function's arguments changed.
The problem with this is that it should return the same function type as the function passed in by argument, so it can be transparent to the caller.
For instance, this should be allowed but it's not:
function updateStatus(id: number, newStatus: string): void {
// ...
}
// ...
// Type 'Function' is not assignable to type '(id: number, newStatus: string) => void'
this.updateStatus = throttle(this.updateStatus.bind(this), 500);
How can I achieve this?
Instead of using the Function type use a generic constraint.
The function signature should look like this:
function throttle<T extends Function>(func: T, wait: number = 0): T
In a simple example:
function throttle<T extends Function>(func: T, wait: number = 0): T {
return null;
}
function fn(a: string, b: number): boolean {
return false;
}
let throttled = throttle(fn, 3); // type of throttled: (a: string, b: number) => boolean
throttled("stirng", 0); // ok
throttled(3, 4); // error: Argument of type '3' is not assignable to parameter of type 'string'
(code in playground)
Why isn't std.algorithm.reduce in Phobos pure? Is it an unfixed Issue or is there a reason why it can't be?
Has this something todo with the question:
"What does a pure function look like"
Andrei asked in the final lecture at DConf 2013?
See: http://forum.dlang.orgthread/20120306224101.GA30389#quickfur.ath.cx
I want the function sparseness in the following code to be pure. I guess I could always replace reduce with a foreach loop for now right?
import std.algorithm: reduce, min, max;
import std.typetuple: templateAnd;
import std.traits: isArray, Unqual;
import std.range: ElementType, isInputRange, isBidirectionalRange, isFloatingPoint;
//** Returns: true if $(D a) is set to the default value of its type. */
bool defaulted(T)(T x) #safe pure nothrow { return x == T.init; }
alias defaulted untouched;
/** Returns: Number of Default-Initialized (Zero) Elements in $(D range). */
size_t sparseness(T)(in T x, int recurseDepth = -1) #trusted /* pure nothrow */ {
import std.traits: isStaticArray;
static if (isStaticArray!T ||
isInputRange!T) {
import std.range: empty;
immutable isEmpty = x.empty;
if (isEmpty || recurseDepth == 0) {
return isEmpty;
} else {
const nextDepth = (recurseDepth == -1 ?
recurseDepth :
recurseDepth - 1);
static if (isStaticArray!T) { // TODO: We can't algorithms be applied to static arrays?
typeof(return) ret;
foreach (ref elt; x) { ret += elt.sparseness(nextDepth); }
return ret;
} else {
import std.algorithm: map, reduce;
return reduce!"a+b"(x.map!(a => a.sparseness(nextDepth)));
}
}
} else static if (isFloatingPoint!T) {
return x == 0; // explicit zero because T.init is nan here
} else {
return x.defaulted;
}
}
unittest {
assert(1.sparseness == 0);
assert(0.sparseness == 1);
assert(0.0.sparseness == 1);
assert(0.1.sparseness == 0);
assert(0.0f.sparseness == 1);
assert(0.1f.sparseness == 0);
assert("".sparseness == 1);
assert(null.sparseness == 1);
immutable ubyte[3] x3 = [1, 2, 3]; assert(x3[].sparseness == 0);
immutable float[3] f3 = [1, 2, 3]; assert(f3[].sparseness == 0);
immutable ubyte[2][2] x22 = [0, 1, 0, 1]; assert(x22[].sparseness == 2);
immutable ubyte[2][2] x22z = [0, 0, 0, 0]; assert(x22z[].sparseness == 4);
}
Update:
I decided on instead using isIterable and foreach instead of the above, as this works just aswell for me right now and makes things #safe pure nothrow. I see no need right now to use higher order functions to solve this problem. I also found Davids Simchas' upcoming std.rational very natural to use here:
import rational: Rational;
/** Returns: Number of Default-Initialized (Zero) Elements in $(D x) at
recursion depth $(D depth).
*/
Rational!ulong sparseness(T)(in T x, int depth = -1) #safe pure nothrow {
alias R = typeof(return); // rational shorthand
static if (isIterable!T) {
import std.range: empty;
immutable isEmpty = x.empty;
if (isEmpty || depth == 0) {
return R(isEmpty, 1);
} else {
immutable nextDepth = (depth == -1 ? depth : depth - 1);
ulong nums, denoms;
foreach (ref elt; x) {
auto sub = elt.sparseness(nextDepth);
nums += sub.numerator;
denoms += sub.denominator;
}
return R(nums, denoms);
}
} else static if (isFloatingPoint!T) {
return R(x == 0, 1); // explicit zero because T.init is nan here
} else {
return R(x.defaulted, 1);
}
}
If you change nextDepth to immutable rather than const then sparseness will be pure.
I believe this is a bug, it may be to do with the closure being passed to reduce capturing nextDepth, and for some reason thinking it may be mutable because it is const. Values declared as const are however identical to those declared as immutable -- the difference only manifests itself with indirections -- so I believe it is an error.
You may want to file a minimal repro case as a bug.
(it cannot be nothrow however, because reduce can, in fact, throw)
I want to test if an object is a vector, any vector, not only a vector of a single type.
I ran a test:
var v:Vector.<int> = new Vector.<int>();
v.push(3);
v.push(1);
v.push(2);
trace(v is Array); // false
trace(v is Vector); // false
trace(v is Vector.<int>); // true
trace(v is Vector.<*>); // false
It seems that the only thing that returns true is the one which specifies the vector type, but I want to test it for ANY type.
I will need a very efficient method to compare, because getQualifiedClassName is too slow.
My current approach is:
private static function isVector(obj:Object):Boolean {
return (getQualifiedClassName(obj).indexOf('__AS3__.vec::Vector') == 0);
}
But it is 2x slower than the is operator.
I need speed because it's for a object serialization class, and it needs to be very fast.
The problem is that Vector.<*> is a different class than that used for <Number>, <int>, or <uint>. The numeric primitives have special classes for better efficiency. String and Boolean are also primitives, but unlike the numeric primitives they are detected by <*>. As a result, you need only test for the generic Vector and the 3 numeric types.
This solution is over 2 times as fast as getQualifiedClassName in the worst case where the object is either not a Vector, or is a Vector.<uint>, and 5 times faster if the object is a non-primitive base type Vector, like Vector.<Object>:
return (obj is Vector.<*>
|| obj is Vector.<Number>
|| obj is Vector.<int>
|| obj is Vector.<uint>);
Here's a simplistic test:
var moo:Vector.<uint> = new Vector.<uint>();
var timer:Timer = new Timer();
var b:Boolean;
timer.startTimer();
for (var i:int = 0; i < 1000000; i++)
{
b = (moo is Vector.<*>
|| moo is Vector.<Number>
|| moo is Vector.<int>
|| moo is Vector.<uint>);
}
logger.info(" is timer: " + timer.endTimer());
timer.startTimer();
for (i = 0; i < 1000000; i++)
{
b = (flash.utils.getQualifiedClassName(moo).indexOf('__AS3__.vec::Vector') == 0);
}
logger.info("gqcn timer: " + timer.endTimer());
[LOG] com.tcg.test: is timer: 320
[LOG] com.tcg.test: gqcn timer: 756
Change moo to Vector.<Object>:
[LOG] com.tcg.test: is timer: 158
[LOG] com.tcg.test: gqcn timer: 743
Other methods are way too inefficient, so I'm still using my approach:
private static function isVector(obj:Object):Boolean {
return (getQualifiedClassName(obj).indexOf('__AS3__.vec::Vector') == 0);
}
trace(new Array().fixed);//undefined
trace(new Object().fixed);//undefined
trace(new Vector.<Sprite>().fixed);//false
trace(new Vector.<*>().fixed);// false
If you need serialization for any kind of object, you have to iterate over all possible types anyway, so you could use a sequential approach to find your vector type:
v is ... (simple data types)
v is ... (object types that are not collections)
v is Array
v is XMLList
v is ... (all other collection types you can think of)
if none of the above is true, it must be a vector
serialize objects in the vector. If you have more than one type, it's Vector.<*>, otherwise set the vector type according to the object type of the content items.
Use
(obj as Vector.<*>) is Vector.<*>
/// Return Class of any Target
static public function getClass( Target:* ):Class
{
return getDefinitionByName ( getQualifiedClassName( Target ) ) as Class ;
}
/// Check if object is type of Vector.< * >
static public function isVector( any:* ):Boolean
{
return String( getClass( any ) ).indexOf( "[class Vector.<" ) > -1;
}
/// Get Vector < Class >
static public function getVectorType( vector:* ):Class
{
var c:String = String( getClass( vector ) );
var s:int = c.indexOf( '<' ) + 1;
var e:int = c.indexOf( '>' );
return getDefinitionByName( c.substring( s, e ) ) as Class;
}
private function getIsVector(obj:Object):Boolean
{
return String(obj.constructor).indexOf('[class Vector.<*>]') == 0;
}