Flow. How to specify the union type depending on the function argument - flowtype

Let's say we have two types and union of them:
type A = {tag: 'a'};
type B = {tag: 'b'};
type U = A | B;
And function, which returns A or B, depending on provided tag:
const getSpecificObj = (tag: string) => U;
How can I specify that returning type? So then I will be able to use it like:
const a: A = getSpecificObj('a');
P.S. Yes, I know that I can write the same function for every type or override. But maybe there are another ways. For example in TS I can write:
type Extract<A, T> = A extends { tag: T } ? A : never;
<T>(tag: T): Extract<U, T>

Rather than returning a union of the types A and B, you need to parameterize the function getSpecificObj by the type of its argument, and then return an object which references that type parameter. Here's how I would do it:
type A = {tag: 'a'};
type B = {tag: 'b'};
function getSpecificObj<T : string>(tag: T) : { tag: T } {
return { tag };
}
const a : A = getSpecificObj('a');
const b : B = getSpecificObj('b');
The annotation <T : string> on getSpecificObj makes sure the function isn't more general than you need, and will only accept strings as tags. You can think of this as moving the string annotation off of the tag parameter and into the generic type of the tag parameter.
You can view the example on the try-flow page here.

Related

class instance returned in a static method doesn't match type

I'm returning an instance of class in a static method but flow is complaining about incompatible generic type. Is this a problem with flow or am I doing something wrong?
Code:
class Foo<T> {
var1: T
constructor(var1: T) {
this.var1 = var1
}
static staticMethod(var1: T) {
return new Foo(var1)
}
}
let x:Foo<number> = new Foo(1) // works
let y:Foo<number> = Foo.staticMethod(1) // doesn't work
Error:
let y:Foo<number> = Foo.staticMethod(1)
^ Cannot assign `Foo.staticMethod(...)` to `y` because `T` [1] is incompatible with number [2] in type argument `T` [3].
It seems like flow can't infer the type here, but it works fine if you annotate the return type.
Change
static staticMethod(var1: T) {
to
static staticMethod(var1: T): Foo<T> {

kotlin - Pass method reference to function

Let's say I have the following Java class:
public class A {
public Result method1(Object o) {...}
public Result method2(Object o) {...}
...
public Result methodN(Object o) {...}
}
Then, in my Kotlin code:
fun myFunction(...) {
val a: A = ...
val parameter = ...
val result = a.method1(parameter) // what if i want methodX?
do more things with result
}
and I want to be able to choose which methodX will be called inside myFunction. in Java, I would pass A::method7 as an argument and call it. in Kotlin it doesn't compile. How should I solve it in Kotlin?
You can also pass the method reference in Kotlin (without needing the heavy hammer that is reflection):
fun myFunction(method: A.(Any) -> Result) {
val a: A = ...
val parameter = ...
val result = a.method(parameter)
do more things with result
}
myFunction(A::method1)
myFunction {/* do something in the context of A */}
This declares method as part of A, meaning you can call it with normal object.method() notation. It Just Works™ with the method reference syntax.
There's also another form that works with the same call syntax, but makes A more explicit:
fun myFunction(method: (A, Any) -> Result) { ... }
myFunction(A::method1)
myFunction {a, param -> /* do something with the object and parameter */}
You can actually do this exactly like you wanted to:
fun myFunction(kFunction: KFunction2<A, #ParameterName(name = "any") Any, Result>) {
val parameter = "string"
val result: Result = kFunction(A(), parameter)
//...
}
myFunction(A::method1)
myFunction(A::method2)

type check dynamic properties flowtype

I am trying to add dynamic properties to an object and have flow type check them:
my function would be like this:
function defineStuff(obj:MyType, keys:string[]):??? {
keys.forEach(function(key) {
Object.defineProperty(obj, key, {get:function(){....}});
obj["Add"+key] = function(value) {....};
obj["Remove"+key] = function(value) {....};
}
return obj;
}
I would like to be able to do stuff like this;
var obj : MyType = fetchMyObj();
defineStuff(obj, ["Thing", "OtherThing"]);
var thing = obj.Thing;
obj.AddOtherThing(10);
all dynamic properties type would be number
is there a syntax for doing this in flow? (i.e. how to fill the ???)
This should work for dictionaries.
type MyType = {[key: string]: number};
// ...
function defineStuff(obj: MyType, keys:string[]): MyType {
keys.forEach(function(key) {
Object.defineProperty(obj, key, {get:function(){....}});
// you can't have functions here, because you say that all values should be numbers
// obj["Add"+key] = function(value) {....};
// obj["Remove"+key] = function(value) {....};
}
return obj;
}
// ...
See docs

How should a disjoint union wrapper be structured for refinement to work?

On the Flowtype "Try" site
// #flow
type One = {type: "One"};
type Two = {type: "Two"};
type Node = One | Two;
class Foo<N: Node> {
node: N;
constructor(n: N) {
this.node = n;
}
}
const fooNode: Foo<Node> = new Foo({type: "One"});
if (fooNode.node.type === "One") {
const fooOne: Foo<One> = fooNode;
}
the if type check is not enough to refine the type, if I understand right, because the type is not guaranteed to be constant.
Since I want to avoid the possibility of this being an X/Y problem, the usecase I'm playing with at the moment is searching from a given node with a .find method that would return the refined type, e.g. using
parent(): Foo<N> | null {
// ...
return null;
}
find<U: Node>(callback: (foo: Foo<N>) => Foo<U> | null): Foo<U> | null {
let p = this;
do {
const result = callback(p);
if (result) return result;
p = p.parent();
} while (p);
return null;
}
with
const f: Foo<Node> = new Foo({type: "One"});
const result: Foo<Two>|null = f.find((p) => p.node.type === "Two" ? p : null);
which would allow me to return the refined type at the while searching.
The problem is with the type annotation on this line:
const fooNode: Foo<Node> = new Foo({type: "One"});
By explicitly using Foo<Node> you're preventing the refinement from happening. You can use Foo<*> to make the inference work correctly.
Here's an example:
https://flowtype.org/try/#0PTAEAEDMBsHsHcBQiAuBPADgU1AeQHY4C8oA3qOtgFygBEBWtoAvgNyqY4Aq8soJ5Slhq0esJmw7ZQAOVgATYnkKgAPqDHtEAY2gBDAM4HQAMViwAPDJpzFAPjKJQofAuGytz7bHwGUAJwBXbRRYfwAKfBsASkdnZxQACwBLAwA6V0V+F3ZnZkR85G9fFFBIc1t3M0sAKgcSQnhTc3DSIREGWmZo9nDy2EqaaosGOx7EPoq3IfMLMTHWUBBQLH9-MKA
There are two problems. Foo is invariant, so you will never be able to refine it: it doesn't have any known subtypes other than itself.
On the other hand, even if Foo was covariant, it wouldn't work. You simply can't refine a generic class.
The only practical option is to unwrap, refine, and wrap again.

Typescript - type to represent any class?

What type should I use in typescript to represent any class?
I'm trying to write a function that takes an array of classes and returns an array with different order.
function shuffle(classes: typeof Object[]) : typeof Object[] {
return ...;
}
class A { }
class B extends A { }
class C extends B { }
class D extends B { }
shuffle([A, B, C, D]);
Argument of type 'typeof A[]' is not assignable to parameter of type 'ObjectConstructor[]'.
Then I've tried:
shuffle([typeof A, typeof B, typeof C, typeof D]);
error TS2345: Argument of type 'string[]' is not assignable to parameter of type 'ObjectConstructor[]'.
Type 'string' is not assignable to type 'ObjectConstructor'.
What's the right way? Generics? How? This doesn't work:
export function <T extends typeof Object> shuffle(classes: T[]) : T[]
This neither:
export function <T extends Object> sortClassesBySpeciality(classes: typeof T[]) : typeof T[]
Also why typeof (typeof A) is "string" and "" + typeof A is function? Ok, got this, typeof has two very different meanings context of type definition and expression.
(The ultimate goal is to sort the classes by level of extends from Object.)
You should avoid using the type Object in typescript, you better use any as the docs say:
You might expect Object to play a similar role, as it does in other
languages. But variables of type Object only allow you to assign any
value to them - you can’t call arbitrary methods on them, even ones
that actually exist
But if you want to represent classes then you need to have the following form:
{ new (): CLASS_TYPE }
Or in your case:
function shuffle(classes: Array<{ new (): any }>): Array<{ new (): any }> {
return [];
}
class A { }
class B extends A { }
class C extends B { }
class D extends B { }
shuffle([A, B, C, D]);
(code in playground)
If all of your classes are based on a super class (as your example implies) then you can simply do:
function shuffle(classes: Array<{ new (): A }>): Array<{ new (): A }> {
return [];
}
Edit
Just saw that you want to
sort the classes by level of extends from Object
To answer that:
function shuffle(classes: Array<{ new (): any }>): Array<{ new (): any }> {
return classes.sort((a, b) => getInheritanceLevel(a) - getInheritanceLevel(b));
}
function getInheritanceLevel(cls: { new (): any }): number {
let level = 0;
while (Object.getPrototypeOf(cls.prototype) !== Object.prototype) {
level++;
cls = Object.getPrototypeOf(cls.prototype).constructor;
}
return level;
}
shuffle([D, A, C, B]); // returns [A, B, D, C]
(code in playground)

Resources