我是否必须在 TypeScript 中为高阶函数类型指定参数名称?
Do I have to specify parameter names for higher-order function types in TypeScript?
尝试使用 TypeScript 来弄湿我的脚,我将 运行 保留为 . An 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: <b>(_: Ord) => (_: Ord) => boolean</b>) => ...
f
期望类型为
的高阶函数
Ord => Ord => boolean
但是如果我使用这种类型签名
// danger !! unnamed parameters
(f: (Ord) => (Ord) => boolean)
TypeScript 将假定 Ord
作为参数的 name,隐含类型为 any
// what TypeScript thinks it means
(f: (Ord: any) => (Ord: any) => boolean)
当然这不是我想要的,但这就是我得到的。为了得到我真正想要的,我必须指定高阶函数的参数名称
// now it's correct
(f: (_: Ord) => (_: Ord) => boolean)
但是拜托,这没有任何意义。在这种情况下,我只能访问 f
,而不能访问 f
在我最终调用它时将绑定的参数...
问题
为什么我必须为 TypeScript 中的高阶函数参数提供 names?
它没有任何意义,并且使函数签名又长又丑,更难编写,更难阅读。
更新
"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" –
我很高兴有机会回复这个问题。我可以用 (number -> number -> number)
签名命名更多函数。
first
、second
、mod
、min
、max
- 按位函数
&
、|
、xor
、<<
和 >>
(x, y) => sqrt(sq(x) + sq(y))
(x, y) => x + x + y + y + superglobalwhocares
- 和任何您可以想到的其他功能
澄清一下,我并不是说不应该为函数参数本身命名。我建议函数参数的 parameters 不应该命名...
// 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 ...)
如果我尝试了,我无法在 func
中为 f
的参数提供名称!因为谁知道我会使用哪个功能?都合适。
如果我试图给它们起名字,我就把用户的想象力归为一类......
// 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
没有提供额外的描述——我想这让我想到了 我的 点:它们不是 meant 有名称或描述。 f
对我们可能使用它的方式有 零 的了解,但这并不重要;只要它有一个 (number => number => number)
接口,那就是我们关心的 all。这是我们可以向 func
用户提供的关于 f
参数的 最 有用的信息。
"It would be quite confusing for function like:
foo(cb: (number, number) => (number, string) => boolean)
What does it do?" -
同样的推理也适用于此。除了 (cb: (number, number) => (number, string) => boolean))
是一个设计不佳的函数这一事实(你能说出多少有用的混合类型四元(4 元)函数?), 无关紧要. f
不能假装知道关于我能想出的无数函数的任何描述符。
所以我的问题是,为什么我必须为函数参数参数指定公开无意义的名称?
运动
你能用有意义的名字替换 _
吗?
const apply2 = (f: (<b>_</b>: number) => (<b>_</b>: 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 签名中?
- 这是一种在 JS 中使用函数的非常不切实际的方式
- TS 在函数引用上的泛型很糟糕,所以很多来自 Haskell 的直觉在这里不起作用
回到你的问题,你必须提供名称,因为 TS 语法要求你这样做,合理的部分是当类型本身不能这样做时,参数的名称传达额外的含义
当您指定 (f: (Ord) => (Ord) => boolean)
时,TypeScript 只会看到您指定的函数带有一个名为 Ord
的参数。您还没有指定类型。
编辑:我可以看出这是当前 TypeScript 的一个限制。在此处提交请求:https://github.com/Microsoft/TypeScript/issues/14173
为了支持这种语法,编译器(和语言服务)需要自己引入名称。
考虑使用代码的时间:
它提供的语法与在 TypeScript 中定义函数的语法相同。即 (name: Type) => ...
。如果不引入名称,用户会很困惑。
另一方面,如果参数带有任何特定含义,IMO 值得提供参数名称,以便用户知道该怎么做。
像这样的函数会很混乱:
foo(cb: (number, number) => (number, string) => boolean)
它有什么作用?
很难编写柯里化定义,至少以一种可读的方式。
我会做的是尽可能多地提取函数声明之外的签名,像这样:
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]) => {
...
}
我还删除了您在 Ord
类型别名之前的 declare
,不需要它。您可以为这些类型找到更好的名称。
另一件事是您不需要在此处指定 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) => {
...
};
So my question is, why the heck do I have to specify overtly meaningless names for function parameter parameters ?
我不认为它们毫无意义。我可以想到 至少 命名参数有意义的三个很好的理由:
一致性
这就是您在 TypeScript 中定义 属性 类型的方式:
class Person {
public firstName: string;
public lastName: string;
public age: number;
}
这是指定变量类型的方式:
let person: Person;
参数类型:
function meet(who: Person) {
}
函数和方法return类型:
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;
}
那么规则就是 "Use the syntax target: type
everywhere, except function types, where it is just the type in the parameters, unless there is a this
parameter, then it actually is that syntax, but only in the this
parameter." 我不怪 TypeScript 设计者宁愿遵循规则 "Use the syntax target: type
everywhere."
开发辅助
这是我将鼠标悬停在 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;
尝试使用 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: <b>(_: Ord) => (_: Ord) => boolean</b>) => ...
f
期望类型为
Ord => Ord => boolean
但是如果我使用这种类型签名
// danger !! unnamed parameters
(f: (Ord) => (Ord) => boolean)
TypeScript 将假定 Ord
作为参数的 name,隐含类型为 any
// what TypeScript thinks it means
(f: (Ord: any) => (Ord: any) => boolean)
当然这不是我想要的,但这就是我得到的。为了得到我真正想要的,我必须指定高阶函数的参数名称
// now it's correct
(f: (_: Ord) => (_: Ord) => boolean)
但是拜托,这没有任何意义。在这种情况下,我只能访问 f
,而不能访问 f
在我最终调用它时将绑定的参数...
问题
为什么我必须为 TypeScript 中的高阶函数参数提供 names?
它没有任何意义,并且使函数签名又长又丑,更难编写,更难阅读。
更新
"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" –
我很高兴有机会回复这个问题。我可以用 (number -> number -> number)
签名命名更多函数。
first
、second
、mod
、min
、max
- 按位函数
&
、|
、xor
、<<
和>>
(x, y) => sqrt(sq(x) + sq(y))
(x, y) => x + x + y + y + superglobalwhocares
- 和任何您可以想到的其他功能
澄清一下,我并不是说不应该为函数参数本身命名。我建议函数参数的 parameters 不应该命名...
// 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 ...)
如果我尝试了,我无法在 func
中为 f
的参数提供名称!因为谁知道我会使用哪个功能?都合适。
如果我试图给它们起名字,我就把用户的想象力归为一类......
// 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
没有提供额外的描述——我想这让我想到了 我的 点:它们不是 meant 有名称或描述。 f
对我们可能使用它的方式有 零 的了解,但这并不重要;只要它有一个 (number => number => number)
接口,那就是我们关心的 all。这是我们可以向 func
用户提供的关于 f
参数的 最 有用的信息。
"It would be quite confusing for function like:
foo(cb: (number, number) => (number, string) => boolean)
What does it do?" -
同样的推理也适用于此。除了 (cb: (number, number) => (number, string) => boolean))
是一个设计不佳的函数这一事实(你能说出多少有用的混合类型四元(4 元)函数?), 无关紧要. f
不能假装知道关于我能想出的无数函数的任何描述符。
所以我的问题是,为什么我必须为函数参数参数指定公开无意义的名称?
运动
你能用有意义的名字替换 _
吗?
const apply2 = (f: (<b>_</b>: number) => (<b>_</b>: 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 签名中?
- 这是一种在 JS 中使用函数的非常不切实际的方式
- TS 在函数引用上的泛型很糟糕,所以很多来自 Haskell 的直觉在这里不起作用
回到你的问题,你必须提供名称,因为 TS 语法要求你这样做,合理的部分是当类型本身不能这样做时,参数的名称传达额外的含义
当您指定 (f: (Ord) => (Ord) => boolean)
时,TypeScript 只会看到您指定的函数带有一个名为 Ord
的参数。您还没有指定类型。
编辑:我可以看出这是当前 TypeScript 的一个限制。在此处提交请求:https://github.com/Microsoft/TypeScript/issues/14173
为了支持这种语法,编译器(和语言服务)需要自己引入名称。
考虑使用代码的时间:
它提供的语法与在 TypeScript 中定义函数的语法相同。即 (name: Type) => ...
。如果不引入名称,用户会很困惑。
另一方面,如果参数带有任何特定含义,IMO 值得提供参数名称,以便用户知道该怎么做。
像这样的函数会很混乱:
foo(cb: (number, number) => (number, string) => boolean)
它有什么作用?
很难编写柯里化定义,至少以一种可读的方式。
我会做的是尽可能多地提取函数声明之外的签名,像这样:
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]) => {
...
}
我还删除了您在 Ord
类型别名之前的 declare
,不需要它。您可以为这些类型找到更好的名称。
另一件事是您不需要在此处指定 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) => {
...
};
So my question is, why the heck do I have to specify overtly meaningless names for function parameter parameters ?
我不认为它们毫无意义。我可以想到 至少 命名参数有意义的三个很好的理由:
一致性
这就是您在 TypeScript 中定义 属性 类型的方式:
class Person {
public firstName: string;
public lastName: string;
public age: number;
}
这是指定变量类型的方式:
let person: Person;
参数类型:
function meet(who: Person) {
}
函数和方法return类型:
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;
}
那么规则就是 "Use the syntax target: type
everywhere, except function types, where it is just the type in the parameters, unless there is a this
parameter, then it actually is that syntax, but only in the this
parameter." 我不怪 TypeScript 设计者宁愿遵循规则 "Use the syntax target: type
everywhere."
开发辅助
这是我将鼠标悬停在 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;