In typescript, how do you determine whether another parameter needs to be added based on the previous Boolean parameter? - typescript-typings

This is a example
const fn = (bool, str1, str2) => {
// do something
}
if bool is true, str2 needs to be passed in, otherwise not

You can define overloads like this:
function fn(bool: false, str1: string): void;
function fn(bool: true, str1: string, str2: string): void;
function fn(bool: boolean, str1: string, str2?: string): void {
// do something
}
fn(false, "a"); // OK
fn(false, "a", "b"); // Error
fn(true, "a"); // Error
fn(true, "a", "b"); // OK
Playground

Related

Typescript: Generic type of method params to match type of callback function params

I'm trying to make a class that accepts a function in the constructor. The function can have arguments of any type. Then I want to put a method on the class that accepts that same arguments as function parameter, as it will be a wrapper around this callback. Here's a simplified example to show what I'm trying to do
interface Options<T> {
callbackFn(...x: any[]) => Promise<T>
}
class ExampleClass<T> {
private options: Options<T>;
result: T;
constructor(options: Options<T>) {
this.options = options;
}
async wrapperFn(...x: any[]) {
// Do some stuff before the callback
this.result = await this.options.callbackFn(x)
// Do some stuff after
}
}
const example = new ExampleClass<string>({
callbackFn: (a: string, b:string) => new Promise((res) => {
res(a + b);
})
});
example.wrapperFn("foo", "bar")
This is basically the way I have it now, and it works but it obviously doesn't enforce the types of the params of wrapperFn which isn't ideal. Is there any way to do something like this?
If you want the compiler to keep track of both the callback return type and the callback argument list type, then you'll want Options to be generic in both the return type (you called it T but I'll call it R for "return") and the argument list type (I'll call it A for "arguments"):
interface Options<A extends any[], R> {
callbackFn(...x: A): Promise<R>
}
Now you can just use A anywhere you were using any[] before, and you'll get stronger typing. This also implies that ExampleClass needs to be generic in A and R too:
class ExampleClass<A extends any[], R> {
private options: Options<A, R>;
result?: R;
constructor(options: Options<A, R>) {
this.options = options;
}
async wrapperFn(...x: A) {
// Do some stuff before the callback
this.result = await this.options.callbackFn(...x)
// Do some stuff after
}
}
Let's test it out:
const example = new ExampleClass({
callbackFn: (a: string, b: string) => new Promise<string>((res) => {
res(a + b);
})
});
// const example: ExampleClass<[a: string, b: string], string>
example.wrapperFn("foo", "bar") // okay
example.wrapperFn("foo", 123); // error!
// --------------------> ~~~
// Argument of type 'number' is not assignable to parameter of type 'string'.
Looks good.
Playground link to code

Is it possible to convert string to type in Scala reflection?

I want to convert string to type in scala.Take two following cases as an example:
case class Product(id: String, title: String, description: String)
type aType = Product
client.getProduct[aType](id, "/products").map { x => println(s"Retrieved Product with id '$id': $x") } // Working
// Retrieved Product with id 'test_id': Some(Product(test_id,MyTitle,The text of my Product))
case class Store(id: String, title: String, Address: String)
type aType = Store
client.getStore[aType](id, "/stores").map { x => println(s"Retrieved Store with id '$id': $x") } // working
// Retrieved Store with id 'test_id': Some(Store(test_id, Store Name,The address of my Store))
I want to make this code general for any request, given case classes are already defined. for example
case class Product(id: String, title: String, description: String)
case class Store(id: String, title: String, Address: String)
case class API_Detail(Name: String, CaseClassName: String, Api_Url:String)
var API_List = List[API_DS]()
val request_type = "Product" // or "Store"
val id = "test_id"
val API_List_Item = API_List.filter(_.Name == request_type)
// Want to do like this...
type aType = API_List_Item.CaseClassName.toType /**/
val RequestURL = API_List_Item.Api_Url
/* Interested to know how to convert string to type. To my knowledge
some form of reflection will be implemented. */
client.getRespone[aType](id, RequestURL).map { x => println(s"Retrieved $request_type with id '$id': $x") } // Working
// Retrieved Product with id 'test_id': Some(Product(test_id,MyTitle,The text of my Product))
thanks for inviting me to this thread. Perhaps you can keep the solution simple enough by using pattern matching. The other solution using implicit class is also valid.
val requestUrl = "/products"
requestUrl match {
case "/products" => Products(...)
case "/store" => Store(...)
case _ => UnknownType
}
For additional examples on Pattern Matching, see my tutorial at allaboutscala.com
I'm extending my previous answer on Pattern Matching. There is no need to re-pattern match if we have a Polymorphic Function and I'm making use of Type Classes. I also provide detailed tutorials at allaboutscala.com on the building blocks for type classes: traits, implicits and functions in general.
You will need to expand getResponse(...) to build and populate the fields for your concrete types.
case class Product(id: String, title: String, description: String)
case class Store(id: String, title: String, address: String)
trait Client[T] {
def getResponse(s: String): Option[T]
}
object Client {
implicit val productClient: Client[Product] = new Client[Product] {
override def getResponse(s: String): Option[Product] = s match {
case "/product" => Some(Product("id", "title", "description"))
case _ => None
}
}
implicit val storeClient: Client[Store] = new Client[Store] {
override def getResponse(s: String): Option[Store] = s match {
case "/store" => Some(Store("id", "title", "address"))
case _ => None
}
}
def apply[T : Client](s: String): Option[T] =
implicitly[Client[T]].getResponse(s)
}
val product: Option[Product] = Client[Product]("/product")
val store: Option[Store] = Client[Store]("/store")

How to make compiler check string argument is valid in Typescript?

In TypeScript, how should one go about having the compiler determine whether or not a string is a valid argument to a method/function?
Right now, I am using string literals to accomplish this. For example,
type ValidLetter = "A" | "B" | "C" | "D"; // string literal definition
public PostLetter(letter: ValidLetter) {
...
api.post("https://example.com/letters/", letter);
// POST method only accepts "A", "B", "C", or "D"
}
PostLetter("A") // All good!
PostLetter("Z") // Compiler error
The only thing is, at compile time, I don't know the values I will be passing in to the Post method. I could be receiving any kind of string,
let a = "A";
let foobar = "foobar";
PostLetter(a) // Compiler error
PostLetter(foobar) // Compiler error
What I'm looking for is a way of checking if a string is a valid member of a string literal. I've already attempted using typeof, instanceof, user-defined type guards, and casting. None of them seem to have what it takes.
How would I go about determining that a is a member of ValidLetter and foobar is not? Or perhaps string literals are not the way to go.
TypeScript simply does not do any runtime type checking. Type checking happens at compile time, and type information isn't included in the produced JavaScript file.
post.ts
type ValidLetter = "A" | "B" | "C";
function post(letter: ValidLetter) {
}
Produces the following JavaScript:
post.js
function post(letter) {
}
So you have to re-specify the type check yourself manually in runtime code:
type ValidLetter = "A" | "B" | "C";
function post(letter: ValidLetter) {
if (letter !== "A" && letter !== "B" && letter !== "C") throw "error!";
}
Not too bad. But it's a bit redundant, isn't it?
There's a library called runtypes, that allows you to specify your types once, and it produces a compile-time TypeScript type, as well as keeping type information to do runtime checks:
import { Literal, Union, Static } from 'runtypes'
const ValidLetter = Union(Literal('A'), Literal('B'), Literal('C'));
type ValidLetter = Static<typeof ValidLetter>;
function post(letter: ValidLetter) {
ValidLetter.check(letter);
}
So now you get both full compile-time checks, and runtime checks.
You should be able to do this with a mix of value maps, as well as the user-defined type guards:
const ValidLetterMap = { A: 1, B: 1, C: 1, D: 1 };
type ValidLetter = keyof typeof ValidLetterMap;
declare function postLetter(letter: ValidLetter): void;
postLetter("A"); // ok
postLetter("E"); // err
const a = "A";
postLetter(a); // ok
let a$ = "A";
postLetter(a$); // err, a$ is of type string since it is mutable
function isValidLetter(letter: string): letter is ValidLetter {
return letter in ValidLetterMap;
}
if (isValidLetter(a$)) {
postLetter(a$); // now ok because we've "proven" that a$ is a valid letter
}
Edit: here's a generic form, relying on a slight hack to expose typing.
class StringLiteral<T extends string> {
private literalSet: {[P in T]: true};
// sort of a hack so we can expose a union type of valid letters
public get typeProvider(): T {
throw new Error("typeProvider is only meant for typing info, it has no value");
}
constructor(...literals: T[]) {
this.literalSet = literals.reduce(
(acc, curr) => (acc[curr] = true, acc),
{} as {[P in T]: true}
);
}
public isValid(candidate: string): candidate is T {
return candidate in this.literalSet;
}
}
// how to use
const lettersLiteral = new StringLiteral("A", "B", "C", "D");
declare function postLetter(letter: typeof lettersLiteral.typeProvider): void;
let a$ = "A";
postLetter(a$); // not ok
if (lettersLiteral.isValid(a$)) {
postLetter(a$); // ok!!
}

Using reflection SetString

I have a struct like this one:
type ProductionInfo struct {
StructA []Entry
}
type Entry struct {
Field1 string
Field2 int
}
I would like to change the value of Field1 using reflection but the reflect object returned always CanSet() = false. What can I do? See playground example.
https://play.golang.org/p/eM_KHC3kQ5
Here is the code:
func SetField(source interface{}, fieldName string, fieldValue string) {
v := reflect.ValueOf(source)
tt := reflect.TypeOf(source)
for k := 0; k < tt.NumField(); k++ {
fieldValue := reflect.ValueOf(v.Field(k))
fmt.Println(fieldValue.CanSet())
if fieldValue.CanSet() {
fieldValue.SetString(fieldValue.String())
}
}
}
func main() {
source := ProductionInfo{}
source.StructA = append(source.StructA, Entry{Field1: "A", Field2: 2})
SetField(source, "Field1", "NEW_VALUE")
}
Multiple errors. Let's iterate over them.
First, you pass a value of ProductionInfo and not a value of Entry whose field you want to modify, so first change it to:
SetField(source.StructA[0], "Field1", "NEW_VALUE")
Next, you are passing a (non-pointer) value. You can't modify the fields of a non-pointer struct with reflection, because that would only modify a copy which would be discarded. In order to avoid this (and further confusion), this is not allowed (CanSet() returns false). So you have to pass a pointer to the struct:
SetField(&source.StructA[0], "Field1", "NEW_VALUE")
Now inside SetField() the reflect.ValueOf(source) will describe the passed pointer. You may use Value.Elem() to navigate to the reflect.Value of the pointed object (the struct value):
v := reflect.ValueOf(source).Elem()
And now it works. Modified code:
func SetField(source interface{}, fieldName string, fieldValue string) {
v := reflect.ValueOf(source).Elem()
fmt.Println(v.FieldByName(fieldName).CanSet())
if v.FieldByName(fieldName).CanSet() {
v.FieldByName(fieldName).SetString(fieldValue)
}
}
func main() {
source := ProductionInfo{}
source.StructA = append(source.StructA, Entry{Field1: "A", Field2: 2})
fmt.Println("Before: ", source.StructA[0])
SetField(&source.StructA[0], "Field1", "NEW_VALUE")
fmt.Println("After: ", source.StructA[0])
}
Output (try it on the Go Playground):
Before: {A 2}
true
After: {NEW_VALUE 2}

How to use the type String as a parameter?

object UtilitiesHelper extends App {
override def main(args: Array[String]): Unit = {
def convert(value: String, targetType: Any): Any = {
targetType match {
case _: Int => value.toInt
case _: Float => value.toFloat
case _: Double => value.toDouble
case _: String => value
}
}
def convert2(value: String, targetType: Any): Any = {
targetType match {
case Int => value.toInt
case Float => value.toFloat
case Double => value.toDouble
// Hack #1
case _ => value
// Ideally we want
//case String => value
}
}
println(convert("1", 1))
println(convert("1.0", 1.0))
println(convert("Hello world", ""))
println(convert2("1", Int))
println(convert2("1.0", Double))
// Hack #2
println(convert2("Hello world", "".getClass()))
// Ideally we want
//println(convert2("Hello world", String)))
}
}
What's the proper way to accomplish what hack #1 and #2 is doing?
I can't figure out why convert2("1", String) gives a compile-time error (with the message "object java.lang.String is not a value") whereas convert2("1", Int) compiles and runs perfectly fine. Does it have something to do with Int being a Scala native type and String being an alias for java.lang.String?
I came up to something like this. It is a bit more complicated, but it works for me.
trait Converter[T] {
def convert(value: String): T
}
object ConvertExample extends Application {
implicit object IntConverter extends Converter[Int] {
def convert(value: String) = value.toInt
}
implicit object StringConverter extends Converter[String] {
def convert(value: String) = value.toString
}
def convert[T](value: String)(implicit converter: Converter[T]): T = converter.convert(value)
println(convert[Int]("3"))
println(convert[String]("asdasd"))
}
Your code works with Int because there is an object called object Int, defined somewhere, the same not being true for String. That said, you can write your code by using a type parameter, like so:
def convert2[T](value: String): T = {
// do something...
}

Resources