Explanation of generics using Javascript Flowtype

I haven't written in a statically typed language before. I am mainly developing in Javascript and lately I have been interested in learning more about FB Flowtype.

I find the documentation nicely written and I understand most of it. However, I don't fully understand the concept of generics . I have tried some examples / explanations but no luck.

Can someone please explain what generics are, what they are mainly used for, and maybe give an example?

+3


source to share


3 answers


Let's say I want to write a class that just stores one value. Obviously, this is contrived; I keep it simple. In fact, it can be some collection, for example Array

, that can store more than one value.

Let's say I need to wrap number

:

class Wrap {
  value: number;
  constructor(v: number) {
    this.value = v;
  }
}

      

Now I can create an instance that stores a number and I can get this number:

const w = new Wrap(5);
console.log(w.value);

      

So far so good. But wait, now I also want to wrap string

! If I'm naively just trying to wrap a string, I get the error:

const w = new Wrap("foo");

      

Gives an error:

const w = new Wrap("foo");
                       ^ string. This type is incompatible with the expected param type of
constructor(v: number) {
                    ^ number

      

It doesn't work because I told Flow it Wrap

just accepts numbers

. I could rename Wrap

to WrapNumber

, then copy it, call the copy, WrapString

and change number

to string

inside the body. But it's tedious and now I have two copies of the same thing to maintain. If I copy every time I want to wrap a new type, it quickly gets out of hand.



But note that it Wrap

doesn't actually work for value

. It doesn't matter if it is, number

or string

, or something else. It only exists to store it and return it later. The only important invariant here is that the value you give it and the value you return is of the same type . It doesn't matter which particular type is used, it's just that the two values ​​are the same.

So, with that in mind, we can add a parameter like:

class Wrap<T> {
  value: T;
  constructor(v: T) {
    this.value = v;
  }
}

      

T

here is just a placeholder. This means, "I don't care what type you specify here, but it's important that T

the same type is used everywhere ." If I pass it to you Wrap<number>

, you can access the property value

and know what it is number

. Likewise, if I pass it to you Wrap<string>

, you know what is value

for this instance - string

. With this new definition for, Wrap

try again to wrap both number

and string

:

function needsNumber(x: number): void {}
function needsString(x: string): void {}

const wNum = new Wrap(5);
const wStr = new Wrap("foo");

needsNumber(wNum.value);
needsString(wStr.value);

      

Flow describes a type parameter and can figure out that this will all work at runtime. We also get the error as expected if we try to do this:

needsString(wNum.value);

      

Mistake:

20: needsString(wNum.value);
                ^ number. This type is incompatible with the expected param type of
11: function needsString(x: string): void {}
                            ^ string

      

( tryflow for a complete example)

+4


source


Generalization between statically typed languages ​​is a method of defining a single function or class that can be applied to dependencies of any type, instead of writing a separate function / class for each possible data type. They ensure that the type of one value will always be the same type of another type that is assigned the same general value.

For example, if you wanted to write a function that added two parameters together, that operation (depending on the language) could be completely different. In JavaScript, since it is not a statically typed language, you can do this anyway and type-check inside a function, however Facebook Flow

allows for type consistency and checking in addition to separate definitions.

function add<T>(v1: T, v2: T): T {
    if (typeof v1 == 'string')
        return `${v1} ${v2}`
    else if (typeof v1 == 'object')
        return { ...v1, ...v2 }
    else
        return v1 + v2
}

      

In this example, we define a function with a common type T

and say that all parameters will be of the same type T

, and the function will always return the same type T

. Inside the function, since we know that the parameters will always be of the same type, we can check the type of one of them using standard JavaScript and return what we perceive and "append" for that type.



When used later in our code, this function can then be called as:

add(2, 3) // 5
add('two', 'three') // 'two three'
add({ two: 2 }, { three: 3 }) // { two: 2, three: 3 }

      

But we'll throw input errors if we try:

add(2, 'three')
add({ two: 2 }, 3)
// etc.

      

+4


source


Basically, it's just a placeholder for a type.

When using a generic type, we say that any stream type can be used here.

By putting <T>

before the arguments of a function, we say that the function can (but does not have to) use the generic type T

anywhere in its argument list, its body, and as a return type.

Let's take a look at their basic example:

function identity<T>(value: T): T {
  return value;
}

      

This means that the parameter value

inside identity

will have some type that is unknown in advance. Whatever the type, the return value identity

must match that type as well.

const x: string = identity("foo"); // x === "foo"
const y: string = identity(123);   // Error

      

An easy way to think about generics is to imagine one of the primitive types instead T

and see how it works, and then understand that that primitive type can be replaced with any other.

In terms of identity

: think of it as a function that takes [string] and returns [string]. Then understand that [string] can be any other valid stream type. This means that identity

is a function that takes T

and returns a T

, where T

is any stream type.

The docs also have this helpful analogy:

Generic types work the same way as variables or function parameters, except that they are used for types.

Note. Another word for this concept is polymorphism .

+1


source







All Articles