Do I have to specify parameter names for higher-order function types in TypeScript?
试着用TypeScript弄湿我的脚,我一直遇到麻烦。今天重新出现了一个旧函数,就像一个练习一样,我很好奇是否可以将它转换为TypeScript。到目前为止,颈部完全疼痛。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | declare type Ord = number | string; // type signature for f sucks really bad // (f: Ord => Ord => boolean) would be really nice, if possible // but instead I have to give names (_) for the parameters? dumb const arrayCompare = (f: (_: Ord) => (_: Ord) => boolean) => ([x,...xs]: Ord[]) => ([y,...ys]: Ord[]): boolean => { if (x === undefined && y === undefined) return true; else if (! f (x) (y)) return false; else return arrayCompare (f) (xs) (ys); } // here the names of the parameters are actually used const eq = (x: Ord) => (y: Ord) : boolean => x === y; // well at least it works, I guess ... console.log(arrayCompare (eq) ([1,2,3]) ([1,2,3])); // true console.log(arrayCompare (eq) (['a','b','c']) (['a','b','c'])); // true |
所以问题是具体的(见粗体)
1 | const arrayCompare = (f: (_: Ord) => (_: Ord) => boolean) => ... |
1 | Ord => Ord => boolean |
但如果我使用这种类型的签名
1 2 | // danger !! unnamed parameters (f: (Ord) => (Ord) => boolean) |
TypeScript将假定
1 2 | // what TypeScript thinks it means (f: (Ord: any) => (Ord: any) => boolean) |
当然这不是我想要的,但这就是我得到的。为了得到我真正想要的东西,我必须为高阶函数指定参数的名称
1 2 | // now it's correct (f: (_: Ord) => (_: Ord) => boolean) |
但是来吧没有意义。我只能在这个上下文中访问
题
为什么我必须在TypeScript中为高阶函数参数提供名称?
这没有任何意义,使得函数签名变得冗长,丑陋,难以编写,更难以阅读。
UPDATE
"as far as names for parameters, consider a function that takes a callback of -> (number -> number -> number) ->, so based solely on the types your options are: add, subtract, multiply, divide, power, compare of which only one makes sense, now if a callback parameter had a name add: (number -> number -> number) the choice would be obvious" – Aleksey Bykov
我很高兴有机会回复此事。我可以用
-
first ,second ,mod ,min ,max -
按位函数
& ,| ,xor ,<< 和>> -
(x, y) => sqrt(sq(x) + sq(y)) -
(x, y) => x + x + y + y + superglobalwhocares - 以及你可以梦想的任何其他功能
为了清理,我不建议函数参数本身不应该给出一个名称。我建议函数参数的参数不应该给出名字......
1 2 3 4 5 | // this func = (f: (number => number => number)) => ... // not this func = (f: (foo: number) => (bar: number) => number)) => ... |
为什么?因为
1 2 3 4 5 6 7 8 9 10 11 12 | // for the record, i would never name parameters like this // but for those that like to be descriptive, there's nothing wrong with these const add = (addend: number) => (augend: number) => number ... const sub = (minuend: number) => (subtrahend: number) => number ... const divide = (dividend: number) => (divisor: number) => number ... const mult = (multiplicand: number) => (multiplier: number) => number ... // I could use any of these with my func func (add ...) func (sub ...) func (divide ...) func (mult ...) |
如果我尝试的话,我无法在
如果我试着在它们上面加上名字,我就会把用户想象的功能归结为......
1 2 | // maybe the user thinks only a division function can be specified (?) func = (f: (dividend: number) => (divisor: number) => number) => ... |
1 2 | // provide generic name for f's parameters func = (f: (x: number) => (y: number) => number) => ... |
但那有什么意义呢?它不像
"It would be quite confusing for function like:
1 foo(cb: (number, number) => (number, string) => boolean)What does it do?" - unional
同样的推理适用于此。除了
所以我的问题是,为什么我必须为函数参数参数指定明显无意义的名称?
行使
你能用有意义的名字取代
1 2 3 4 5 6 7 8 9 10 11 | const apply2 = (f: (_: number) => (_: number) => number) => (x: number) => (y: number): number => { return f (x) (y) }; const sqrt = (x: number): number => Math.sqrt(x); const sq = (x: number): number => x * x; const add = (addend: number) => (augend: number): number => addend + augend; const pythag = (side1: number) => (side2: number): number => sqrt(add(sq(side1)) (sq(side2))); console.log(apply2 (add) (3) (4)); // 7 console.log(apply2 (pythag) (3) (4)); // => 5 |
如果没有,你能否提出一个令人信服的论据,为什么这些名字必须出现在你的TypeScript签名中?
至少以可读的方式编写currying定义很难。
我想做的是尽可能多地提取函数声明之外的签名,如下所示:
1 2 3 4 5 6 7 8 | type Ord = string | number; type ThirdFunction = (objs: Ord[]) => boolean; type SecondFunction = (objs: Ord[]) => ThirdFunction; type FirstFunction = (fn: (o: Ord) => (o: Ord) => boolean) => SecondFunction; const arrayCompare: FirstFunction = f => ([x,...xs]) => ([y,...ys]) => { ... } |
(游乐场代码)
我还删除了
另一件事是你不需要在这里指定
1 | const eq = (x: Ord) => (y: Ord) : boolean => x === y; |
可:
1 | const eq = (x: Ord) => (y: Ord) => x === y; |
或者您可以使用单个
1 2 3 4 5 6 7 8 9 10 | type Ord = number | string; type arrayCompareFunc = (f: (x: Ord) => (y: Ord) => boolean) => (xs: Ord[]) => (ys: Ord[]) => boolean; const arrayCompare: arrayCompareFunc = f => ([x,...xs) => ([y,...ys) => { ... }; |
当您指定
编辑:我可以看到它是当前TypeScript的一个限制。请求在此处提交:https://github.com/Microsoft/TypeScript/issues/14173
为了支持这种语法,编译器(和语言服务)需要自己引入名称。
考虑使用代码的时间:
它提供的语法与在TypeScript中定义函数的方式相同。即
另一方面,如果参数具有任何特定含义,IMO值得提供参数名称,以便用户知道该怎么做。
对于以下功能来说会很困惑:
它有什么作用?
回到你的问题,你必须提供名称,因为TS语法要求你这样做,理性的部分是当一个类型单独没有这样做时,参数的名称传达了额外的意义
So my question is, why the heck do I have to specify overtly meaningless names for function parameter parameters ?
我认为它们毫无意义。我可以想到为什么命名参数有意义的至少三个很好的理由:
一致性
这是在TypeScript中定义属性类型的方法:
1 2 3 4 5 | class Person { public firstName: string; public lastName: string; public age: number; } |
这是您指定变量类型的方式:
1 | let person: Person; |
参数类型:
1 2 | function meet(who: Person) { } |
函数和方法返回类型:
1 2 3 | function isUnderage(person: Person): boolean { return person.age < 18; } |
这是函数类型参数在没有参数名称的情况下的外观:
1 | let myFunc: (string, string, number) => boolean; |
...要么...
1 | function myFuncWithCallback(callback: (string, string, number) => boolean): void {} |
...要么...
1 2 3 | type CallbackType = (string, string, number) => boolean; let myFunc: CallbackType; function myFuncWithCallback(callback: CallbackType): void {} |
这与上面的其他声明不太一致。
在整个TypeScript中,只要您使用类型来表示目标的静态类型,就可以使用
所以这不是一种罕见的模式,这是有充分理由的。
如果不在函数类型中指定参数名称,则在回调类型中指定
1 2 3 | interface UIElement { addClickListener(onclick: (this: void, e: Event) => void): void; } |
你会想要这样:
1 2 3 | interface UIElement { addClickListener(onclick: (this: void, Event) => void): void; } |
那么规则就是"在任何地方使用语法
开发辅助工具
当我将鼠标悬停在TypeScript中的函数上时,这就是我得到的:
您希望它如何读取
TypeScript不是命名函数类型参数的唯一语言:
C#委托定义如下:
1 | delegate void EventHandler(object sender, EventArgs e); |
Delphi函数变量:
1 | type TFunc = function(x: Integer, y: Integer): Integer; |