Many times I find myself passing in as an arg a certain Shape type but where each key is optional, only at least one is required.
For example:
type Shape = {
+isFetching: boolean,
+errorFetching: null | string
}
type ShapeOpt = {
isFetching?: boolean,
errorFetching?: boolean
}
function set(data: ShapeOpt) {
for (const key in data) {
global[key] = data[key];
}
}
Is there a utility function to convert from Shape to ShapeOpt?
There is a $Shape<Type> helper for generating an object type where each key is optional. But I don't know of a way to say that at least one item is required automatically.
Related
Updated Question
I want to define a function named bsearch() to do binary searches against arrays of arbitrary object types. When I invoke the function, I want it to check whether or not the Type of the array contains a compare() method and use it, if it does. If it does not, I want it to fall back to using < and === (so it will work with strings and numbers).
What should the function declaration look like? (I don't need an actual implementation, just the syntax for a type-safe solution.)
Or maybe I'm going about this all wrong? How can I create a function that uses a method built into a parameter type if it exists, or use some other function when it doesn't?
Original Question
This is the original question, but I've replaced it with the above as it seems this wasn't getting my point across.
I want to define a function named bsearch() to do binary searches against arrays of arbitrary object types. So I'd like to do something like this:
type Comparator = <Type>(a: Type, b: Type) => -1 | 0 | 1;
static bsearch<Type extends { compare?: Comparator }>(
ary: Type[],
value: Type
): number { ... }
My goal is to specify that Type must extend a type that may or may not include the compare method. In my function, I will check whether the compare method exists on the value parameter and call if it does, or use a generic function (that uses < and ===) if it does not.
The definition of bsearch() does not produce any warnings or errors, but attempts to invoke it from my unit test does:
class Person {
name: string;
length: number;
compare: Comparator<Person>; // What goes here?
}
describe('Utils tests', () => {
const arrayOfInt = [10, 20, 30, 40];
const arrayOfStr = ['Alfred', 'Bob', 'Chuck'];
const arrayOfPersons: Person = [
{name:'Barney',length:2},
{name:'Fred',length:6}
{name:'Wilma',length:12},
];
it('can find integer in an array of integers', () => {
let search_for = 30;
let result = Utils.bsearch(arrayOfInt, search_for)
expect(result).to.be.equal(2);
});
it('can find string in an array of strings', () => {
let search_for = 'Bob';
let result = Utils.bsearch(arrayOfStr, search_for)
expect(result).to.be.equal(1);
});
it('can find Person in an array of Persons', () => {
// This one uses Person.compare() to do the search.
// The previous two tests used the fallback technique.
let search_for = {name:'Fred',length:6};
let result = Utils.bsearch(arrayOfPersons, search_for)
expect(result).to.be.equal(1);
});
});
The error message is:
TS2345: Argument of type 'number[]' is not assignable to parameter of type '{ compare?: Comparator | undefined; }[]'. Type 'number' has no properties in common with type '{ compare?: Comparator | undefined; }'.
I would appreciate pointers to other techniques if there is a better way to accomplish this (I'm still a TypeScript newbie).
Your generic is:
Type extends { compare?: Comparator }
Which means that Type must fulfill { compare?: Comparator } type. While passing object value, for example { name: 'Barney', length: 2, comparator: /* snip */}, is obviously correct, it's not the case for primitives like 10 and Bob. You need to include information about primitive types in the generic, for example:
Type extends ({ compare?: Comparator }) | number | string
Also, you'd probably want to enrich a bit the object typing:
{[key: string]: unknown, compare?: () => void } | number | string
Because, based on your description, you'd also want to accept also objects that do not have compare function in their type signature at all. If it does sound strange, I recommend reading about excess property checking.
I have a type like so:
type o = {
a?: string,
b: string
}
I want to build a type utility that will convert this into
type o = {
a?: ?string,
b: string
}
ie. I want to convert all optional keys, to have optional values too.
Here's what I have so far using $ObjMapi: Flow
Any suggestions?
Consider the following code from gopl.io/ch2/echo4
package main
import (
"flag"
"fmt"
"strings"
)
var n = flag.Bool("n", false, "omit trailing newline")
var sep = flag.String("s", " ", "separator")
func main() {
flag.Parse()
fmt.Print(strings.Join(flag.Args(), *sep))
if !*n {
fmt.Println()
}
}
I'm interested why the variables n and sep are pointers to the flag variables, rather than normal variable type.
It is because they need to be assigned value after they are created. The order of actions is:
Create variable var n = flag.Bool("n", false, "omit trailing newline") The value is false now.
Assign value with flag.Parse(). Variable is now assigned value passed as command line argument.
If you check the code here, you'll see that there's an exported variable called CommandLine, which is a pointer to a FlagSet. This is where the magic happens. When you import that library, it's instantiated. When you invoke the exported functions, for example, flag.Bool(), that function, in turn, calls the method Bool(), which has a pointer receiver to...FlagSet. It will create a new bool to store your flag's value, invoke BoolVar() to store a pointer to the newly created bool variable within the FlagSet data structure (you'll need to trace BoolVar to see how this is accomplished), and returns the very same pointer to you so you can later get the current value (which could be the default value or an entirely new value as a result of the call toParse())
// CommandLine is the default set of command-line flags, parsed from os.Args.
// The top-level functions such as BoolVar, Arg, and so on are wrappers for the
// methods of CommandLine.
var CommandLine = NewFlagSet(os.Args[0], ExitOnError)
// NewFlagSet returns a new, empty flag set with the specified name and
// error handling property. If the name is not empty, it will be printed
// in the default usage message and in error messages.
func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet {
f := &FlagSet{
name: name,
errorHandling: errorHandling,
}
f.Usage = f.defaultUsage
return f
}
// A FlagSet represents a set of defined flags. The zero value of a FlagSet
// has no name and has ContinueOnError error handling.
//
// Flag names must be unique within a FlagSet. An attempt to define a flag whose
// name is already in use will cause a panic.
type FlagSet struct {
// Usage is the function called when an error occurs while parsing flags.
// The field is a function (not a method) that may be changed to point to
// a custom error handler. What happens after Usage is called depends
// on the ErrorHandling setting; for the command line, this defaults
// to ExitOnError, which exits the program after calling Usage.
Usage func()
name string
parsed bool
actual map[string]*Flag
formal map[string]*Flag
args []string // arguments after flags
errorHandling ErrorHandling
output io.Writer // nil means stderr; use Output() accessor
}
// Bool defines a bool flag with specified name, default value, and usage string.
// The return value is the address of a bool variable that stores the value of the flag.
func (f *FlagSet) Bool(name string, value bool, usage string) *bool {
p := new(bool)
f.BoolVar(p, name, value, usage)
return p
}
// Bool defines a bool flag with specified name, default value, and usage string.
// The return value is the address of a bool variable that stores the value of the flag.
func Bool(name string, value bool, usage string) *bool {
return CommandLine.Bool(name, value, usage)
}
Going back to your question:
why the variables n and sep are pointers to the flag variables, rather than normal variable type.
It's because Parse() can manipulate the original variables and your new variables n and sep would have only captured a copy of the original values. By using the pointer, you and the FlagSet are looking at exact the same variables.
I'm trying to think of a function that would allow a Map<String, Any?> object to be treated as Map<String,Any> through type inference through applying a single function.
I am pretty new to the transformation functions in Kotlin and have tried the various filter and filterValues filterNot on the map like so:
val input = mapOf(Pair("first",null))
val filtered: Map<String,Any> = input.filter { it.value!=null }
it also fails to compile with any of these
input.filterValues { it!=null }
input.filterNot { it.value==null }
input.filterNot { it.value is Nothing }
The closest I can seem to get is applying multiple steps or having an Unchecked cast warning. I would have thought that filtering the values to be !=null would suffice. My only other thought is that it's due to the generics?
The filter functions return a Map with the same generic types as the original map. To transform the type of the value, you need to map the values from Any? to Any, by doing a cast. The compiler can't know that the predicate you pass to filter() makes sure all the values of the filtered map are non-null, so it can't use type inference. So your best et is to use
val filtered: Map<String, Any> = map.filterValues { it != null }.mapValues { it -> it.value as Any }
or to define a function doing the filtering and the transformation in a single pass, and thus be able to use smart casts:
fun filterNotNullValues(map: Map<String, Any?>): Map<String, Any> {
val result = LinkedHashMap<String, Any>()
for ((key, value) in map) {
if (value != null) result[key] = value
}
return result
}
The compiler just doesn't perform type analysis deep enough to infer that, for example, input.filterValues { it != null } filters out null values from the map and thus the resulting map should have a not-null value type. Basically there can be arbitrary predicate with arbitrary meaning in terms of types and nullability.
There is no special case function for filtering null values out of a map in the stdlib (like there is .filterIsInstance<T>() for iterables). Therefore your easiest solution is to apply an unchecked cast thus telling the compiler that you are sure about the type safety not being violated:
#Suppress("UNCHECKED_CAST")
fun <K, V> Map<K, V?>.filterNotNullValues() = filterValues { it != null } as Map<K, V>
See also: another question with a similar problem about is-check.
This yields no warnings kotlin 1.5.30
listOfNotNull(
nullableString?.let { "key1" to it },
nullableString?.let { "key2" to it }
).toMap()
I have this Swift code for merging two Dictionaries together:
extension Dictionary {
mutating func extend(newVals: Dictionary <String, Any>) {
for (key,value) in newVals {
self.updateValue(value, forKey:key)
}
}
}
The line that contains .updateValue( , forKey:) generates the following error:
'protocol<>' is not convertible to 'Value'
Previously I had newVals as a Dictionary <String, String> and it worked fine, so my guess is the problem is caused by the use of Any.
My problem here is that I am using Dictionaries with a mixture of Strings and Ints (and possibly other types of value later on).
Is there anyway to get this extend function to work with the type Any?
You don't actually need to (and you can't) specify the Any type in the function declaration - you need to have the subtypes of the newValues dictionary match the dictionary it's extending:
extension Dictionary {
mutating func extend(newVals: Dictionary) {
for (key,value) in newVals {
self.updateValue(value, forKey:key)
}
}
}
var dict = ["one": 1, "two": 2, "hello": "goodbye"]
dict.extend(["three": 3])
dict.extend(["foo": "bar"])
Key and Value are type aliases within the Dictionary type that map to the specific key- and value-types of a particular instance.