Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Explain generics using Javascript's Flowtype

I have never written in statically typed language before. I'm mostly developing in Javascript and lately I've been interested in learning more about FB's Flowtype.

I find the documentation nicely written and I understand most of it. However I don't quite get the concept of generics. I've tried googling some examples / explanations but with no luck.

Could someone please explain what generics are, what are they mostly used for and perhaps provide an example?

like image 779
user3056783 Avatar asked Jun 28 '17 18:06

user3056783


People also ask

How do you explain generics?

Genetics is the study of how genes and how traits are passed down from one generation to the next. Our genes carry information that affects our health, our appearance, and even our personality! GENetics is where it all begins. Some scientists are curious about basic questions of life: Where did it come from?

What are generics and how is using them useful?

In a nutshell, generics enable types (classes and interfaces) to be parameters when defining classes, interfaces and methods. Much like the more familiar formal parameters used in method declarations, type parameters provide a way for you to re-use the same code with different inputs.

What are generics in Swift with example?

Swift 4 language provides 'Generic' features to write flexible and reusable functions and types. Generics are used to avoid duplication and to provide abstraction. Swift 4 standard libraries are built with generics code. Swift 4s 'Arrays' and 'Dictionary' types belong to generic collections.

How do you create a class type in typescript using generics?

Using Class Types in Generics. When creating factories in TypeScript using generics, it is necessary to refer to class types by their constructor functions. For example, function create < Type > ( c: { new (): Type }): Type {. return new c ();

What is generics in Java?

Generics means parameterized types. The idea is to allow type (Integer, String, … etc, and user-defined types) to be a parameter to methods, classes, and interfaces. Using Generics, it is possible to create classes that work with different data types. An entity such as class, interface, or method that operates on a parameterized type is called ...

Why choose flowType JS?

The font-size thresholds will stop FlowType.JS from resizing the text beyond certain font-sizes. Lastly, you have full control over the base font-size, so that you can set your typography perfectly. FlowType.JS is extremely easy to use.

What is generics in C++?

Generics means parameterized types. The idea is to allow type (Integer, String, … etc., and user-defined types) to be a parameter to methods, classes, and interfaces. Using Generics, it is possible to create classes that work with different data types.


2 Answers

Let's say I want to write a class that just stores a single value. Obviously this is contrived; I'm keeping it simple. In reality this might be some collection, like an Array, that can store more than one value.

Let's say I need to wrap a 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 that number out:

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

So far so good. But wait, now I also want to wrap a string! If I naively just try to wrap a string, I get an error:

const w = new Wrap("foo");

Gives the error:

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

This doesn't work because I told Flow that Wrap just takes numbers. I could rename Wrap to WrapNumber, then copy it, call the copy WrapString, and change number to string inside the body. But that is tedious and now I have two copies of the same thing to maintain. If I keep copying every time I want to wrap a new type, this will quickly get out of hand.

But notice that Wrap doesn't actually operate on the value. It doesn't care whether it is number or string, or something else. It only exists to store it and give it back later. The only important invariant here is that the value you give it and the value you get back are the same type. It doesn't matter what specific type is used, just that those two values have the same one.

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

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

T here is just a placeholder. It means "I don't care what type you put here, but it's important that everywhere T is used, it is the same type." If I pass you a Wrap<number> you can access the value property and know that it is a number. Similarly, if I pass you a Wrap<string> you know that the value for that instance is a string. With this new definition for Wrap, let's try again to wrap both a number and a 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 infers the type parameter and is able to understand that everything here will work at runtime. We also get an error, as expected, if we try to do this:

needsString(wNum.value);

Error:

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

(tryflow for the full example)

like image 90
Nat Mote Avatar answered Oct 11 '22 05:10

Nat Mote


Generics among statically typed languages are a method of defining a single function or class that can be applied to any type dependency 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 at the type of another that are assigned to the same generic value.

For example, if you wanted to write a function that added two parameters together, that operation (depending on the language) could be entirely different. In JavaScript, since it is not a statically typed language to begin with, you can do this anyway and type check within the function, however Facebook's Flow allows for type consistency and validation in addition to single 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 generic 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 of the function since we know that the parameters will always be of the same type, we can test the type of one of them using standard JavaScript and return what we perceive and "addition" for that type to be.

When in use 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 will throw typing errors if we attempt:

add(2, 'three')
add({ two: 2 }, 3)
// etc.
like image 28
m_callens Avatar answered Oct 11 '22 06:10

m_callens