TypeScript(五)函数

TypeScript 定义函数的四种方式

第一种方式可以直接调用,后三种需要先实现定义的函数再调用。

第一种 函数声明式:

function sum(x: number, y: number): number {
  return x + y
}
// 调用时形参和实参一一对应
sum(1, 2)

第二种 函数表达式:

let sum: (x: number, y: number) => number = (a, b) => a + b
// 或
let sumX = (a: number, b: number): number => a + b
sum(2, 2)
sumX(1, 4)

第三种 接口实现:

interface ISum {
  (x: number, y: number): number
}
// 跟变量声明是等价的:let ISum: (a: number, b: number) => number
let sum: ISum = (a, b) => a + b
sum(4, 2)

第四种 类型别名:(推荐方式)

type ISum = (x: number, y: number) => number
// 应用如下:
let sum: ISum = (a, b) => a + b
sum(3, 2)

函数类型

函数类型包含两部分:参数类型和返回值类型。

  1. 参数名不一定要相同,只要参数类型匹配,那么就认为它是有效的函数类型。
const sum: (num1: number, num2: number) => number = (x, y) => x + y
  1. 返回值类型是函数类型的必要部分,如果函数没有返回任何值,则返回值类型为 void。
const sum: (num1: number, num2: number) => void = (x, y) => {
  console.log(x + y)
}

类型推断

如果在赋值语句的一边指定了类型但另一边没有类型的话,TypeScript 编译器会自动识别出类型, 这叫做“按上下文归类”,是类型推论的一种。

const getSum: (x: number, y: number) => number = (x, y) => x + y

可选参数

JavaScript 里,每个参数都是可选的。没传参的时候,值就是 undefined。但在 TypeScript 中函数参数默认都是必传的,必传的意思并不是不能传递 null 和 undefined 作为实参,而是编译器会检查是否为每个参数传入了值。简而言之,编译器会检查传入实参的个数是否和形参相同。

function bar(name: string, age: number): string {
  return `${name} age is ${age}`
}

bar('jack', 12) // ok
bar('nike') // Expected 2 arguments, but got 1.
bar('rose', 12, 'shanghai') // Expected 2 arguments, but got 3.

TypeScript 的可选参数需要在参数名后使用 ? 标识符 实现可选参数的功能。 比如上例希望 age 是可选的:

function bar(name: string, age?: number): string {
  if (age) return `${name} age is ${age}`
  return `the name is ${name}`
}

bar('jack', 12) // ok
bar('nike') // ok
bar('rose', 12, 'shanghai') // Expected 1-2 arguments, but got 3.

注意: 可选参数必须在必选参数后面,且后面不能再有必选参数

参数默认值

可以通过为参数提供一个默认值,当参数是可选的且没有传值或传递的值是 undefined 时,则会使用参数的默认值。

function fullName(firstName: string, lastName: string = 'Smith') {
  return `${firstName}  ${lastName}`
}

fullName('Bob') // Bob  Smith
fullName('Bob', undefined) // Bob  Smith
fullName('Bob', 'Adams', 'Sr.') // Expected 1-2 arguments, but got 3.
fullName('Bob', 'Adams') // Bob  Adams

参数默认值与可选参数不同之处:

  1. 没有传值时默认参数是取默认值,而可选参数的值是 undefined
  2. 带默认值的参数不需要放在必选参数的后面。如果带默认值的参数出现在必选参数前面,则调用时必须明确的传入 undefined 值来取得默认值

剩余参数

TypeScript 的剩余参数和 ES6 的剩余参数一样。

interface ITotal {
  (pre: number, cur: number): number
}

function sum(num1: number, ...rest: number[]): number {
  const handle: ITotal = (pre, cur) => pre + cur

  return rest.reduce(handle, num1)
}

sum(1, 2, 3, 4, 5, 6, 7) // 28

this

this 与箭头函数

TypeScript 在 noImplicitThis 模式下,不允许 this 上下文隐式定义。

const person = {
  name: 'Mike',
  getName() {
    return function () {
      console.log(this.name)
    }
  }
}
const getName = person.getName()
getName()

上例函数中的 this 在 noImplicitThis 模式开启时报错(this' implicitly has type 'any' because it does not have a type annotation),未开启时指向 window。

可以将返回函数设置成箭头函数解决该问题。

const person = {
  name: 'Mike',
  getName() {
    return () => {
      console.log(this.name)
    }
  }
}
const getName = person.getName()
getName() // 'MIke'

但上面的代码还是会存在一些问题。因为即使能够保证箭头函数里面的 this 与外层函数的 this 保持一致, 但是外层函数的 this 不一定就是 person 这个对象。函数中的 this 依旧是 any 类型。

this 参数

在 JavaScript 中,this 不能用做变量或参数名,所以 TypeScript 使用语法空间来让你在函数体中声明 this 的类型。this 类型声明必须放在参数的首位:

interface IPerson {
  name: string
  getInfo(this: IPerson, age: number): string
}

const info: IPerson = {
  name: 'jack',
  getInfo(age) {
    return this.name + age
  }
}

info.getInfo(23) // ✅
const info1: IPerson = {
  name: 'rose',
  getInfo(age) {
    return this.name + age
  }
}
info1.getInfo(34) // ✅

const obj = {
  name: 'mike'
}

info.getInfo.call(obj, 45) // ❌
// obj 不是IPerson的类型

重载

函数重载允许一个函数通过不同数量或类型的参数,返回不同类型的值。

比如:实现一个函数 reverse,输入数字的时候,输出反转的数字,输入字符串的时候,输出反转的字符串。

通过联合类型实现:

function reverse(val: number | string): number | string {
  if (typeof val === 'number') {
    return Number(val.toString().split('').reverse().join(''))
  } else if (typeof val === 'string') {
    return val.split('').reverse().join('')
  }
}

联合类型的缺陷是不能精确表达不同的输入类型所对应的输出类型。 可以通过函数重载定义多个函数类型。

函数重载实现:

function reverse(num: number): number
function reverse(str: string): string
function reverse(val: any): number | string | boolean {
  if (typeof val === 'number') {
    return Number(val.toString().split('').reverse().join(''))
  } else if (typeof val === 'string') {
    return val.split('').reverse().join('')
  }
  return false
}

console.log(reverse(123456)) // 654321
console.log(reverse('sina')) // 'anis'

以上前两个函数是函数重载列表,第三个是函数实体。

注意:重载只能通过 function 声明。

类型谓词(is)

在 TypeScript 中,函数还支持另外一种特殊的类型描述,如下示例 :

function isString(s): s is string { // 类型谓词
  return typeof s === 'string';
}
function isNumber(n: number) {
  return typeof n === 'number';
}
function operator(x: unknown) {
  if(isString(x)) { // ok x 类型缩小为 string
  }
  if (isNumber(x)) { // ts(2345) unknown 不能赋值给 number
  }
}

在上述代码中,在添加返回值类型的地方,通过“参数名 + is + 类型”的格式明确表明了参数的类型,进而引起类型缩小,所以类型谓词函数的一个重要的应用场景是实现自定义类型守卫。

类型谓词只能用来定义自定义类型守卫,实际上是告诉引擎,当守卫条件成立的情况下(返回 true),将被守卫的类型缩小到 is 指定的更明确的类型。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,012评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,628评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,653评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,485评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,574评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,590评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,596评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,340评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,794评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,102评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,276评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,940评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,583评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,201评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,441评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,173评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,136评论 2 352

推荐阅读更多精彩内容