typescript中函数_在Typescript中键入函数的更好方法

typescript中函数

Let’s start from the bottom line: there’s a simple alternative to typing Typescript functions, that’s more concise and logical than the Typescript’s “native” method. It makes use of a utility type, which I’ll call simply Function, that is easily defined:

让我们从底线开始:键入Typescript函数有一个简单的替代方法,它比Typescript的“本机”方法更为简洁和逻辑。 它利用一种实用程序类型,我将其简称为Function,该类型很容易定义:

type Function<A = any[], B = any> = (...args: A extends any[] ? A : [A]) => B;

For example:

例如:

function minimumOf(fn: Function<number, number>, interval: [number, number]) {
	// Approximate/solve...
}


// Works!
const m1 = minimumOf(Math.sqrt, [1, 3]);


// Argument of type '(x: number, y: number) => number' is not assignable to parameter of type 'Functiona<[number], number>'.
const m2 = minimumOf(Math.pow, [1, 3]);

Feel free to save it, and use it wherever you find it valuable!

随时保存它,并在有价值的地方使用它!

There’s no hack behind this definition — it simply uses some of Typescript’s advanced features effectively:

此定义背后没有任何技巧-它仅有效地使用Typescript的一些高级功能:

  • At the heart of it all lies the types A and B — those are Generics, and they’re a popular (advanced) concept in object-oriented programming in many languages, Typescript included. To put it simply, A and B are wildcards, which adapt themselves to the given context (or to whatever you explicitly tell them to, if you do). They can be used to provide better enforcement in more complex or abstract cases, as well as a better programming experience, and are widespread in Typescript APIs and libraries. For example, for the fn parameter in the example above, A and B both become number types.

    它们的核心都在于类型A和B-它们是泛型 ,它们是许多语言(包括Typescript)在面向对象编程中的流行(高级)概念。 简而言之,A和B是通配符,它​​们使自己适应于给定的上下文(或适应于您明确告诉它们的任何操作)。 它们可以用于在更复杂或抽象的情况下提供更好的实施,以及更好的编程体验,并且广泛用于Typescript API和库中。 例如,对于上面示例中的fn参数,A和B都成为数字类型。

  • We make use of Typescript’s ability to infer tuple types as sets of function parameters. For example, if A gets the type [number, boolean], the compiler will interpret that as a function that receives two parameters — the first is a number, and the second is a boolean.

    我们利用Typescript的功能将元组类型推断为函数参数集。 例如,如果A的类型为[number, boolean] ,则编译器会将其解释为接收两个参数的函数-第一个是数字,第二个是布尔值。

  • To provide a true generalization of Typescript’s native Function type, which receives any set of parameters (any[]) and can return anything (any), we set default values to the generics A and B. That means that if A and B are not explicitly set, and Typescript cannot infer them from the context, A will be set to any[] (i.e. you can pass any number of parameters of any type to the function), and B will be set to any (i.e. the function might return anything) — just like the native Function type. Of course, if you don’t want this type to shadow the native Function type, you can give it a different name, such as Func.

    为了提供Typescript的本机Function类型的真实概括,该类型可以接收任何参数集( any[] )并可以返回任何值( any ),我们将默认值设置为泛型A和B。这意味着如果A和B不是显式设置,并且Typescript无法从上下文推断它们,A将被设置为any[] (即您可以将任意数量的任何类型的参数传递给函数),而B将被设置为any (即函数可能返回一切)-就像本机Function类型一样。 当然,如果您不希望此类型隐藏本机Function类型,则可以给它一个不同的名称,例如Func

  • For convenience, in the case of a function receiving a single parameter, we use conditional types to automatically wrap the parameter into a single-element tuple. As stated above, the Typescript compiler then knows to interpret that as a function that receives a single parameter, of the given type. Thus a function that receives a number and outputs a number can be typed as Function<number, number> instead of Function<[number], number>. Note: if you intend such a function to accept a single argument of type A, where A is an array or a tuple, you must wrap A in square brackets, e.g. Function<[A], number>. Otherwise, the compiler will interpret the type such that the function needs to receive multiple arguments, whose type is the type of the elements of A.

    为了方便起见,在函数接收单个参数的情况下,我们使用条件类型将参数自动包装到单个元素元组中。 如上所述,Typescript编译器随后知道将其解释为接收给定类型的单个参数的函数。 因此,可以将接收数字并输出数字的Function<number, number>键入为Function<number, number>而不是Function<[number], number> 。 注意:如果要让这样的函数接受类型A的单个参数( 其中A是数组或元组) ,则必须将A括在方括号中,例如Function<[A], number> 。 否则,编译器将解释类型,以使函数需要接收多个参数,其类型为A元素的类型。

Now that the proposition is in place, let’s talk about why:

现在,该提议已经到位,让我们讨论一下原因

动机 (Motivation)

You might be asking: “what’s wrong with Typescript’s built-in function typing capabilities?” , to which I say — not much, to be honest, not at the basic level. It definitely works, but it’s somewhat cumbersome, and requires the programmer to include information that may be redundant.

您可能会问:“ Typescript的内置函数键入功能出了什么问题?” ,我要说的是-老实说,不是基本水平。 它确实可以工作,但是有点麻烦,并且要求程序员包括可能多余的信息。

In case you’re not familiar with the built-in function type notation, it looks something like this:

如果您对内置的函数类型表示法不熟悉,它看起来像这样:

(arg1: Type1, arg2: Type2, /*...*/, argN: TypeN) => ReturnedType

For example, the function minimumOf() defined above can be written with the basic notation like so:

例如,上面定义的函数minimumOf()可以使用如下基本符号来编写:

function minimumOf(fn: (x: number) => number, interval: [number, number]) {
	/*...*/
}

And, truthfully, the basic notation isn’t much longer to use, in this case. But the seemingly unnoticeable label “x” for the parameter gets a lot more prominent in many real-life situations. In fact, this is an inconvenience you’ll run into quickly in a real-life app — you must give this parameter a name, and your dilemma is whether to choose a meaningful, long name that makes the type (and your code as a whole) less readable and often adds redundancy to it — or a short, non-indicative name that makes the label worthless. For functions with multiple parameters, especially those with parameters that serve a similar purpose (and are thus named similarly), this becomes a real problem.

而且,实际上, 在这种情况下 ,基本符号的使用时间不再长。 但是,在许多实际情况下,该参数看似不起眼的标签“ x”变得更加突出。 实际上,这是一个不便之处,在现实生活中,您会很快遇到这个麻烦–您必须为该参数指定一个名称,而您的难题是是否要选择一个有意义的长名称作为该类型的名称(以及您的代码作为整体)的可读性差,通常会增加冗余度-或简短的非指示性名称,使标签一文不值。 对于具有多个参数的功能,尤其是那些具有类似目的(因此具有类似名称)的参数的功能,这成为一个实际的问题。

Imagine, for example, that you’re developing a team task manager app, and you’re currently implementing the simple action of assigning a task to teammate. The function is to receive as params the task and teammate, as well as an “assigner” param, to keep track of who assigned what. Being a good Typescript programmer, you defined types for each parameter (used throughout the application) — with the names Task, Teammate and Assigner respectively — each having their own fields. Now say you need to specify the function’s type explicitly (for example, you’re defining a React component that receives this function as a prop, or generally any interface with this field in it).

例如,假设您正在开发团队任务管理器应用程序,并且当前正在执行将任务分配给队友的简单操作。 功能是接收任务和队友作为参数,以及“分配者”参数,以跟踪谁分配了什么。 作为一名优秀的Typescript程序员,您可以为每个参数(在整个应用程序中使用)定义类型,分别命名为Task,Teammate和Assigner,每个参数都有自己的字段。 现在说您需要显式指定函数的类型(例如,您正在定义一个React组件,该组件以prop的形式接收此函数,或者通常是其中包含此字段的任何接口)。

With Typescript’s base notation, you would write:

使用Typescript的基本符号,您可以编写:

assignTask: (task: Task, teammate: Teammate, assigner: Assigner) => void

but, as you probably noticed, the type is truly long, and is clearly redundant: nothing is gained from the words “task”, “teammate” or “assigner” appearing twice. And in case you’re wondering, those parameter labels impose no constraints on the name of the actual parameter names in a function assignable to that type. That means that the function

但是,您可能已经注意到,这种类型确实很长,并且显然是多余的:两次出现的“任务”,“团队成员”或“分配者”一词什么也得不到。 而且,如果您想知道,这些参数标签对可分配给该类型的函数中的实际参数名称没有任何限制。 这意味着功能

assignTask(aaa: Task, bbb: Teammate, ccc: Assigner): void {
//...
}

is completely assignable to our function, making the parameters labels quite useless.

完全可以分配给我们的功能,从而使参数标签变得毫无用处。

Using the Function<> notation, we can achieve a much cleaner, more readable type:

使用Function<>表示法,我们可以实现更简洁,更易读的类型:

assignTask: Function<[Task, Teammate, Assigner], void>

It is worth noting that in some functions, the parameter labels can serve a meaningful purpose, and be a form of documentation, making clear which parameter is supposed to get what. However, this by no means should be mandatory, and as we’ve seen above can make the code significantly worse. Either way, labeling the parameters is now also possible with the Function<> notation — we’ll get to that later.

值得一提的是,在某些函数中,参数标签可以起到有意义的作用,并且可以作为文档的一种形式,明确说明哪个参数应该得到什么。 但是,这绝不是强制性的,并且正如我们上面所看到的,这会使代码变得更糟。 无论哪种方式,现在都可以使用Function<>标记来标记参数-我们将在后面介绍。

My next point is that functions — as an fundamental entity — are agnostic of a “label” you give their arguments, as I’ll explain next:

我的下一点是,功能(作为基本实体)与您为其提供参数的“标签”无关,正如我将在下面解释的那样:

充当实体 (Functions as an entity)

In mathematics, the fundamental definition of a function is a composition of three parts:

在数学中,函数的基本定义由三部分组成:

  • A domain — the set of all values that the function receives (and them only). Simple examples would be the set of all numbers, or the set of all strings — but any set goes, really.

    -函数接收的所有值的集合(并且只有它们)。 简单的例子是所有数字的集合或所有字符串的集合-但实际上任何集合都可以。

  • A range — a set which contains all the values that the function can return (but not necessarily them only; the set of all real numbers is a valid range for a function that always returns 1).

    范围 -包含函数可以返回的所有值的集合(但不一定仅是它们;所有实数的集合是始终返回1的函数的有效范围)。

  • A graph — a “mapping” of each value in the domain to a value in the range (technically, this is also a set).

    图形 -域中每个值到范围内的值的“映射”(从技术上讲,这也是一个集合)。

Two functions are considered equal if and only if these three are exactly the same in each.

当且仅当这三个函数彼此完全相同时,才认为这两个函数相等。

Often, these sets are implicit. For example, in high school math the notation “𝑓(x) = x² ” is often used to denote the function whose domain and range are both ℝ, the set of all (real) numbers, and which maps each element in the domain (each number x) to its square.

通常,这些集合是隐式的。 例如,在高中数学中,符号“𝑓(x)=x²”通常用于表示其域和范围均为ℝ的函数,即所有(实)数字的集合,并映射该域中的每个元素(每个数字x)到其平方。

Functions in computer science are not exactly the same as in math. For example, a function in a program has some knowledge of its environment and can be affected by the given context, such as a class method using one of the class members, which affects the output of the function. However for the purposes of this discussion, the differences between functions in math and in programming can be set aside.

计算机科学中的功能与数学中的功能并不完全相同 。 例如,程序中的函数具有其环境的某些知识,并且可能会受给定上下文的影响,例如,使用其中一个类成员的类方法会影响函数的输出。 但是,出于讨论目的,可以搁置数学函数和编程函数之间的差异。

If you compare the above definition of a function to our original discussion of TS function types, you’ll find that the two factors of a function that are taken into account in its type are its domain and range. A function “from ℝ to ℝ”, i.e. a functions with domain and range ℝ, is a function that receives a number and outputs a number; there no ambiguity about it, the two are completely equivalent. You could do the same with functions that receive strings, booleans, or any other type; the “conversion” from function in the mathematical perspective to a Typescript function type is that the TS function type denotes the element of a set — number, string, boolean, etc. — and math denotes the set itself (e.g. ℝ, the set of all real numbers).

如果将上述函数定义与我们最初对TS函数类型的讨论进行比较,您会发现在函数类型中考虑到的两个因素是其域和范围。 函数“从ℝ到ℝ”,即具有域range的函数,是接收数字并输出数字的函数; 对此没有任何歧义,两者是完全等同的。 您可以对接收字符串,布尔值或任何其他类型的函数执行相同的操作; 从数学角度上的函数到Typescript函数类型的“转换”是TS函数类型表示集合的元素(数字,字符串,布尔值等),而数学表示集合本身(例如ℝ,所有实数)。

The mathematical definition also accounts for functions that receive multiple variables (parameters) — by treating a combination of variables as a single value (a tuple), and taking the set of all the possible combinations (n-tuples — just like tuples in programming) as the domain. If you’ll look above, you’ll see that the Function<> notation does exactly that — it marks assignTask as a function that receives a “tuple” containing a Task, a Teammate and an Assigner, and Typescript is smart enough to understand that we’re referring to different parameters of the function.

数学定义还考虑了接收多个变量(参数)的函数-通过将变量组合视为一个值(一个元组),并采用所有可能组合的集合(n个元组,就像编程中的元组一样)作为域。 如果您在上面查看,您会发现Function<>表示法确实做到了这一点–它将assignTask标记为一个函数,该函数接收包含任务,队友和分配者的“元组”,并且Typescript很聪明,足以理解我们指的是函数的不同参数。

But functions as an abstract, fundamental entity (following the mathematical definition), are not concerned with the labels you give the elements of the domain or the range; indeed, the “high-school” notation above uses x as a label for a generic element of ℝ (that is, a label for a number), but we could have used “y”, “z”, or any other symbol just the same. This is the same in programming — the compiler doesn’t care what you named your variables or function parameters, it only needs to know what label refers to what argument. Us programmers use the mandatory label for documentation purposes — we give it the argument a name that reflects its purpose, so that the function is more readable — and it is a great and encouraged practice, but it’s not a cardinal part of the “type” (domain & range) of a function, and therefore it doesn’t need to be required when typing a function.

但是充当抽象的基本实体(遵循数学定义)的功能与您为域或范围的元素提供的标签无关; 确实,上面的“高中”表示法使用x作为ℝ的通用元素的标签(即数字的标签),但是我们本可以使用“ y”,“ z”或其他任何符号相同。 这是编程一样的-编译器不关心命名你的变量或函数的参数,只需要知道什么标签指的是什么说法。 美国程序员将强制性标签用于文档目的-我们为参数指定一个名称,以反映其用途,从而使函数更易于阅读-这是一种不错的方法,值得鼓励,但这并不是“类型”的基本组成部分(域和范围),因此在键入函数时不需要。

To summarize this point, the Function<> notation better reflects the type of the function as an entity; parameter labels are sometimes helpful, but they’re not linked to the concept of a function at the fundamental level.

总而言之, Function<>表示法更好地反映了作为实体的函数的类型 ; 参数标签有时会很有帮助,但是它们在基本级别上并未与函数的概念链接。

打字稿4元组标签 (Typescript 4 tuple labels)

As stated before, function parameter labels are often meaningless and redundant, but can occasionally be useful for clarifying which parameter is what, mostly when the parameter types or the function name fail to do so effectively. In this case, you may choose to use the built-in notation in Typescript for typing functions, and that’s totally ok. But, Typescript 4.0 adds a way to label tuple elements, so you can give your parameters labels even with the Function<> notation.

如前所述,函数参数标签通常是无意义的和多余的,但有时在阐明哪个参数是参数时很有用,主要是在参数类型或函数名称未能有效执行时。 在这种情况下,您可以选择使用Typescript中的内置符号来键入函数,这完全可以。 但是, Typescript 4.0添加了一种标记元组元素的方法 ,因此即使使用Function<>表示法,也可以给参数标签。

The minimumof() function, which was written above as

minimumof()函数,上面写为

function minimumOf(fn: Function<number, number>, interval: [number, number]) {
	// Approximate/solve...
}

can also be written

也可以写

function minimumOf(fn: Function<[x: number], number>, interval: [number, number]) {
	// Approximate/solve...
}

If you truly care about including the x in the function type. Note, however, that if you choose to label one parameter — you have to label them all. With that, the main advantage to using Typescript’s built-in function type notation is equally achievable with the Function<> notation, at pretty much the same “cost” of code length.

如果您真正关心在函数类型中包含x。 但是请注意,如果选择标记一个参数,则必须全部标记它们。 这样, 使用Typescript的内置函数类型表示法的主要优点就可以与 Function<> 表示法 相同地实现,而代码长度的“成本”几乎相同。

摘要 (Summary)

As we’ve seen, the Function<> notation is a superior alternative to Typescript’s built-in notation for typing functions; it is defined with a single, quite simple line of code. It “overrides” Typescript’s native Function type, adding generics that provide stronger typing (which, at the end of the day, is what Typescript is all about), thus making up a convenient generalization of the native type.

正如我们所看到的, Function<>表示法是Typescript内置的用于键入函数的表示法的替代方案; 它是用一行非常简单的代码定义的。 它“覆盖”了Typescript的本机Function类型,并添加了提供更强类型的泛型(归根结底,这就是Typescript的目的),从而简化了本机类型的泛化。

As we’ve seen, it also often leads to cleaner code and avoids redundancy, especially in real-life projects, but it also supports additional parameter labels, for cases where it might be preferable.

如我们所见,它通常还可以使代码更简洁并避免冗余,尤其是在实际项目中,但在可能更可取的情况下,它还支持其他参数标签。

Lastly, the Function<> notation echoes the qualities of functions as a fundamental, mathematical concept — it may not be a very practical advantage, but I have a deep appreciation for that.

最后, Function<>表示呼应作为基本的数学概念的功能的品质-这可能不是非常实用的优点,但是我对此深有感激。

The only “drawback”, if you could even call it that, is that the Function<> type is one that you have to define yourself — it’s not a native type that Typescript provides. That being said, it’s only a single, simple line of code to define! For any project spanning over a few files, and definitely for real-life applications, it’s truly negligible.

唯一的“缺点”,即使您甚至可以称呼它为: Function<>类型是您必须定义自己的类型-它不是Typescript提供的本机类型。 话虽如此, 仅需定义一行简单的代码即可! 对于跨越几个文件的任何项目,并且绝对是针对实际应用程序,这确实可以忽略不计。

Let me hear what you think — Your thoughts, feedback and criticism are all welcome.

让我听听您的想法-欢迎您的想法,反馈和批评。

I hope I’ve helped you learn something new!

希望我能帮助您学习新知识!

翻译自: https://levelup.gitconnected.com/the-better-way-to-type-functions-in-typescript-e4ab4fd2171

typescript中函数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值