
{"id":5259,"date":"2025-05-18T17:18:49","date_gmt":"2025-05-18T21:18:49","guid":{"rendered":"https:\/\/ikriv.com\/blog\/?p=5259"},"modified":"2025-05-18T17:19:16","modified_gmt":"2025-05-18T21:19:16","slug":"typescript-you-can-pass-a-class-method-as-lambda-but-it-will-blow-up","status":"publish","type":"post","link":"https:\/\/ikriv.com\/blog\/?p=5259","title":{"rendered":"TypeScript: you can pass class method as lambda, but it will blow up"},"content":{"rendered":"<p>TypeScript allows passing class methods to functions that expect a lambda, but it won&#8217;t work at runtime. Consider this code:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nfunction applyTwice(f: (x: number) =&gt; number, x: number): number {\r\n    return f(f(x));\r\n}\r\n<\/pre>\n<p>Suppose we have class Calculator like this:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nclass Calculator {\r\n    private factor: number;\r\n\r\n    constructor(factor: number) {\r\n        this.factor = factor;\r\n    }\r\n\r\n    multiply(x: number): number {\r\n        return x * this.factor;\r\n    }\r\n}\r\n<\/pre>\n<p>We can then do:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nconst calculator = new Calculator(3);\r\nconst result = applyTwice(calculator.multiply, 2);\r\nconsole.log(result);\r\n<\/pre>\n<p>It will compile, but it will blow up at runtime with <code>Cannot read properties of undefined (reading 'factor')<\/code>.<br \/>\nWhen <code>calculator.multiply<\/code> is passed in this manner, the magic <code>this<\/code> parameter becomes <code>undefined<\/code, which causes the runtime error. The compiler, however, will not say a word.\n\nThe right way to handle this situation is to explicitly bind <code>this<\/code> to <code>calculator<\/code>, as follows:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nconst calculator = new Calculator(3);\r\nconst result = applyTwice(calculator.multiply.bind(calculator), 2); \/\/ returns 2*3*3 = 18\r\nconsole.log(result);\r\n<\/pre>\n<p>This works as expected, but looks kind of redundant.<\/p>\n<p>TypeScript behaves a little bit like C++ in this situation, where class method also needs to be explicitly bound to a variable:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\n#include &lt;iostream&gt;\r\n#include &lt;functional&gt;\r\n\r\nclass Calculator {\r\npublic:\r\n    explicit Calculator(int factor) : factor_(factor) {}\r\n\r\n    int multiply(int x) const {\r\n        return x * factor_;\r\n    }\r\n\r\nprivate:\r\n    int factor_;\r\n};\r\n\r\n\/\/ applyTwice applies a function f to x twice: f(f(x))\r\nint applyTwice(const std::function&lt;int(int)&gt;&amp; f, int x) {\r\n    return f(f(x));\r\n}\r\n\r\nint main() {\r\n    Calculator calculator(3);\r\n\r\n    auto boundMultiply = &#x5B;&amp;calculator](int x) {\r\n        return calculator.multiply(x);\r\n    };\r\n\r\n    int result = applyTwice(boundMultiply, 2); \/\/ 3 * (3 * 2) = 18\r\n    std::cout &lt;&lt; result &lt;&lt; std::endl;\r\n\r\n    return 0;\r\n}\r\n<\/pre>\n<p>Unlike TypeScript, if we try to simply pass <code>calculator.multiply<\/code> to <code>applyTwice<\/code>, it will generate an error. Exact error message depends on the compiler, but the main point is it won&#8217;t compile.<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\n  \/\/ GCC: invalid use of non-static member function &#039;int Calculator::multiply(int) const&#039;\r\n  int result = applyTwice(calculator.multiply, 2); \r\n<\/pre>\n<p>Python, Java, and C# automatically bind class methods to the variable, so the code works as expected out of the box.<\/p>\n<p>Python:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nclass Calculator:\r\n    def __init__(self, factor):\r\n        self.factor = factor\r\n\r\n    def multiply(self, x):\r\n        return x * self.factor\r\n\r\ndef apply_twice(f, x):\r\n    return f(f(x))\r\n\r\ncalculator = Calculator(3)\r\nresult = apply_twice(calculator.multiply, 2)  # 3 * (3 * 2) = 18\r\nprint(result)\r\n<\/pre>\n<p>Java:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nimport java.util.function.IntUnaryOperator;\r\n\r\npublic class Main {\r\n\r\n    \/\/ Method that applies a function twice\r\n    public static int applyTwice(IntUnaryOperator f, int x) {\r\n        return f.applyAsInt(f.applyAsInt(x));\r\n    }\r\n\r\n    static class Calculator {\r\n        private final int factor;\r\n\r\n        public Calculator(int factor) {\r\n            this.factor = factor;\r\n        }\r\n\r\n        public int multiply(int x) {\r\n            return x * factor;\r\n        }\r\n    }\r\n\r\n    public static void main(String&#x5B;] args) {\r\n        Calculator calculator = new Calculator(3);\r\n        int result = applyTwice(calculator::multiply, 2); \/\/ 3 * (3 * 2) = 18\r\n        System.out.println(result);\r\n    }\r\n}\r\n<\/pre>\n<p>C#:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\nusing System;\r\n\r\nclass Program\r\n{\r\n    static int ApplyTwice(Func&lt;int, int&gt; f, int x)\r\n    {\r\n        return f(f(x));\r\n    }\r\n\r\n    class Calculator\r\n    {\r\n        private int factor;\r\n\r\n        public Calculator(int factor)\r\n        {\r\n            this.factor = factor;\r\n        }\r\n\r\n        public int Multiply(int x)\r\n        {\r\n            return x * factor;\r\n        }\r\n    }\r\n\r\n    static void Main()\r\n    {\r\n        var calculator = new Calculator(3);\r\n        int result = ApplyTwice(calculator.Multiply, 2); \/\/ 3 * (3 * 2) = 18\r\n        Console.WriteLine(result);\r\n    }\r\n}\r\n<\/pre>\n<p>TypeScript behaves worse than most popular languages, because it neither binds the method automatically (Java, C#, Python), nor enforces the binding via the compiler (C++).<\/p>\n<p>Thanks to ChatGPT for creating this example and quickly translating it into 5 different languages.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>TypeScript allows passing class methods to functions that expect a lambda, but it won&#8217;t work at runtime. Consider this code: function applyTwice(f: (x: number) =&gt; number, x: number): number { <a href=\"https:\/\/ikriv.com\/blog\/?p=5259\" class=\"more-link\">[&hellip;]<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"Layout":"","footnotes":""},"categories":[29],"tags":[30],"class_list":["entry","author-ikriv","post-5259","post","type-post","status-publish","format-standard","category-typescript","tag-typescript"],"_links":{"self":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/5259","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=5259"}],"version-history":[{"count":5,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/5259\/revisions"}],"predecessor-version":[{"id":5264,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/5259\/revisions\/5264"}],"wp:attachment":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=5259"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=5259"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=5259"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}