Restricting function to object type - flowtype

I have a function which iterates over an object's properties, like this:
function somef(obj) {
for (const prop in obj) {}
}
This is reported as a possible string by Flowtype:
Cannot iterate using a `for...in` statement because string [1] is not an object, null, or undefined. [invalid-in-rhs]
54| for (const prop in obj) {}
^^^
References:
54| for (const prop in obj) {}
^^^^ [1]
I tried typing obj as any, and this checks, but then it allows passing in a string to somef(), which I understand as string being an any:
function somef(obj:any) {
for (const prop in obj) {} // Would fail at runtime
}
somf("mystring) // Checks
Is it possible to restrict somef to only accept bracket objects obj of the form {} on which I can call: for (prop in obj) {} on?

Your problem is that you have not given any explicit type def to the function so it implicitly defines types based on the code around it, given it's being called with a string, it would assume obj is a string and it can't be looped. It throws an error correctly, just not in the place you expect because of this implicitly typed def.
If you know the exact structure of the object, you can type as
function somef(obj: { a: string, b: string }) {
for (const prop in obj) {}
}
But if it's more unknown I recommend indexer object,
function somef(obj: { [key: string]: any }) {
for (const prop in obj) {} // Would fail at runtime
}
docs: https://flow.org/en/docs/types/objects/
try example

Related

How can I determine if an Array is readonly using TS compiler-api?

I'd like to determine if an array type is readonly. This includes ReadonlyArray and readonly prefixed.
Examples:
type a = ReadonlyArray<string>
type b = readonly string[]
The relevant non-exposed TypeChecker code is:
let globalReadonlyArrayType = <GenericType>getGlobalTypeOrUndefined("ReadonlyArray" as __String, /*arity*/ 1) || globalArrayType;
function isReadonlyArrayType(type: Type): boolean {
return !!(getObjectFlags(type) & ObjectFlags.Reference) && (<TypeReference>type).target === globalReadonlyArrayType;
}
function getGlobalTypeOrUndefined(name: __String, arity = 0): ObjectType | undefined {
const symbol = getGlobalSymbol(name, SymbolFlags.Type, /*diagnostic*/ undefined);
return symbol && <GenericType>getTypeOfGlobalSymbol(symbol, arity);
}
function getTypeOfGlobalSymbol(symbol: Symbol | undefined, arity: number): ObjectType {
function getTypeDeclaration(symbol: Symbol): Declaration | undefined {
const declarations = symbol.declarations;
for (const declaration of declarations) {
switch (declaration.kind) {
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.EnumDeclaration:
return declaration;
}
}
}
if (!symbol) {
return arity ? emptyGenericType : emptyObjectType;
}
const type = getDeclaredTypeOfSymbol(symbol);
if (!(type.flags & TypeFlags.Object)) {
error(getTypeDeclaration(symbol), Diagnostics.Global_type_0_must_be_a_class_or_interface_type, symbolName(symbol));
return arity ? emptyGenericType : emptyObjectType;
}
if (length((<InterfaceType>type).typeParameters) !== arity) {
error(getTypeDeclaration(symbol), Diagnostics.Global_type_0_must_have_1_type_parameter_s, symbolName(symbol), arity);
return arity ? emptyGenericType : emptyObjectType;
}
return <ObjectType>type;
}
TypeChecker Method
cspotcode pointed out that you can get IndexInfo via the TypeChecker.
const isReadonlyArrayType = (type: Type) =>
type.checker.isArrayLikeType(type) &&
!!type.checker.getIndexInfoOfType(type, IndexKind.Number)?.isReadonly
TS Compiler Method
The following matches the compiler's logic.
let globalReadonlyArrayType: Type;
export const isReadonlyArrayType = (type: Type): boolean => {
const { checker } = type;
if (!globalReadonlyArrayType) {
const symbol =
checker.resolveName('ReadonlyArray', /* location */ void 0, SymbolFlags.Type, /* excludeGlobals */ false)!;
globalReadonlyArrayType = checker.getDeclaredTypeOfSymbol(symbol);
}
return !!((type as ObjectType).objectFlags & ObjectFlags.Reference) &&
((<TypeReference>type).target === globalReadonlyArrayType);
};
Notes
It appears that there may be no immediate advantage of the TypeChecker method over using the Compiler method. The one concern that I had was that comparing target equality may fail if ReadonlyArray was extended, but it appears that this is currently not possible with TypeScript (v3.9.3)
Logic-wise, if performing isArrayLikeType first, the TypeChecker method would be performing a little more work, but likely not enough to worry about in terms of performance.
With that said, it seems that there may be advantage in the TypeChecker method over the second in the event that TS changes its readonly logic, allows extension of ReadonlyArray, etc.
For that reason, I'd recommend using the TypeChecker method.
If you're not using byots, you could probably replace the call to isArrayLikeType with !!((type as ObjectType).objectFlags & ObjectFlags.Reference)
Caveat: My understanding of ReadonlyArray is at a basic level, as of writing this, so if I'm wrong on any of this, please let me know!

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)

Recursively Create ReadOnly Object in FlowJS

In redux, the state should be immutable. I would like Flow to prevent anyone from mutating that state. So, given an object of arbitrary depth:
type object = {
a: {
b: {
d: string
}
},
c: number
}
How can I create a new type that is recursively readonly, so that I cannot do:
let TestFunction = (param: $RecursiveReadOnly<object>) => {
param.a.b.d = 'some string'
}
The builtin $ReadOnly utility of Flow will create a type like this, which isn't what is needed, because b & d are still writable:
{
+a: {
b: {
d: string
}
},
+c: number
}
I've been trying to use the $Call & $ObjMap(i), but I can't figure out how to recursively travel an object in Flow. The objective is to have this:
{
+a: {
+b: {
+d: string
}
},
+c: number
}
Thanks to kalley for his solution. From what I understood, kalley tried to make any object received by a function recursively read only. Since I really only needed known objects as parameters, this works perfectly:
// Type definition that works with arbitrary nested objects/arrays etc.
declare type RecursiveReadOnly<O: Object> = $ReadOnly<$ObjMap<O, typeof makeRecursive>>
declare type RecursiveReadOnlyArray<O: Object> = $ReadOnlyArray<$ReadOnly<$ObjMap<O, typeof makeRecursive>>>
type Recursive<O: Object> = $ObjMap<O, typeof makeRecursive>
declare function makeRecursive<F: Function>(F): F
declare function makeRecursive<A: Object[]>(A): $ReadOnlyArray<$ReadOnly<Recursive<$ElementType<A, number>>>>
declare function makeRecursive<O: Object>(O): RecursiveReadOnly<O>
declare function makeRecursive<I: string[] | boolean[] | number[]>(I): $ReadOnlyArray<$ElementType<I, number>>
declare function makeRecursive<I: string | boolean | number | void | null>(I): I
// Usage example.
type obj = {
a: {
b: {
d: string,
}
}
}
let TestFunction = (param: RecursiveReadOnly<obj>) => {
param.a.b.d = 'some string' // Flow throws an error
}

What's the behavior of Iterable#all & Why did Kotlin Char::class.java != char.javaClass

I'm trying an example in kotlin, like:
fun test(){
val harfler = listOf("a","b",'c','d')
println(harfler.all {
it.javaClass == String::class.java || it.javaClass == Char::class.java
})
}
List contains Char or String but all function in this expression returns false,Why return false?
Can anybody explain it?
Edit
for #JBNizet
As #JB Nizet has already told you how to analyze the problem.
According to the Mapped Types, The Kotlin Char will be mapped to Java Type decide on its declaration.
when declare as a non-nullable type Char it is a primitive Java type char.
when declare as a nullable type Char? it is a Java wrapper type Character.
when declare as a type argument List<Char> it is a Java wrapper type Character.
val it = 'a'
// v--- it should be `Any`
val array: Array<Any> = arrayOf('a')
// v--- char
println(it.javaClass)
// v--- print [java.lang.Character]
println(array.map { it.javaClass })
But I want to say that there is a different between the usage and the declaration.
For example, the parameter type it is a java.lang.Character, but its javaClass is char.
fun typeOf(it: Char?) = it?.javaClass
fun test() {
// v--- java.lang.Character
println(::typeOf.javaMethod!!.parameterTypes[0])
// v--- but it return `char` rather than `java.lang.Character`
println(typeOf('a'))
}
And the example below show the different as further, this is why I declare the array type to Array<Any> rather than Array<Char> in preceding example:
// v--- uses `java.lang.Character` instead
val array: Array<Char> = arrayOf('a')
// v--- java.lang.Character
println(array.javaClass.componentType)
// v--- [char]
println(array.map { it.javaClass })
Why did the strange behavior occurs in Koltin?
This is because Kotlin Char and other wrapper classes represent 2 roles. one is a Java primitive type char, another is a Java wrapper class java.lang.Character. However, Kotlin Char is statically which means you can't change its type in runtime. and a Char should be mapped to a char by default in Kotlin.
IF you want get the wrapper type every time, you should use KClass.javaObjectType instead, for example:
// v--- char
println(Char::class.java)
// v--- java.lang.Character
println(Char::class.javaObjectType)
The Iterable#all operation is a short-circuiting operation, which means if any first element didn't satisfied will return false immediately.
inline fun <T> Iterable<T>.all(predicate: (T) -> Boolean): Boolean {
// return `false` immediately the condition didn't satisfied
// v
for (element in this) if (!predicate(element)) return false
return true
}
When checking a Kotlin class like as Char and others. you should use the Kotlin type checking mechanism rather than traditional comparing approach, it helps you avoid such a confusion. for example:
val anything: Array<Any> = arrayOf('a')
val chars: Array<Char> = arrayOf('a')
println(chars.all { it is Char }) // print true
println(anything.all { it is Char }) // print true
So your code can replace with type checking as below:
fun test() {
val harfler = listOf("a", "b", 'c', 'd')
// v---------------v--- use type checking here
println(harfler.all { it is String || it is Char }) // print true
}

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