尝试使用TypeScript让我的脚湿透,我一直跑进trouble。一个old function resurfaced today,就像练习一样,我很好奇是否可以将它转换为TypeScript。到目前为止,这是一个彻头彻尾的痛苦。
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
所以问题是具体的(参见粗体)
const arrayCompare = (f: (_: Ord) => (_: Ord) => boolean) => ...
f
期待类型
Ord => Ord => boolean
但是如果我使用这种类型的签名
// danger !! unnamed parameters
(f: (Ord) => (Ord) => boolean)
TypeScript会将Ord
假设为参数的名称,隐含类型为any
// what TypeScript thinks it means
(f: (Ord: any) => (Ord: any) => boolean)
当然这不是我想要的,但这就是我得到的东西。为了获得我真正想要的东西,我必须为高阶函数
指定参数的名称// now it's correct
(f: (_: Ord) => (_: Ord) => boolean)
但是c' mon没有任何意义。我只能在此上下文中访问f
,而不是f
在我最终调用它时将绑定的参数...
问题
为什么我必须在TypeScript中为高阶函数参数提供名称?
这没有任何意义,使得函数签名变得冗长,丑陋,难以编写,更难以阅读。
更新
"就参数名称而言,考虑一个回调函数的函数 - > (数字 - >数字 - >数字) - >,所以完全根据您的选项类型:加,减,乘,除,幂,比较只有一个有意义,现在如果回调参数有一个name add :( number - > number - > number)选择很明显" - Aleksey Bykov
我很高兴有机会回复此事。我可以使用(number -> number -> number)
签名来命名更多功能。
first
,second
,mod
,min
,max
&
,|
,xor
,<<
和>>
(x, y) => sqrt(sq(x) + sq(y))
(x, y) => x + x + y + y + superglobalwhocares
为了清理问题,我并未建议不应给出函数参数本身的名称。我建议不应该为函数参数参数指定名称......
// this
func = (f: (number => number => number)) => ...
// not this
func = (f: (foo: number) => (bar: number) => number)) => ...
为什么呢?因为f
不知道我将提供的函数的参数。
// 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 ...)
如果我尝试的话,我无法在f
中提供func
个参数的名称!因为谁知道我将使用哪种功能?所有这些都是合适的。
如果我试图在其上加上名字,我就会将用户想象的功能归结为......
// maybe the user thinks only a division function can be specified (?)
func = (f: (dividend: number) => (divisor: number) => number) => ...
dividend
和divisor
不适合这里,因为上面列出的任何功能都适合。 最佳我可以这样做
// provide generic name for f's parameters
func = (f: (x: number) => (y: number) => number) => ...
但那又有什么意义呢?它不像x
和y
成为绑定标识符。并且x
和y
没有提供任何添加的说明 - 我想这会将我带到我的点:他们并非意味着拥有名称或描述。 f
对我们使用它的方式有零知识,但这并不重要;只要它有(number => number => number)
界面,我们关心的是所有。这是我们可以向func
用户提供有关f
参数的大多数有用信息。
&#34;对于像
这样的功能来说会很混乱foo(cb: (number, number) => (number, string) => boolean)
它做了什么?&#34; - unional
同样的推理适用于此。除了(cb: (number, number) => (number, string) => boolean))
是一个设计不佳的函数(你可以命名多少有用的混合型四元(4-arity)函数?)之外,它并不重要。 f
无法假装知道任何关于使用此类签名可以提出的无数函数的描述符。
所以我的问题是,为什么我必须为函数参数参数指定明显无意义的名称?
运动
可以用有意义的名字替换_
吗?
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签名中?
答案 0 :(得分:1)
至少以可读的方式编写currying定义很难 我会尽可能地在函数声明之外提取签名,如下所示:
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]) => {
...
}
我还删除了declare
类型别名之前的Ord
,没有必要。你可以找到更好的名字
另一件事是你不需要在这里指定boolean
:
const eq = (x: Ord) => (y: Ord) : boolean => x === y;
可以:
const eq = (x: Ord) => (y: Ord) => x === y;
或者您可以使用单个type
声明来表达该函数。所有事情都考虑了可读性。
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) => {
...
};
答案 1 :(得分:0)
回到你的问题,你必须提供名称,因为TS语法要求你这样做,理性的部分是当一个类型单独没有这样做时,参数的名称传达了额外的意义
答案 2 :(得分:0)
当您指定(f: (Ord) => (Ord) => boolean)
时,所有TypeScript都会看到您正在使用一个名为Ord
的参数指定函数。您尚未指定类型。
为了支持这种语法,编译器(和语言服务)需要自己引入名称。
考虑使用代码的时间:
它提供的语法与在TypeScript中定义函数的方式相同。即(name: Type) => ...
。如果没有引入名称,那么对用户来说会非常混乱。
另一方面,如果参数带有任何特定含义,IMO值得提供参数名称,以便用户知道该怎么做。
对于像以下这样的功能来说会很混乱:
foo(cb: (number, number) => (number, string) => boolean)
它做了什么?
答案 3 :(得分:-1)
所以我的问题是,为什么我必须为函数参数参数指定明显无意义的名称?
我不认为它们毫无意义。我可以想到至少为什么命名参数有意义的三个好理由:
<强>一致性强>
这是在TypeScript中定义属性类型的方法:
class Person {
public firstName: string;
public lastName: string;
public age: number;
}
这是指定变量类型的方法:
let person: Person;
参数类型:
function meet(who: Person) {
}
函数和方法返回类型:
function isUnderage(person: Person): boolean {
return person.age < 18;
}
这是函数类型参数在没有参数名称的情况下的外观:
let myFunc: (string, string, number) => boolean;
...或...
function myFuncWithCallback(callback: (string, string, number) => boolean): void {}
...或...
type CallbackType = (string, string, number) => boolean;
let myFunc: CallbackType;
function myFuncWithCallback(callback: CallbackType): void {}
这与上面的其他声明不太相符。
在整个TypeScript中,只要您使用类型来表示目标的静态类型,就可以使用target: type
。这很容易记住。如果您开始制定以下规则:在定义函数类型的参数时,在除之外的所有情况下使用语法target: type
,这会使您的语言不那么一致,因此更难学习和使用。技术上可能没有必要,但一致性本身就是一种价值。 JavaScript充满了怪癖,TypeScript继承了很多怪癖。最好不要引入任何额外的不一致。
所以这不是一种罕见的模式,这是有充分理由的。
this
参数
如果不在函数类型中指定参数名称,指定this
parameters in callback types将变得更加混乱和不一致。从链接页面考虑这个例子:
interface UIElement {
addClickListener(onclick: (this: void, e: Event) => void): void;
}
你会想要这样:
interface UIElement {
addClickListener(onclick: (this: void, Event) => void): void;
}
那么规则就是“在任何地方使用语法target: type
,除了函数类型,它只是参数中的类型,除非那里是this
参数,然后它实际上是该语法,但只在this
参数中。“我并不责怪TypeScript设计师更倾向于使用规则“在任何地方使用语法target: type
。”
发展辅助工具
这是我将鼠标悬停在TypeScript中的函数时得到的结果:
您希望如何阅读func: (number, Element) => any
而不是它具有的描述性参数名称?类型信息本身就没用了。从此定义自动生成的任何代码都必须为参数提供无意义的名称,如param1
和param2
。这显然不是最佳的。
TypeScript不是命名函数类型参数的唯一语言:
C#委托的定义如下:
delegate void EventHandler(object sender, EventArgs e);
Delphi函数变量:
type TFunc = function(x: Integer, y: Integer): Integer;