├── .eslintignore ├── .gitignore ├── knowledge.map ├── Haskell ├── maybe2.hs ├── maybe.hs └── maybe3.hs ├── README.md ├── .editorconfig ├── JavaScript ├── 4-line.js ├── 2-proto-short.js ├── 1-proto.js ├── 3-closure.js ├── 5-proto-ap.js ├── 7-use-case.js ├── 6-closure-ap.js ├── 9-functor.js ├── 8-path.js └── a-collector.js ├── Scala └── Advanced │ ├── 2-functor-syntax.scala │ ├── README.md │ └── 1-functor.scala ├── LICENSE └── .eslintrc.json /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /knowledge.map: -------------------------------------------------------------------------------- 1 | { 2 | dependencies: ['Generators', 'Function', 'Closure'] 3 | } -------------------------------------------------------------------------------- /Haskell/maybe2.hs: -------------------------------------------------------------------------------- 1 | main = do 2 | print $ (* 2) . (+ 5) . (^ 2) <$> Just 5 3 | print $ (* 2) . (+ 5) . (^ 2) <$> Nothing 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mappable objects 2 | 3 | [![Функциональные объекты, функторы и монады](https://img.youtube.com/vi/3Z7f0Gi8pxw/0.jpg)](https://www.youtube.com/watch?v=3Z7f0Gi8pxw) 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | end_of_line = lf 6 | charset = utf-8 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | [{*.js,*.mjs,*.ts,*.json,*.yml}] 11 | indent_size = 2 12 | indent_style = space 13 | -------------------------------------------------------------------------------- /JavaScript/4-line.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const maybe = (x) => (fn) => maybe(x && fn ? fn(x) : null); 4 | 5 | // Usage 6 | 7 | maybe(5)()(console.log); 8 | maybe(5)((x) => ++x)(console.log); 9 | maybe(5)((x) => x * 2)(console.log); 10 | maybe(null)((x) => x * 2)(console.log); 11 | maybe(5)((x) => x * 2)((x) => ++x)(console.log); 12 | -------------------------------------------------------------------------------- /JavaScript/2-proto-short.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function Maybe(x) { 4 | this.x = x; 5 | } 6 | 7 | Maybe.prototype.map = function(fn) { 8 | return new Maybe(this.x && fn ? fn(this.x) : null); 9 | }; 10 | 11 | // Usage 12 | 13 | new Maybe(5).map().map(console.log); 14 | new Maybe(5).map((x) => x * 2).map(console.log); 15 | new Maybe(null).map((x) => x * 2).map(console.log); 16 | -------------------------------------------------------------------------------- /JavaScript/1-proto.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function Maybe(x) { 4 | this.x = x; 5 | } 6 | 7 | Maybe.prototype.map = function(fn) { 8 | if (this.x && fn) { 9 | return new Maybe(fn(this.x)); 10 | } else { 11 | return new Maybe(null); 12 | } 13 | }; 14 | 15 | // Usage 16 | 17 | new Maybe(5).map().map(console.log); 18 | new Maybe(5).map((x) => x * 2).map(console.log); 19 | new Maybe(null).map((x) => x * 2).map(console.log); 20 | -------------------------------------------------------------------------------- /JavaScript/3-closure.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function maybe(x) { 4 | return function(fn) { 5 | if (x && fn) { 6 | return maybe(fn(x)); 7 | } else { 8 | return maybe(null); 9 | } 10 | }; 11 | } 12 | 13 | // Usage 14 | 15 | maybe(5)()(console.log); 16 | maybe(5)((x) => ++x)(console.log); 17 | maybe(5)((x) => x * 2)(console.log); 18 | maybe(null)((x) => x * 2)(console.log); 19 | maybe(5)((x) => x * 2)((x) => ++x)(console.log); 20 | -------------------------------------------------------------------------------- /Haskell/maybe.hs: -------------------------------------------------------------------------------- 1 | import Prelude hiding (Maybe(..), Functor, fmap, (<$>)) 2 | 3 | data Maybe a = Just a | Nothing 4 | deriving (Show) 5 | 6 | class Functor f where 7 | fmap :: (a -> b) -> f a -> f b 8 | 9 | (<$>) :: Functor f => (a -> b) -> f a -> f b 10 | (<$>) = fmap 11 | infixl 4 <$> 12 | 13 | instance Functor Maybe where 14 | fmap f (Just x) = Just $ f x 15 | fmap _ Nothing = Nothing 16 | 17 | main = do 18 | print $ (* 2) <$> Just 5 19 | print $ (* 2) <$> Nothing 20 | -------------------------------------------------------------------------------- /JavaScript/5-proto-ap.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function Maybe(x) { 4 | this.x = x; 5 | } 6 | 7 | Maybe.prototype.map = function(fn) { 8 | const res = (this.x && fn) ? fn(this.x) : null; 9 | return res instanceof Maybe ? res : new Maybe(res); 10 | }; 11 | 12 | Maybe.prototype.ap = function(functor) { 13 | return this.map((val) => functor.map((f) => f(val))); 14 | }; 15 | 16 | // Usage 17 | 18 | const a = new Maybe(5); 19 | const f1 = new Maybe((x) => x * 2); 20 | const f2 = new Maybe((x) => ++x); 21 | 22 | a.ap(f1).ap(f2).map(console.log); 23 | 24 | -------------------------------------------------------------------------------- /Haskell/maybe3.hs: -------------------------------------------------------------------------------- 1 | import Prelude hiding (Applicative(..)) 2 | 3 | class Functor f => Applicative f where 4 | pure :: a -> f a 5 | (<*>) :: f (a -> b) -> f a -> f b 6 | (*>) :: f a -> f b -> f b 7 | (<*) :: f a -> f b -> f a 8 | 9 | a *> b = fmap const b <*> a 10 | (<*) = flip (*>) 11 | {-# MINIMAL pure, (<*>) #-} 12 | 13 | instance Applicative Maybe where 14 | pure = Just 15 | (Just f) <*> v = fmap f v 16 | Nothing <*> _ = Nothing 17 | 18 | main = do 19 | print $ (*) <$> Nothing <*> Nothing 20 | print $ (*) <$> Nothing <*> Just 2 21 | print $ (*) <$> Just 3 <*> Nothing 22 | print $ (*) <$> Just 3 <*> Just 2 23 | -------------------------------------------------------------------------------- /JavaScript/7-use-case.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const maybe = (x) => { 4 | const map = (fn) => maybe(x ? fn(x) : null); 5 | map.ap = (functor) => functor((f) => (x && f ? f(x) : null)); 6 | return map; 7 | }; 8 | 9 | // Usage 10 | 11 | const config = { 12 | coords: { 13 | x: 0, 14 | y: 5, 15 | }, 16 | velocity: { 17 | x: 1, 18 | y: 1, 19 | }, 20 | }; 21 | 22 | const addVelocity = (velocity) => (coords) => { 23 | coords.x += velocity.x; 24 | coords.y += velocity.y; 25 | return coords; 26 | }; 27 | 28 | const coords = maybe(config.coords); 29 | const velocity = maybe(config.velocity); 30 | 31 | coords.ap(velocity(addVelocity))(console.log); 32 | -------------------------------------------------------------------------------- /JavaScript/6-closure-ap.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const maybe = (x) => { 4 | const map = (fn) => maybe(x ? fn(x) : null); 5 | const ap = (functor) => functor.map((f) => (x && f ? f(x) : null)); 6 | const chain = (f) => f(x); 7 | return Object.assign(map, { map, ap, chain }); 8 | }; 9 | 10 | // Usage 11 | 12 | const twice = (x) => x * 2; 13 | const inc = (x) => ++x; 14 | 15 | maybe(5)(twice)(inc)(console.log); 16 | maybe(5).map(twice).map(inc).map(console.log); 17 | maybe(5)(twice).ap(maybe(inc))(console.log); 18 | maybe(5)(twice).ap(maybe())(console.log); 19 | maybe(5).chain((x) => maybe(x * 2))(inc)(console.log); 20 | maybe(5).chain((x) => maybe(x * 2)).map(inc)(console.log); 21 | -------------------------------------------------------------------------------- /JavaScript/9-functor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function Counter() {} 4 | 5 | // TODO: check this code 6 | 7 | const counter = (initial) => { 8 | const f = (val) => { 9 | f.count += val; 10 | Object.keys(f.events).filter((n) => n <= f.count).forEach((n) => { 11 | f.events[n].forEach((callback) => callback(f.count)); 12 | delete f.events[n]; 13 | }); 14 | return f; 15 | }; 16 | Object.setPrototypeOf(f, Counter.prototype); 17 | return Object.assign(f, { count: 0, events: {} })(initial); 18 | }; 19 | 20 | Counter.prototype.on = function(n, callback) { 21 | const event = this.events[n]; 22 | if (event) event.push(callback); 23 | else this.events[n] = [callback]; 24 | return this(0); 25 | }; 26 | 27 | // Usage 28 | 29 | const c = counter(10); 30 | c.on(5, (val) => console.log('Counter > 5, value:', val)); 31 | c.on(25, (val) => console.log('Counter > 25, value:', val)); 32 | c(5); 33 | setTimeout(() => c(15), 100); 34 | -------------------------------------------------------------------------------- /Scala/Advanced/2-functor-syntax.scala: -------------------------------------------------------------------------------- 1 | package functor 2 | 3 | import scala.language.higherKinds 4 | 5 | object FunctorSyntax { 6 | //Implicit Functor operations class 7 | implicit class FunctorOps[F[_], A](val obj: F[A]) extends AnyVal { 8 | def fmap[B](f: A => B)(implicit functor: Functor[F]): F[B] = 9 | functor.fmap(obj)(f) 10 | } 11 | } 12 | 13 | object Main2 extends App { 14 | import Option._ 15 | import FunctorSyntax._ 16 | 17 | /* 18 | * Option is created using factory method and there is reason for this. 19 | * For instance such example wont compile: 20 | * 21 | * Some(2).fmap(_ + 1) 22 | * 23 | * The reason is that it will search for Functor[Some] 24 | * and would not take into account Functor[Option] 25 | * that could be fixed by making Functor typeclass Invariant like it is fixed in Cats 26 | * but it is a little bit complicated for our simple example 27 | */ 28 | println(some("2").fmap(_.toDouble).fmap(a => a + 3)) 29 | //prints Some(5.0) 30 | } 31 | -------------------------------------------------------------------------------- /Scala/Advanced/README.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | The most flexible way to implement functor in Scala is to define it as typeclass. 3 | Good example of such behaviour is Cats and Scalaz libraries that supply developer with different typeclasses. This libraries include Functor too, but in much more complicated and mature form than in this example. 4 | 5 | ## How to Run 6 | First sources should be compiled to `.jar` file using `scalac` command: 7 | > scalac *.scala -d Functor.jar 8 | 9 | Next the examples can be executed by their fully qualified name(either `functor.Main1` or `functor.Main2`): 10 | 11 | > scala -cp Functor.jar functor.Main1 12 | 13 | Another way to run examples is to use build tools like: sbt, gradle or maven 14 | 15 | ## Helpful resources 16 | * [Functor in Cats](https://typelevel.org/cats/typeclasses/functor.html) 17 | * [Functor in Scalaz](http://eed3si9n.com/learning-scalaz/Functor.html) 18 | * [Cats Functor exercises](https://www.scala-exercises.org/cats/functor) 19 | * [sbt website](https://www.scala-sbt.org/) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2022 How.Programming.Works contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /JavaScript/8-path.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fp = {}; 4 | 5 | fp.path = (data) => ( 6 | (path) => ( 7 | fp.maybe(path)((path) => ( 8 | path.split('.').reduce( 9 | (prev, key) => (prev[key] || {}), 10 | (data || {}) 11 | ) 12 | )) 13 | ) 14 | ); 15 | 16 | fp.maybe = (x) => (fn) => fp.maybe(x && fn ? fn(x) : null); 17 | 18 | // Usage 19 | 20 | const fs = require('node:fs'); 21 | 22 | const config = { 23 | server: { 24 | host: { 25 | ip: '10.0.0.1', 26 | port: 3000 27 | }, 28 | ssl: { 29 | key: { 30 | filename: './7-path.js' 31 | } 32 | } 33 | } 34 | }; 35 | 36 | // Imperative style 37 | 38 | if ( 39 | config && 40 | config.server && 41 | config.server.ssl && 42 | config.server.ssl.key && 43 | config.server.ssl.key.filename 44 | ) { 45 | const fileName = config.server.ssl.key.filename; 46 | fs.readFile(fileName, 'utf8', (err, data) => { 47 | if (data) console.log(); 48 | }); 49 | } 50 | 51 | // Functional style 52 | 53 | fp.path(config)('server.ssl.key.filename')( 54 | (file) => fs.readFile(file, 'utf8', (err, data) => { 55 | fp.maybe(data)(console.log); 56 | }) 57 | ); 58 | -------------------------------------------------------------------------------- /JavaScript/a-collector.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function Collector() {} 4 | 5 | const collect = (expected) => { 6 | const collector = (key, value) => { 7 | if (collector.finished) return collector; 8 | collector.count++; 9 | collector.data[key] = value; 10 | if (value instanceof Error) { 11 | collector.callback(value, collector.data); 12 | return collector; 13 | } 14 | if (collector.expected === collector.count) { 15 | collector.finished = true; 16 | collector.callback(null, collector.data); 17 | } 18 | return collector; 19 | }; 20 | 21 | const fields = { 22 | count: 0, 23 | expected, 24 | data: {}, 25 | callback: null, 26 | finished: false 27 | }; 28 | 29 | Object.setPrototypeOf(collector, Collector.prototype); 30 | return Object.assign(collector, fields); 31 | }; 32 | 33 | Collector.prototype.done = function(callback) { 34 | this.callback = callback; 35 | return this; 36 | }; 37 | 38 | // Usage 39 | 40 | const dc = collect(4).done((err, data) => { 41 | console.log('Done callback '); 42 | console.dir({ err, data }); 43 | }); 44 | 45 | dc('key1', 'value1'); 46 | 47 | setTimeout(() => { 48 | dc('key2', 'value2'); 49 | }, 100); 50 | 51 | setImmediate(() => { 52 | dc('key3', 'value3'); 53 | }); 54 | 55 | dc('key4', 'value4'); 56 | dc('key5', 'value5'); 57 | 58 | // { 59 | // key1: 'value1', 60 | // key4: 'value4', 61 | // key5: 'value5', 62 | // key3: 'value3' 63 | // } 64 | -------------------------------------------------------------------------------- /Scala/Advanced/1-functor.scala: -------------------------------------------------------------------------------- 1 | package functor 2 | 3 | import scala.language.higherKinds 4 | 5 | //Functor typeclass with existential F type 6 | trait Functor[F[_]] { 7 | def fmap[A, B](fa: F[A])(f: A => B): F[B] 8 | } 9 | 10 | object Functor { 11 | //Summons functor instance if it is in scope 12 | def apply[F[_]](implicit f: Functor[F]): Functor[F] = f 13 | } 14 | 15 | //Option typeclass that represents optional value. Value type is covariant 16 | sealed trait Option[+A] 17 | //Option child that holds value. Value type is covariant 18 | case class Some[+A](get: A) extends Option[A] 19 | //Option child that represents nothing 20 | case object None extends Option[Nothing] 21 | 22 | object Option { 23 | //Some factory method 24 | def some[T](value: T): Option[T] = Some(value) 25 | 26 | //None factory method 27 | def none[T]: Option[T] = None 28 | 29 | /* 30 | * Remember this factory methods and that 31 | * their return type is Option and not Some or None, 32 | * they will come in handy in second example ;) 33 | */ 34 | 35 | //Functor implementation for Option 36 | implicit val optionFunctor: Functor[Option] = new Functor[Option] { 37 | override def fmap[A, B](fa: Option[A])(f: A => B): Option[B] = fa match { 38 | case Some(v) => Some(f(v)) 39 | case None => None 40 | } 41 | } 42 | } 43 | 44 | object Main1 extends App { 45 | println(Functor[Option].fmap(None)((a: Int) => a + 2)) 46 | //prints None 47 | println(Functor[Option].fmap(Some("2"))(_.toInt)) 48 | //prints Some(2) 49 | } 50 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "node": true 6 | }, 7 | "extends": "eslint:recommended", 8 | "parserOptions": { 9 | "ecmaVersion": "latest" 10 | }, 11 | "globals": { 12 | "BigInt": true 13 | }, 14 | "rules": { 15 | "indent": [ 16 | "error", 17 | 2 18 | ], 19 | "linebreak-style": [ 20 | "error", 21 | "unix" 22 | ], 23 | "quotes": [ 24 | "error", 25 | "single" 26 | ], 27 | "semi": [ 28 | "error", 29 | "always" 30 | ], 31 | "no-loop-func": [ 32 | "error" 33 | ], 34 | "block-spacing": [ 35 | "error", 36 | "always" 37 | ], 38 | "camelcase": [ 39 | "error" 40 | ], 41 | "eqeqeq": [ 42 | "error", 43 | "always" 44 | ], 45 | "strict": [ 46 | "error", 47 | "global" 48 | ], 49 | "brace-style": [ 50 | "error", 51 | "1tbs", 52 | { 53 | "allowSingleLine": true 54 | } 55 | ], 56 | "comma-style": [ 57 | "error", 58 | "last" 59 | ], 60 | "comma-spacing": [ 61 | "error", 62 | { 63 | "before": false, 64 | "after": true 65 | } 66 | ], 67 | "eol-last": [ 68 | "error" 69 | ], 70 | "func-call-spacing": [ 71 | "error", 72 | "never" 73 | ], 74 | "key-spacing": [ 75 | "error", 76 | { 77 | "beforeColon": false, 78 | "afterColon": true, 79 | "mode": "minimum" 80 | } 81 | ], 82 | "keyword-spacing": [ 83 | "error", 84 | { 85 | "before": true, 86 | "after": true, 87 | "overrides": { 88 | "function": { 89 | "after": false 90 | } 91 | } 92 | } 93 | ], 94 | "max-len": [ 95 | "error", 96 | { 97 | "code": 80, 98 | "ignoreUrls": true 99 | } 100 | ], 101 | "max-nested-callbacks": [ 102 | "error", 103 | { 104 | "max": 7 105 | } 106 | ], 107 | "new-cap": [ 108 | "error", 109 | { 110 | "newIsCap": true, 111 | "capIsNew": false, 112 | "properties": true 113 | } 114 | ], 115 | "new-parens": [ 116 | "error" 117 | ], 118 | "no-lonely-if": [ 119 | "error" 120 | ], 121 | "no-trailing-spaces": [ 122 | "error" 123 | ], 124 | "no-unneeded-ternary": [ 125 | "error" 126 | ], 127 | "no-whitespace-before-property": [ 128 | "error" 129 | ], 130 | "object-curly-spacing": [ 131 | "error", 132 | "always" 133 | ], 134 | "operator-assignment": [ 135 | "error", 136 | "always" 137 | ], 138 | "operator-linebreak": [ 139 | "error", 140 | "after" 141 | ], 142 | "semi-spacing": [ 143 | "error", 144 | { 145 | "before": false, 146 | "after": true 147 | } 148 | ], 149 | "space-before-blocks": [ 150 | "error", 151 | "always" 152 | ], 153 | "space-before-function-paren": [ 154 | "error", 155 | { 156 | "anonymous": "never", 157 | "named": "never", 158 | "asyncArrow": "always" 159 | } 160 | ], 161 | "space-in-parens": [ 162 | "error", 163 | "never" 164 | ], 165 | "space-infix-ops": [ 166 | "error" 167 | ], 168 | "space-unary-ops": [ 169 | "error", 170 | { 171 | "words": true, 172 | "nonwords": false, 173 | "overrides": { 174 | "typeof": false 175 | } 176 | } 177 | ], 178 | "no-unreachable": [ 179 | "error" 180 | ], 181 | "no-global-assign": [ 182 | "error" 183 | ], 184 | "no-self-compare": [ 185 | "error" 186 | ], 187 | "no-unmodified-loop-condition": [ 188 | "error" 189 | ], 190 | "no-constant-condition": [ 191 | "error", 192 | { 193 | "checkLoops": false 194 | } 195 | ], 196 | "no-console": [ 197 | "off" 198 | ], 199 | "no-useless-concat": [ 200 | "error" 201 | ], 202 | "no-useless-escape": [ 203 | "error" 204 | ], 205 | "no-shadow-restricted-names": [ 206 | "error" 207 | ], 208 | "no-use-before-define": [ 209 | "error", 210 | { 211 | "functions": false 212 | } 213 | ], 214 | "arrow-parens": [ 215 | "error", 216 | "always" 217 | ], 218 | "arrow-body-style": [ 219 | "error", 220 | "as-needed" 221 | ], 222 | "arrow-spacing": [ 223 | "error" 224 | ], 225 | "no-confusing-arrow": [ 226 | "error", 227 | { 228 | "allowParens": true 229 | } 230 | ], 231 | "no-useless-computed-key": [ 232 | "error" 233 | ], 234 | "no-useless-rename": [ 235 | "error" 236 | ], 237 | "no-var": [ 238 | "error" 239 | ], 240 | "object-shorthand": [ 241 | "error", 242 | "always" 243 | ], 244 | "prefer-arrow-callback": [ 245 | "error" 246 | ], 247 | "prefer-const": [ 248 | "error" 249 | ], 250 | "prefer-numeric-literals": [ 251 | "error" 252 | ], 253 | "prefer-rest-params": [ 254 | "error" 255 | ], 256 | "prefer-spread": [ 257 | "error" 258 | ], 259 | "rest-spread-spacing": [ 260 | "error", 261 | "never" 262 | ], 263 | "template-curly-spacing": [ 264 | "error", 265 | "never" 266 | ], 267 | "consistent-return": [ 268 | "error", 269 | { "treatUndefinedAsUnspecified": true } 270 | ] 271 | } 272 | } 273 | --------------------------------------------------------------------------------