TypeScirpt中文教程之函数上—-翻译自ts官方(Typescirpt Chinese tutorial function – translated from TS official)

在ts中表达一个函数等方法有多种:

函数类型表达式

function greeter(fn: (a: string) => void) {  fn(“Hello World”);}

function printToConsole(s: string) {

  console.log(s);}

greeter(printToConsole);

这种类似于箭头函数的表示是最简单的。其中加粗部分表示一个以字符串类型的a为入参,并且没有任何返回值的函数。

另一种写法:使用类的别名去定义一个函数类:

type GreetFunction = (a: string) => void;  // 定义一个参数为字符串并且没有返回值的函数类

function greeter(fn: GreetFunction) { … } 

  调用签名

在JavaScript中,函数除了可以调用外,还可以具有属性。但是,函数类型表达式语法不允许声明属性。如果我们想用属性描述可调用的东西,我们可以在对象类型中编写调用签名:

type DescribableFunction = {  description : string,

  (someArg: number): boolean;};

function doSomething(fn: DescribableFunction) {  console.log(fn.description + “returned” + fn(3));}

函数的签名和函数类型表达式有稍微的不同:在参数列表和返回值之间使用冒号: ,而上面的函数类型表达式使用箭头 => 。

结构签名

js中函数也可以使用 new 来创建一个实例。ts中你也可以编写一个结构签名并在new后面使用。

type SomeConstructor = {  new (s: string): SomeObject;};function fn(ctor: SomeConstructor) {  return new ctor(‘Hello’);}

比如js中的Date对象,在调用时可以使用new也可以忽略new,你可以将条用签名和结构签名随意结合。

例如:

interface CallOrContruct {  new (s: string): Date;  (n?: number): number;}

泛型函数

通常一个函数的入参和返回值是有关联的,或者两个参数间有着某种关联。让我们试想一下一个函数返回数组中的第一个元素:

function firstElement(arr: any[]) {  return arr[0];}

这个函数虽然可以正常工作,但是返回值是一个 any 类型。最理想的是返回数组元素的类型。

在TypeScript中,当我们想要描述两个值之间的对应关系时,使用泛型。为此,我们在函数签名中声明一个类型参数:

function firstElement<Type>(arr: Type[]): Type | undefined {  return arr[0];}

通过添加一个Type的参数,并在参数和返回值两个位置上使用,这样我们就创建了一个输入值和返回值有关联的函数,尝试调用他们:const s = firstElement([‘a’, ‘b’, ‘c’]);  // s 是 string 类型const n = firstElement([1, 2, 3, 4]);  // n 是 number 类型

上面这个例子,使用了单一的类型参数 Type。实际应用中我们定义多个类型参数,例如:

function map<Input, Output>(arr: Input[], func: (arg: Input) => Output): Output[] {  return arr.map(func);}const parsed = map([‘1’, ‘2’, ‘3’, ‘4’], (n) => parseInt(n));

约束

我们已经编写了一些泛型函数,可以处理任何类型的值。有时,我们希望将两个值关联起来,但只能对值的某个子集进行操作。在这种情况下,我们可以使用约束来限制类型参数可以接受的类型。

 让我们编写一个返回两个值中较长值的函数。为此,我们需要一个number类型的长度属性。我们通过编写extends子句将类型参数约束到该类型:

function longest<Type extends {length: number}>(a: Type, b: Type) {  if (a.length >= b.length) {    return a;  } else {    return b;  }}

const longestArray = longest([1, 2], [3, 4, 5]);const longestString = longest(‘Bowennan’, ‘JamesBond’);

const notOk = longest(10, 300)  //

由于数字10,300都没有 length 这个属性,所以报错了。

  指定参数类型

TypeScript通常可以推断泛型调用中的预期类型参数,但也有意外情况:

function combine<Type>(arr1: Type[], arr2: Type[]): Type[] {  return arr1.concat(arr2);}

当你这样调用时就会报错:const arr = combine([1, 2, 1], [‘hello’]);  // 第一个参数已经决定了类型为 number,所以当你传入第二个 string 参数时就报错了

我们可以在调用时指定泛型的类型:

const arr = combine<string | number>([1, 2, 1], [‘hello’]);

如何编写好的泛型函数呢?

 1. 遵从类型推导,减少不必要的定义

function firstElement1<Type>(arr: Type[]) {  return arr[0]}function firstElement2<Type extends any[]>(arr: Type) {  return arr[0];} // a: numberconst a = firstElement1([1,2,23])

// b: anyconst b = firstElement2([1,1,23])

第一个函数返回值指向了Type,第二个返回值需要遵从一个没有必要的约束。如果可能,使用类型参数本身要好于约束。

2. 使用尽量少的类型参数

function filter1<Type>(arr: Type[], func: (arg: Type) => boolean): Type[] {  return arr.filter(func);}function filter2<Type, Func extends (arg: Type) => boolean>(arr: Type[], func: Func): Type[] {  return arr.filter(func);}

第二个函数中我们创建了一个单一关联的Func类型参数,指定了参数类型,只是关联到一个参数 func。这种做法只会提升代码的阅读难度,并没有任何优势,尽可能减少类型参数。

3. 至少出现两次,否则没有必要使用泛型

function greet<Str extends string>(s: Str) {  console.log(‘Hello’ + s);}

只有一个参数,没有必要使用类型参数。

function greet(s: string) {…}

————————

There are many ways to express a function in TS:

Function type expression

function greeter(fn: (a: string) => void) {  fn(“Hello World”);}

function printToConsole(s: string) {

console. log(s);}

greeter(printToConsole);

This arrow like representation is the simplest. The bold part represents a function with a of string type as the input parameter and no return value.

Another way to write: use the alias of the class to define a function class:

type GreetFunction = (a: string) => void; / / define a function class with a string parameter and no return value

function greeter(fn: GreetFunction) { … } 

Call signature

In JavaScript, functions can have properties in addition to being called. However, the function type expression syntax does not allow properties to be declared. If we want to describe callable things with attributes, we can write the call signature in the object type:

type DescribableFunction = {  description : string,

  (someArg: number): boolean;};

function doSomething(fn: DescribableFunction) {  console.log(fn.description + “returned” + fn(3));}

The signature of the function is slightly different from the function type expression: a colon is used between the parameter list and the return value:, while the function type expression above uses the arrow = & gt.

Structure signature

JS functions can also use new to create an instance. In TS, you can also write a structure signature and use it after new.

type SomeConstructor = {  new (s: string): SomeObject;};function fn(ctor: SomeConstructor) {  return new ctor(‘Hello’);}

For example, for the date object in JS, you can use new or ignore new when calling. You can combine bar signature and structure signature at will.

For example:

interface CallOrContruct {  new (s: string): Date;  (n?: number): number;}

Generic Functions

Usually, input parameter of the a function is associated with the return value, or there is some association between two parameters. Let’s imagine a function that returns the first element in the array:

function firstElement(arr: any[]) {  return arr[0];}

Although this function works normally, the return value is of type any. Ideally, return the type of array element.

In typescript, we use generics when we want to describe the correspondence between two values. To do this, we declare a type parameter in the function signature:

function firstElement<Type>(arr: Type[]): Type | undefined {  return arr[0];}

By adding a type parameter and using it in both the parameter and the return value, we create a function associated with the input value and the return value, and try to call them: const s = firstelement ([‘a ‘,’ B ‘,’ C ‘]); / / S is a string type const n = firstelement ([1, 2, 3, 4]); / / N is type number

The above example uses a single type parameter type. In practical application, we define multiple type parameters, such as:

function map<Input, Output>(arr: Input[], func: (arg: Input) => Output): Output[] {  return arr.map(func);}const parsed = map([‘1’, ‘2’, ‘3’, ‘4’], (n) => parseInt(n));

constraint

We have written some generic functions that can handle any type of value. Sometimes we want to associate two values, but we can only operate on a subset of values. In this case, we can use constraints to limit the types that type parameters can accept.

Let’s write a function that returns the longer of the two values. To do this, we need a length attribute of type number. We constrain type parameters to this type by writing the extends clause:

function longest<Type extends {length: number}>(a: Type, b: Type) {  if (a.length >= b.length) {    return a;  } else {    return b;  }}

const longestArray = longest([1, 2], [3, 4, 5]);const longestString = longest(‘Bowennan’, ‘JamesBond’);

const notOk = longest(10, 300)  //

Since the number 10300 has no length attribute, an error is reported.

Specify parameter type

Typescript can usually infer expected type parameters in generic calls, but there are exceptions:

function combine<Type>(arr1: Type[], arr2: Type[]): Type[] {  return arr1.concat(arr2);}

When you call it like this, an error will be reported: const arr = combine ([1, 2, 1], [‘Hello ‘]); / / the first parameter has determined that the type is number, so an error will be reported when you pass in the second string parameter

We can specify the type of the generic when calling:

const arr = combine<string | number>([1, 2, 1], [‘hello’]);

How to write a good generic function?

1. Follow type derivation to reduce unnecessary definitions

function firstElement1<Type>(arr: Type[]) {  return arr[0]}function firstElement2<Type extends any[]>(arr: Type) {  return arr[0];} // a: numberconst a = firstElement1([1,2,23])

// b: anyconst b = firstElement2([1,1,23])

The return value of the first function points to type, and the second return value needs to comply with an unnecessary constraint. If possible, using type parameters themselves is better than constraints.

2. Use as few type parameters as possible

function filter1<Type>(arr: Type[], func: (arg: Type) => boolean): Type[] {  return arr.filter(func);}function filter2<Type, Func extends (arg: Type) => boolean>(arr: Type[], func: Func): Type[] {  return arr.filter(func);}

In the second function, we create a single associated func type parameter, specify the parameter type, and only associate it with a parameter func. This approach will only make the code more difficult to read, and has no advantage in reducing type parameters as much as possible.

3. At least twice, otherwise there is no need to use generics

function greet<Str extends string>(s: Str) {  console.log(‘Hello’ + s);}

There is only one parameter, and it is not necessary to use type parameters.

function greet(s: string) {…}