Day19 :【TypeScript 学起来】More on Functions

我们在前面 Day09 , 有简单讨论到 function,这篇就会来看一些更深入 function 的应用。


使用 function 关键字

最简单定义 function 的用法:

function greeter(fn: (a: string) => void) {
  fn("Hello, World");
}
 
function printToConsole(s: string) {
  console.log(s);
}
 
greeter(printToConsole); //Hello, World

(a: string) => void 表示这个function带入的一个命名为 a 的参数,这个参数的型别为字串, 且此 funtion 没有 return 值。

特别注意一定要有参数命名,如(a: string)。如果这样没命名, 这样写 (string) => void 就会表示这个参数命名为string, 型别是 any

此外, type 及 interface 也能定义 function , 可参考 Day15 这篇


Call Signatures,给 function 新增属性

其实这个我一开始真的看不懂,感谢google大神让我找到实现官网例子的方法,大概了解了,下面也来实作一下。

在 JavaScript 中, function 是可以增加属性的(其实我不知道欸,好像没有试过对 function 增加属性)。於是想来证明是否真的可以,写了小例子,还真的可以增加属性:

const jsFunc = () => {
    return "this's a function";
}

jsFunc.prop = "this's a prop";
jsFunc.desc = "this's a desc";
console.log(jsFunc);  //[Function: func] { prop: "this's a prop", desc: "this's a desc" }

但在 TypeScript 中 type expression 是不允许对 function 新增属性的,於是我们可以在 object type 写一个 call signatures, 如下 DescribableFunction。特别注意跟一般 function 不同的是返回型别是使用:, 不是使用 =>

//新增 call signatures
type DescribableFunction = {  
  description: string;
  (someArg: number): boolean;  //这边特别注意我们使用:非 =>
};

function doSomething(fn: DescribableFunction) {
  console.log(fn.description + " returned " + fn(6));
}

//回传boolean 如果大於5回传true
const func = (someArg: number): boolean => { 
  return someArg > 5;  
};

//新增属性
func.description = "isNumber > 5";

doSomething(func); //isNumber > 5 returned true


Construct Signatures

一般我们使用 new 来建立新 object, 就称为 constructors 。

type SomeConstructor = {
  new (s: string): SomeObject;
};
function fn(ctor: SomeConstructor) {
  return new ctor("hello");
}

Optional Parameters 可选参数

虽然在 JavaScript 中没有被带入的函式参数会是 undefined 且可以正常运作,但在 TypeScript 中,函式的参数预设都是必填的,没填的话 compiler 会直接喷错。

跟之前在介绍 object 型别可选属性一样,参数也可以选填,在参数後面使用?

// firstName 是必填,lastName 则可以选填
const getName = (firstName: string, lastName?: string) => {
    return  lastName ? `${firstName} ${lastName}` : firstName;
}

⚠️ 特别注意的是optional parameter 一定只能放在 required parameter 的後面。

⚠️ 官网也有特别提醒在 callback 的时候也尽量不要使用 optional parameter, 可能会发生这种错误


default-initialized parameters 使用参数预设值

const getName = (firstName: string, lastName = "Chen") => {
    return `${firstName} ${lastName}`;
}
  
console.log(getName("Tom")); //Tom Chen

如果要使用预设值,在呼叫该函式的时候要使用undefined


const getName = (firstName = "Tom", lastName: string) => {
    return `${firstName} ${lastName}`;
}

console.log(getName(undefined, "Chen")); //Tom Chen


Rest Parameters

想新增无限组参数,可使用剩余参数关键字 ...

function multiply(n: number, ...m: number[]) {
  return m.map((x) => n * x);
}

const a = multiply(10, 1, 2, 3, 4);

console.log(a); //[10, 20, 30, 40]
const getName = (firstName: string, ...rest: string[]) => {
    return `${firstName} ${rest.join(' ')}`;
} 
const names = getName('Tom', 'Jerry', 'Chen');
console.log(names); 

Rest Arguments

虽然可以用 push 的方式新增剩余参数是没问题:

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
arr1.push(...arr2);

console.log(arr1); // [1, 2, 3, 4, 5, 6]

但在 TypeScript , 但在一些使用上会有问题:
args 会被 inferred 型别为 number[], 不是特定两个数字。

// Inferred type is number[] -- "an array with zero or more numbers",
// not specifically two numbers
const args = [8, 5];
// error
const angle = Math.atan2(...args);  

Math.atan2 是什麽 可参考这里

我们可以把阵列直接当成 Tuple 使用,可以在定义阵列是加上 as const,如此它就是会是一个值固定的 readonly array,这样就没问题了。如果没加上 as const 的话,则会是number[]

// Inferred as 2-length tuple
const args = [8, 5] as const;
// ok
const angle = Math.atan2(...args);

下一篇来笔记 Function Overloads 函式超载 ,第一次知道有这种功能, 觉得很cool。


参考

https://zhuanlan.zhihu.com/p/266823134
https://stackoverflow.com/questions/66874130/how-to-properly-use-functions-construct-signatures-in-typescript
https://basarat.gitbook.io/typescript/type-system/type-inference
https://pjchender.dev/typescript/ts-functions/#%E9%81%B8%E5%A1%AB%E6%80%A7%E7%9A%84%E5%8F%83%E6%95%B8%E8%88%87%E5%8F%83%E6%95%B8%E9%A0%90%E8%A8%AD%E5%80%BC
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2
https://pjchender.dev/typescript/ts-basic-types/


<<:  [ Raspberry Pi ] Compute module 4 eMMC 烧录流程 ((CM4))

>>:  IT铁人DAY 19-Flyweight 享元模式

Day29-用jQuery写得出ToDoList吗_4_单机版ToDoList没有问题!

这篇来说剩下的重要功能 先来写删除的部分 就叫做removeTodo吧 加在a连结上,一样需要回传t...

JavaScript Day25 - Promise(2)

Promise Promise.all():等待里面的都完成才回传,因为是全部完成,所以会是阵列,失...

Day 16 : PHP - 如何在phpMyAdmin里用SQL的语法做资料库的查询?

如标题,这篇想和大家聊聊如何用SQL的语法做资料库的查询 请注意,SQL的函数皆为「全大写的英文字母...

Day6 - 读 Concurrency is not Parallelism - Rob Pike (一)

本篇是看 Concurrency is not Parallelism 的心得 Concurrenc...

Alpine Linux Porting (1.999) The light at the end of tunnel

一样先上进度log: + exec /bin/busybox switch_root /sysroo...