TypeScript function type syntax explanation - function

I'm reading the TypeScript Handbook and in the Functions section under the Function Types heading there is this example (which I understand):
let myAdd = function(x: number, y: number): number { return x+y; };
This is followed by
let’s write the full type of the function out by looking at the each
piece of the function type.
and this syntax:
let myAdd: (x: number, y: number) => number =
function(x: number, y: number): number { return x+y; };
Could someone break this down and explain it as I have never seen this before and can't find an explanation in the handbook?

This line:
let myAdd: (x: number, y: number) => number =
function(x: number, y: number): number { return x+y; };
Consists of 3 parts:
(1) The variable declaration, this part is the let myAdd. I assume that there's nothing to add here, it's just like with js.
(2) The type of the variable: (x: number, y: number) => number.
Here we're defining a type of a function that expects two parameters, both of type number, named x and y.
The function needs to return a number.
(3) The assignment of the value to the variable: = function(x: number, y: number): number { return x+y; }.
This is just like javascript as well, except for the added types for the params and return value.
If you look at it you'll see that the actual implementation matches the declared type perfectly.
You can also write it like this:
let myAdd: (x: number, y: number) => number = function(x, y) { return x+y; };
Or:
let myAdd: (x: number, y: number) => number = (x, y) => { return x+y; };

The first line:
let myAdd: (x: number, y: number) => number
Is declaring the type of the variable "myAdd". Typescript can automatically infer this in the first example, alternatively (which the second example shows) you can implicitly tell Typescript what it should expect.
The second line:
function(x: number, y: number): number { return x+y; };
Refers to the type of the function itself which you have assigned to the variable "myAdd".
For a simpler example illustrating the same thing:
let myString: (input: string) => string = (input: string) => input;
Alternatively a different example of implicitly declaring the variables type:
let myNumber: number = 10;
Both of the above tell Typescript what the variable should be.

Related

detect an enum at runtime and stringify as keys

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.

Key of JSON Object as from function parameter

I know this question was already here, but I do not know how to use it in my case.
I have a function which returns a stringified JSON but I need to change one key with a parameter of this function. I tried something like this to replace it with the value:
function toJSON(... name: string, timestamp: number, x :number, y: number ...): string {
return JSON.stringify({
...
`${name}`: [
{
timestamp: timestamp,
x: x,
y: y
}
]
...
})
Is there an easy way to just replace this Key with a parameter?
the ... means more stuff before and after
Almost good, you should use computed property https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#new_notations_in_ecmascript_2015 instead of string template:
function toJSON(... name: string, timestamp: number, x:number, y: number ...): string {
return JSON.stringify({
...,
[name]: [{ timestamp, x, y }]
});
};
Since es2015, you can use shorthand property names, so you can write { timestamp, x, y } instead of redundand: { timestamp: timestamp, x: x, y: y }

Defining the interface for a function using function properties

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;

Cast a variable as a function/method pointer in Dart? (Delegate?)

Say I have a Map type that stores functions/methods inside of it, like so:
Map triggerHandler = {
'x' : (t) => hello(t)
};
Now, what I want to do next is to declare that the Type of these Map values are to be functions.
I can always do this:
Map<String, dynamic> triggerHandler = {
'x' : (t) => hello(t)
};
But this doesn't help stop programmers from putting in non-functions into the Map values. 'x' could then be a String, or an Integer.
The reason I want to do this is because I have a function that needs to only accept Maps with functions as the values. Calling the array key of 'x' is done by triggerHandlerx when it is a function, rather than triggerHandler[x]. I haven't tested if this causes an error to occur or not, but this doesn't seem semantically correct to me.
It would seem that the most logical way to do this would be to set the Type for the value as a function. Such as giving a Map a type casted value of 'delegate':
Map<String, delegate> triggerHandler = { // Note, this won't work
'x' : (t) => hello(t)
};
What is the correct way to do this in Dart? (If there is a way)
Thank you.
You can use Function as type or if you also want to specify the arguments and return type of these functions you can create typedefs.
Map<String, Function> triggerHandler = {
'x' : (t) => hello(t)
};
typedef int SomeName(SomeType arg1);
Map<String, SomeName> triggerHandler = {
'x' : (t) => hello(t)
};

How to dynamically dispatch based on enrichment?

The spray-json library extends basic Scala types with a toJson method. I'd like to convert an Any into a JsValue if there is such a pimp for the underlying type. My best attempt works, but is verbose:
import cc.spray._
val maybeJson1: PartialFunction[Any, JsValue] = {
case x: BigDecimal => x.toJson
case x: BigInt => x.toJson
case x: Boolean => x.toJson
case x: Byte => x.toJson
case x: Char => x.toJson
case x: Double => x.toJson
case x: Float => x.toJson
case x: Int => x.toJson
case x: Long => x.toJson
case x: Short => x.toJson
case x: String => x.toJson
case x: Symbol => x.toJson
case x: Unit => x.toJson
}
Ideally, I'd prefer something (impossible) like this:
def maybeJson2(any: Any): Option[JsValue] = {
if (pimpExistsFor(any))
Some(any.toJson)
else
None
}
Is there a way to do this without enumerating every type that has been enriched?
There is a way, but it requires a lot of reflection and therefore is quite a headache. The basic idea is as follows. The DefaultJsonProtocol object inherits a bunch of traits that contain implicit objects which contain write methods. Each of those will have an accessor function, but you won't know what it's called. Basically, you'll just take all methods that take no parameters and return one object that has a write method that takes the class of your object and returns a JsValue. If you find exactly one such method that returns one such class, use reflection to call it. Otherwise, bail.
It would look something like this (warning, untested):
def canWriteMe(writer: java.lang.Class[_], me: java.lang.Class[_]):
Option[java.lang.reflect.Method] =
{
writer.getMethods.find(_.getName == "write").filter{ m =>
classOf[JsValue].isAssignableFrom(m.getReturnType) && {
val parm = m.getParameterTypes()
m.length == 1 && parm(0).isAssignableFrom(me)
}
}
}
def maybeJson2(any: Any): Option[JsValue] = {
val couldWork = {
DefaultJsonProtocol.getClass.getMethods.
filter(_.getParameterTypes.length==0).
flatMap(m => canWriteMe(m.getReturnType, any.getClass).map(_ -> m))
}
if (couldWork.length != 1) None else {
couldWork.headOption.map{ case (wrMeth, obMeth) =>
val wrObj = obMeth.invoke(DefaultJsonProtocol)
val answer = wrMeth.invoke(wrObj, any)
}
}
}
Anyway, you're best off pulling the DefaultJsonProtocol class apart in the REPL step by step and finding out how to reliably identify the objects that define the writers, and then get the write methods out of them.
I'm not sure it will fit you needs, but here is an alternative approach wich is really simple and type-safe.
If you kept the type of the argument (instead of using Any) you could rely on implicit parameter resolution to find the correct conversion at compile time:
def toJson[T:JsonFormat]( t: T ): JsValue = implicitly[JsonFormat[T]].write(t)
You won't need an option, because the program will fail at compile time if you try to pass an argument which is not "pimpable".