What is the purpose of polymorphism?
Polymorphism makes a system of a static type more flexible, without losing (significant) security of a static type, weakening the conditions of equivalence of types. The proof remains that the program will only work if it does not contain type errors.
A polymorphic function or data type is more general than monomorphic because it can be used in a wider range of scenarios. In this sense, polymorphism is the idea of generalization in strongly typed languages.
How does this apply to javascript?
Javascript has a weak dynamic type system. Such a type system is equivalent to a strict type system containing only one type. We can think of such a type as a huge type of union (pseudo syntax):
type T = | Undefined | Null | Number | String | Boolean | Symbol | Object | Array | Map | ...
Each value will be associated with one of these type alternatives at runtime. And since Javascript is weakly typed, each value can change its type any number of times.
If we take a theoretical type point and consider that there is only one type, we can safely say that a Javascript type system does not have the concept of polymorphism. Instead, we have duck typing and implicit type coercion.
But this should not stop us from thinking about types in our programs. Due to the lack of types in Javascript, we need to output them during the coding process. Our mind should stand for the missing compiler, i.e. As soon as we look at the program, we should recognize not only algorithms, but also basic (possibly polymorphic) types. These types will help us create more reliable and more reliable programs.
To do this correctly, I will give you an overview of the most common manifestations of polymorphism.
Parametric polymorphism (aka generics)
Parametric polymorphism suggests that different types are interchangeable, because types do not matter at all. A function that defines one or more parameters of a parametric polymorphic type does not need to know anything about the corresponding arguments, but treat them anyway, because they can take any type. This is quite limiting, since such a function can only work with those properties of its arguments that are not part of their data:
// parametric polymorphic functions const id = x => x; id(1); // 1 id("foo"); // "foo" const k = x => y => x; const k_ = x => y => y; k(1) ("foo"); // 1 k_(1) ("foo"); // "foo" const append = x => xs => xs.concat([x]); append(3) ([1, 2]); // [1, 2, 3] append("c") (["a", "b"]); // ["a", "b", "c"]
Ad-hoc polymorphism (aka overloading)
Ad-hoc polymorphism says different types are equivalent only for a specific purpose. To be equivalent in this sense, a type must implement a set of functions specific to this purpose. A function that defines one or more ad-hoc parameters of a polymorphic type must then know which sets of functions are associated with each of its arguments.
Ad-hoc polymorphism makes the function compatible with a larger type domain. The following example illustrates the purpose of map-over and how types can implement this restriction. Instead of a set of functions, the "displayed" restriction includes only one map function:
Subtype polytype
Since the other answers already cover the subtype polymorphism, I skip it.
Structural polymorphism (aka strutrual subtyping)
Structural polymorphism suggests that different types are equivalent if they contain the same structure in such a way that one type has all the properties of another, but may include additional properties. At the same time, structural polymorphism is duck printing during compilation and certainly offers some additional type safety. But, claiming that two values are of the same type only because they have some properties, he completely ignores the semantic level of values:
const weight = {value: 90, foo: true}; const speed = {value: 90, foo: false, bar: [1, 2, 3]};
Unfortunately, speed is considered a subtype of weight , and as soon as we compare the value properties, we actually compare apples to oranges.