TypeScript

一.TypeScript介绍

1.TypeScript概念

在这里插入图片描述
(1).TypeScript(简称:TS)是JavaScript的超集(JS有的TS都有)
在这里插入图片描述
(2).TypeScript = Type + JavaScript(在JS基础之上,为JS添加了类型支持)
在这里插入图片描述
(3).TypeScript是微软开发的开源编程语言,可以在任何运行JavaScript的地方运行。

2.TypeScript为什么要为JS添加类型支持?

背景: JS的类型系统存在"先天缺陷", JS代码中绝大部分错误都是类型错误(Uncaught TypeError)。
问题: 增加了找Bug, 改Bug的时间,严重影响开发效率

从编程语言的动静区分, TypeScript属于静态类型的编程语言,JS属于动态类型的编程语言。
静态类型: 编译期做类型检查;
动态类型: 执行期做类型检查。
代码编译和代码执行的顺序: 1.编译 2.执行。

对于JS来说: 需要等到代码真正去执行的时候才能发现错误(晚)。
对于TS来说: 在代码编译的时候(代码执行前)就可以发现错误(早)。
并且,配合VSCode等开发工具,TS可以提前到在编写代码的同时就发现代码中错误,减少找Bug, 改Bug时间。

3.TypeScript相比JS的优势

(1). 更早(写代码的同时)发现错误,减少找Bug, 改Bug时间,提升开发效率。
(2).程序中任何位置的代码都有代码提示,随时随地的安全感,增强了开发体验。
(3).强大的类型系统提升了代码的可维护性,使得重构代码更加容易。
(4).支持最新的ECMAScript语法,优先体验最新的语法, 让你走在前端技术的最前沿。
(5).TS类型推断机制,不需要再代码中每个地方都显示标注类型,让你在享受优势的同时,尽量降低了成本。
(6).Vue3源码使用TS重写,Angular默认支持TS, React与TS完美配合,TypeScript已成为大中型前端项目的首先编程语言。

二.TypeScript初体验

1.安装编译TS的工具包

问题: 为什么要安装编译TS的工具包?
回答:Node.js/浏览器,只认识JS代码,不认识TS代码。需要先将TS代码转换为JS代码,然后才能运行。
安装命令:

npm i -g typescript

其中typescript包就是用来编译TS代码的包,提供了tsc命令,实现了TS -> JS的转化。
验证是否安装成功:

tsc -v

在这里插入图片描述

2.示例(编译并运行ts代码)

在这里插入图片描述
(1).创建hello.ts文件(注意: TS文件的后缀名为.ts)

console.log("hello ts")

(2).将TS便以为JS: 在终端中输入命令,

tsc hello.ts

此时,在同级目录中会出现一个同名的JS文件
(3).执行JS代码: 在终端输入命令,

node hello.js

说明: 所有合法的js代码都是ts代码,有js基础只需要学习ts的类型即可。
注意: 由TS编译生成的JS文件,代码中就没有类型信息了。

3.简化运行TS的步骤

问题描述: 每次修改代码后,都要重复执行两个命令,才能运行TS代码,太繁琐。
简化方式: 使用ts-node包,直接在Node.js中执行TS代码。
安装命令:

npm i -g ts-node

使用方式:

ts-node hello.ts

解释: ts-node命令在内部偷偷的将TS -> JS, 然后, 再运行JS代码。

三.TypeScript常用类型

1.概述

TypeScript是JS的超集,TS提供了JS的所有功能,并且额外的增加了: 类型系统。

  • 所有的JS代码都是TS代码
  • JS有类型(比如, number/string等),但是JS不会检查变量的类型是否发生变化。而TS会检查。
    TypeScript类型系统的主要优势: 可以显示标记出代码中的意外行为,从而降低了发生错误的可能性。
    1. 类型注解
    2. 常用基础类型

2.类型注解

示例

let age: number = 18

说明: 代码中的:number就是类型注解。
作用: 为变量添加类型约束。比如,上述代码中,约定变量age的类型为number(数值类型)。
解释: 约定了什么类型,就只能给变量赋值该类型的值,否则,就会报错。
在这里插入图片描述

3.常用基础类型

(1).概述

可以将TS中的常用基础类型细分为两类:
1. JS已有类型

  • 原始类型: number/string/boolean/null/undefined/symbol
  • 对象类型: object(包括数组,对象,函数等对象)
    2. TS新增类型
  • 联合类型,自定义类型(类型别名),接口,元组,字面量类型,枚举,void, any等。

(2).原始类型

number/string/boolean/null/undefined/symbol
特点: 简单。这些类型,完全按照js中类型的名称来书写
示例:

let age: number = 18
let myName: string = '刘老师'
let isLoading: boolean = false
let a: null = null
let b: undefined = undefined
let s: symbol = Symbol()
//等等...

(3).对象类型

object(包括数组,对象,函数等对象)
特点: 对象类型,在TS中更加细化,每个具体的对象都有自己的类型语法。

  • 数据类型两种写法:(推荐使用number[]写法)
let numbers: number[] = [1, 3, 5]
let strings: Array<String> = ['a', 'b', 'c']

(4).联合类型

需求: 数组中既有number类型,又有string类型,这个数组的类型应该如何写?

let arr:(number|string)[] = [1, 'a', 3, 'b']

解释: | (竖线)在TS中叫做联合类型(由两个或多个其他类型组成的类型,表示可以是这些类型中的任意一种)。
注意: 这是TS中联合类型的语法,只有一根竖线,不要与JS中的或(||)混淆了。

(5).类型别名

为任意类型起别名。
使用场景:当同一类型(复杂)被多次使用时,可以通过类型别名,简化该类型的使用.
示例:

type CustomArray = (number | string)[]
let arr1: CustomArray = [1, 'a', 3, 'b']
let arr2: CustomArray = ['x', 'y', 6, 7]

解释:
1. 使用type关键字来创建类型别名
2. 类型别名(比如,此处的CustomArray),可以是任意合法的变量名称。
3. 创建类型别名后,直接使用该类型别名作为变量的类型注解即可。

(6).函数类型

有返回值

函数的类型实际上指的是: 函数参数和返回值的类型。
为函数指定类型的两种方式:
1.单独指定参数, 返回值的类型:

function add(num1:number, num2:number):number{
  return num1 + num2
}
const add2 = (num1:number, num2:number):number =>{
  return num1 + num2
}

2.同时指定参数,返回值的类型

const add3:(num1:number, num2:number) => number = (num1, num2)=>{
  return num1 + num2
}

解释: 当函数作为表达式时,可以通过类似箭头函数形式的语法来为函数添加类型。
注意: 这种形式只适用于函数表达式。

无返回值

如果函数没有返回值,那么,函数返回值类型为: void

function greet(name: string): void {
  console.log('hello', name)
}
可选参数

使用函数实现某个功能时,参数可以传也可以不传。这种情况下,在给函数参数指定类型时,就用到了可选参数了。
比如: 数组的slice方法,可以slice()也可以slice(1)还可以slice(1, 3)。

function mySlice(start?: number, end?: number): void {
  console.log('起始索引:', start, '结束索引:', end)
}

可选参数: 在可传可不传的参数名称后面添加?(问号)
注意: 可选参数只能出现在参数列表的最后,也就是说可选参数后面不能再出现必选参数。

(7).对象类型

JS中的对象是有属性和方法构成的,而TS中对象类型就是在描述对象的结构(有什么类型的属性和方法)。
对象类型的写法:

let person: { name: string; age: number; sayHi(): void } = {
  name: 'zz',
  age: 18,
  sayHi() {
    console.log('hi ', name)
  }
}

解释:
1.直接使用{}来描述对象结构。属性采用属性名: 类型的形式,方法采用方法名():返回值类型的形式。
2.如果方法有参数,就在方法名后面的小括号中指定参数类型(比如: greet(name:string):void)
3.在一行代码中指定对象的多个属性类型时,使用;(分号)来分割。

  • 如果一行代码只指定一个属性类型(通过换行来分隔多个属性类型),可以去掉;(分号)。
  • 方法的类型也可以使用箭头函数形式(比如: {satHi:() =>void})。
可变属性

对象的属性或方法,也可以是可选的,此时就用到了可选属性了。
比如, 我们在使用axios({ … })时,如果发生GET请求,method属性就可以省略。

function myAxios(config:{url:string, method?:string}):void{
  console.log(config)
}

可选属性的语法与函数可选参数的语法一致,都使用?(问号)来表示。

(8).接口

当一个对象类型被多次使用时,一般会使用接口(interface)来描述对象的类型,达到复用的目的
示例:

interface IPerson {
  name: string
  age: number
  sayHi(): void
}

let person: IPerson = {
  name: 'jack',
  age: 19,
  sayHi() { }
}

解释:
1.使用interface关键字来声明接口
2.接口名称(比如,此处的IPerson),可以是任意合法的变量名称。
3.声明接口后,直接使用接口名称作为变量的类型。
4.因为每一行只有一个属性类型,因此,属性类型后没有;(分号)

interface(接口)和type(类型别名)的对比:
  • 相同点: 都可以给对象指定类型
  • 不同点:
    1. 接口,只能为对象指定类型
    2. 类型别名,不仅可以为对象指定类型,实际上可以为任意类型指定别名。
接口继承

如果两个接口之间有相同的属性或方法,可以将公共的属性或方法抽离出来,通过继承来实现复用。
比如,这两个接口都有x, y两个属性,重复写两次,可以,但很繁琐。

interface Point2D { x: number; y: number }
interface Point3D { x: number; y: number; z: number }

更好的方式:

interface Point2D { x: number; y: number }
interface Point3D extends Point2D { z: number }

解释:
1. 使用extends(继承)关键字实现了接口Point3D继承Point2D
2. 继承后,Point3D就有了Point2D的所有属性和方法(此时,Point3D同时有x, y, z三个属性)。

(9).元组

场景: 在地图中,使用经纬度坐标标记位置信息。
可以使用数组来记录坐标,那么,该数组中只有两个元素,并且这两个元素都是数值类型。

let position: number[] = [39.5427, 116.2317]

使用number[]的缺点: 不严谨,因为该类型的数组中可以出现任意多个数字。
更好的方式: 元组(Tuple)
元组类型是另一种类型的数组,它确切地知道包含多少个元素,以及特定索引对象的类型。

let position: [number, number] = [39.5427, 116.2317]

解释:
1. 元组类型可以确切地标记出有多少个元素,以及每个元素的类型。
*2.*该示例中,元素有两个元素,每个元素的类型都是number.

(10).类型推论

在TS中,某些没有明确指出类型的地方,TS的类型推论机制会帮助提供类型。
换句话说: 由于类型推论的存在,这些地方,类型注解可以省略不写!
1. 声明变量并初始化时

let age = 18

2. 决定函数返回值

function add(num1:number, num2:number){
  return num1 + num2
}

注意: 这两种情况下,类型注解可以省略不写!
推荐: 能省略类型注解的地方就省略(偷懒,充分利用TS类型推论的能力,提升开发效率)。
技巧:如果不知道类型,可以通过鼠标放在变量名称上,利用VSCode的提示来查看类型。

(11).类型断言

有时候你会比TS更加明确一个值的类型,此时,可以使用类型断言来指定更具体的类型。
比如:

<a href="http://baidu.com" id="link">百度</a>
const aLink: HTMLElement
const aLink = document.getElementById('link')

注意: getElementById方法返回值类型是HTMLElement, 该类型只包含所有标签公共的属性或方法,不包含a标签特有的href等属性。
因此,这个类型太宽泛(不具体),无法操作href等a标签特有的属性或方法。
解决方式: 这种情况下就需要使用类型断言指定更加具体的类型。
使用断言:

const aLink = document.getElementById('link') as HTMLAnchorElement

解释:
1. 使用as关键字实现类型断言
*2.*关键字as后面的类型是一个更加具体的类型(HTMLAnchorElement是HTMLElement的子类型)。
*3.*通过类型断言,aLink的类型变得更加具体,这样就可以访问a标签特有的属性或方法了。
另一种语法,使用<>语法,这种语法形式不常用知道即可:

let aLinks = <HTMLAnchorElement>document.getElementById('link')

技巧: 在浏览器控制台,通过console.dir()打印DOM元素,在属性列表的最后面,即可看到该元素的类型。

(12).字面量类型

思考一下代码,两个变量的类型分别是什么?

let str1 = 'Hello TS'
const str2 = 'Hello TS'

通过TS类型推论机制,可以得到答案:
1. 变量str1的类型为: string
2. 变量str2的类型为: ‘Hello TS’
解释:
1. str1是一个变量(let), 它的值可以是任意字符串,所以类型为: string.
2. str2是一个常量(const), 它的值不能变化只能是’Hello TS’, 所以, 它的类型为: ‘Hello TS’
注意:此处的’Hello TS’, 就是一个字面量类型。也就是说某个特定的字符串也可以作为TS中的类型。
除字符串外,任意的JS字面量(比如,对象,数字等)都可以作为类型使用。

const str2:'Hello TS' = 'Hello TS'
let age: 18 = 18

使用模式: 字面量类型配合联合类型一起使用。
使用场景: 用来表示一组明确的可选列表
比如,在贪吃蛇游戏中,游戏的方向的可选值只能是上,下,左,右中的任意一个。

function changeDirection(direction: 'up' | 'down' | 'left' | 'right') {
  console.log(direction)
}

解释: 参数diection的值只能是up/down/left/right中的任意一个。
优势: 相比于string类型,使用字面量类型更加精确,严谨。

(13).枚举

枚举的功能类似于字面量类型+联合类型组合的功能,也可以表示一组明确的可选值。
枚举: 定义一组命名常量。它描述一个值,该值可以是这些命名常量中的一个。

enum Direction {UP, Down, Left, Right}
function changeDirection(direction:Direction){
  console.log(direction)
}

解释:
1.使用enum关键字定义枚举
2.约定枚举名称,枚举中的值以大写字母开头。
3.枚举中的多个值之间通过(逗号)分隔。
4.定义好枚举后,直接使用枚举名称作为类型注解。
注意: 形参direction的类型为枚举Direction, 那么,实参的值就应该是枚举Direction成员的任意一个。

访问枚举成员:
enum Direction {UP, Down, Left, Right}
function changeDirection(direction:Direction){
  console.log(direction)
}
changeDirection(Direction.UP)

解析: 类似于JS中的对象,直接通过点(.)语法访问枚举的成员。

数字枚举

问题: 我们把枚举成员作为了函数的实参,它的值是什么呢?
在这里插入图片描述
解释: 通过将鼠标移入Direction.UP, 可以看到枚举成员UP的值为0。
注意: 枚举成员是有值的,默认为: 从0开始自增的数值。
我们把,枚举成员的值为数字的枚举,称为: 数字枚举。
当然,也可以给枚举中的成员初始化值。

// Down -> 11, Left -> 12, Right -> 13
enum Direction {UP = 10, Down, Left, Right}
enum Direction {UP = 2, Down = 4, Left = 8, Right = 16}
字符串枚举

枚举成员的值是字符串

enum Direction {UP = 'UP', Down = 'DOWN', Left = 'LEFT', Right = 'RIGHT'}

注意:字符串枚举没有自增长行为,因此,字符串枚举的每个成员必须有初始值。

枚举的特点与原理

枚举是TS为数不多的非JavaScript类型级扩展(不仅仅是类型)的特性之一。
因为: 其他类型仅仅被当做类型,而枚举不仅用作类型,还提供值(枚举成员都是有值的)
也就是说, 其他的类型会在编译为js代码时自动移除。但是,枚举类型会被编译为JS代码!
在这里插入图片描述
说明: 枚举与前面讲到的字面量类型+联合类型组合的功能类似,都用来表示一组明确的可选值列表。
一般情况下,推荐使用字面量类型+联合类型组合的方法,这种方法更加直观,简洁,高效。

(14).any类型

原则: 不推荐使用any! 这会让TypeScript变为"AnyScript"(失去TS类型保护优势)。
因为当值的类型为any时,可以对该值进行任何操作,并且不会有代码提示。


let obj: any = { x: 0 }
obj.bar = 100
obj()
const n: number = obj 

解释:以上操作都不会有任何类型错误提示,即使可能存在错误!
尽可能的避免使用any类型,除非临时使用any来避免书写很长,很复杂的类型!
其他隐式具有any类型的情况:
1.声明变量不提供类型也不提供默认值
2.函数参数不加类型
注意: 因为不推荐使用any,所以,这两种情况下都应该提供类型!

(15).typeof

众所周知, JS中提供了typeof操作符,用来在JS中获取数据的类型。

console.log(typeof "Hello world") // 打印string 

实际上, TS也提供了typeof操作符: 可以在类型上下文中引用变量或属性的类型(类型查询)。
使用场景: 根据已有变量的值,获取该值的类型,来简化类型书写。

let p = {x:1, y:2}
function formatPoint(point:{x:number, y:number}){}
formatPoint(p)
function formatPoints(point: typeof p){}

解释:
1.使用typeof操作符来获取变量p的类型,结果与第一种(对象字面量形式的类型)相同。
2.typeof出现在类型注解的位置(参数名称的冒号后面)所处的环境就在类型上下文(区别于JS代码)
3.注意: typeof只能用来查询变量或属性的类型,无法查询其他形式的类型(比如,函数调用的类型)

四.TypeScript高级类型

1.概述

TS中的高级类型有很多,重点学习以下高级类型:
(1).class类
(2).类型兼容性
(3).交叉类型
(4).泛型和keyof
(5).索引签名类型和索引查询类型
(6).映射类型

2.class类

TypeScript全面支持ES2015中引入的class关键字,并为其添加了类型注解和其他语法(比如,可见性修饰符等).

class基本使用

class Person { }
let p1: Person
p1 = new Person()

解释:
1.根据TS中的类型推论,可以知道Person类的实例对象p的类型是Person。
2.TS中的class, 不仅仅提供了class的语法功能,也作为一种类型的存在。

实例对象初始化

class Person {
  age: number
  gender = '男'
}
let p1: Person
p1 = new Person()
p1.age = 18
console.log(p1)

解释:
1.声明成员age, 类型为number(没有初始值)
2.声明成员gender, 并设置初始值,此时,可省略类型注解(TS类型推论为string类型)。

构造函数

class Person {
  age: number
  gender: string
  constructor(age: number, gender: string) {
    this.age = age;
    this.gender = gender
  }
}
let p1: Person
p1 = new Person(18, '女')
p1.age = 18
console.log(p1)

解释:
1.成员初始化(比如, age:number)后,才可以通过this.age来访问实例成员。
2.需要为构造函数指定类型注解,否则会被隐式推断为any;构造函数不需要返回值类型。

实例方法

class Points {
  x = 10
  y = 10
  scale(n: number): [number, number] {
    this.x *= n;
    this.y *= n;
    return [this.x, this.y]
  }
}
let pos = new Points()
let array = pos.scale(10)
console.log(array[0] + '---' + array[1])

解释: 方法的类型注解(参数和返回值)与函数用法相同。

继承

类继承的两种方式:

[1].extends(继承父类)

说明: JS中只有extends, 而implements是TS提供的

class Animal {
  move() {
    console.log('Moving along')
  }
}
class Cat extends Animal {
  bark() {
    console.log('喵')
  }
}
const cat = new Cat()
cat.move()
cat.bark()

解释:
1.通过extends关键字实现继承
2.子类Cat继承父类Animal, 则Cat的实例对象cat就同时具有了父类Animal和子类Cat

[2].implements(实现接口)
interface Singable {
  sing(): void
}

class Person1 implements Singable {
  sing(): void {
    console.log('z....')
  }
}
let c: Singable = new Person1();
c.sing()

解释:
1.通过implements关键字让class实现接口
2.Person类实现接口Singable意味着,Person类必须提供Singable接口中指定的所有方法和属性。

类型成员可见性

类成员可见性: 可以使用TS来控制class的方法或属性对于class外的代码是否可见。
可见性修饰符包括:

[1].public(公有的)

public: 表示公有的,公开的,公有成员可以被任何地方访问,默认可见性。

class Animal{
   public move(){
     console.log('Moving along')
   }
}

解释:
1.在类属性或方法前面添加public关键字,来修饰该属性或方法是共有的。
2.因为public是默认可见性,所以,可以直接省略。

[2].protected(受保护的)

protected: 表示受保护的,仅对其声明所在的类和子类中(非实例对象)可见。

class Animal{
  protected move(){
     console.log('Moving along!')
  }
}
class Cat extends Animal{
   bark(){
     console.log('汪!')
     this.move()
   }
}

解释:
1.在类属性或方法前面添加protected关键字,来修饰该属性或方法是受保护的。
2.在子类的方法内部可以通过this来访问父类中受保护的成员,但是,对实例不可见!

[3].private(私有的)

private: 表示私有的,只在当前类中可见,对实例对象以及子类也是不可见的。

class Animal{
  private move(){
     console.log('Moving along!')
  }
  walk(){
    this.move()
  }
}

解释:
1.在类属性或方法前面添加private关键字,来修饰该属性或发布方法是私有的。
2.私有的属性或方法只在当前类中可见,对子类和实例对象也都是不可见的!

[4].readonly(只读)

readonly: 表示只读,用来防止在构造函数之外对属性进行赋值。

class Person{
  readonly age:number = 18
  constructor(age:number){
     this.age = age;
  }
}

解释:
1.使用readonly关键字修饰该属性是只读的,注意只能修饰属性不能修饰方法。
2.注意: 属性age后面的类型注解(比如,此处的number) 如果不加,则age的类型为18(字面量类型)
3.接口或者{}表示的对象类型,也可以使用readonly.

3. 类型兼容性

注意: 在结构化类型系统中,如果两个对象具有相同的形状,则认为它们属于同一类型,这种说法并不准确。

类兼容性

更准确的说法: 对于对象类型来说,y的成员至少与x相同,则x兼容y(成员多的可以赋值给少的)。

class Point{
  x:number;
  y:number
}
class Point3D{
  x:number;
  y:number;
  z:number
}
const p:Point = new Point3D()

解释:
1.Point3D的成员至少与Point相同,则Point兼容Point3D
2.所以,成员多的Point3D可以赋值给成员少的Point.

接口兼容性

类似于class。并且,class和interface之间可以兼容

interface Point{
  x:number;
  y:number
}
interface Point2D{
  x:number;
  y:number
}
let p1:Point
let p2:Point2D = p1

interface Point3D{
  x:number;
  y:number;
  z:number
}
let p3:Point3D
p2 = p3
interface Point2D{
  x:number;
  y:number
}
class Point3D{
  x:number;
  y:number;
  z:number
}
let p3:Point2D = new Point3D()

函数兼容性

函数之间兼容性比较复杂,需要考虑:
1.参数个数
2.参数类型
3.返回值

[1].参数个数

参数多的兼容参数少的(或者说,参数少的可以赋值给多的)

type F1 = (a:number) => void
type F2 = (a:number, b:number) => void
let f1:F1
let f2:F2 = f1
const arr = ['a', 'b', 'c']
arr.forEach(()=>{})
arr.forEach((item)=>{})

解释:
1.参数少的可以赋值给参数多的,所以,f1可以赋值给f2。
2.数组forEach方法的第一个参数是回调函数,该示例中类型为:(value:string, index:number, array:string[]) => void。
3.在JS中省略用不到的函数参数实际上是很常见的,这样的使用方式,促成了TS中函数类型之间的兼容性。
4.并且因为回调函数是有类型的,所以,TS会自动推导出参数item, index, array的类型。

[2].参数类型

相同位置的参数要相同(原始类型)或兼容(对象类型)

type F1 = (a: number) => string
type F2 = (a: number) => string
let f1: F1
let f2: F2 = f1
interface Point2D{
   x:number;
   y:number
}
interface Point3D{
   x:number;
   y:number;
   z:number;
}
type F3 = (p:Point2D) => void
type F4 = (p:Point3D) => void
let f1: F3
let f2: F4 = f1

解释:
函数类型F2兼容函数类型F1, 因为F1和F2的第一个参数类型相同。

[3].返回值类型

只关注返回值类型本身即可:

type F5 = () => string
type F6 = () => string
let f5:F5
let f6:F6 = f5
type F7 = () => {name:string}
type F8 = () => {name:string, age:number}
let f7:F7
let f8:F8 = f7

解释:
1.如果返回值类型是原始类型,此时两个类型要相同,比如,类型F5和F6
2.如果返回值类型是对象类型,此时成员多的可以赋值给成员少的,比如,类型F7和F8

4.交叉类型

交叉类型(&): 功能类似于接口继承(extends), 用于组合多个类型为一个类型(常用于对象类型)
示例

interface Person{name:string}
interface Concat{phone:string}
type PersonDetail = Person & Contact
let obj: PersonDetail = {
    name:'jack',
    phone:'18206186979'
}

解释: 使用交叉类型后,新的类型PersonDetail就同时具备了Person和Contact的所有属性类型。
相当于

type PersonDetail = {name:string; phone:string}

交叉类型(&)和接口继承(extends)的对比:

相同点: 都可以实现对象类型的组合。
不同点: 两种方式实现类型组合时,对于同名属性之间,处理类型冲突的方式不同

interface A {
  fn: (value: number) => string
}
interface B extends A {
  fn: (value: string) => string
}

上述代码会报错,需要改成如下形式:

interface A {
  fn: (value: number) => string
}
interface B {
  fn: (value: string) => string
}
type C = A & B

说明:
可以简单的理解为:

fn:(value:string|number) => string

5.泛型

泛型是可以在保证类型安全前提下,让函数等与多种类型一起工作,从而实现复用。常用于: 函数,接口,class中。
需求: 创建一个id函数,传入什么数据就返回该数据本身(也就是说,参数和返回值类型相同)。

function id(value: number): number{ return value }

比如, id(10)调用以上函数就会直接返回10本身。但是,该函数只接收数值类型,无法用于其他类型。
为了能让函数能够接受任意类型,可以将参数类型修改为any。但是,这样就失去了TS的类型保护,类型不安全。

function id(value: any):any { return value }

泛型在保证类型安全(不丢失类型信息)的同时,可以让函数等与多种不同的类型一起工作,灵活可复用。
实际上,在C#和Java等编程语言中,泛型都是用来实现可复用组件的主要工具之一。

泛型函数

创建泛型函数:

function id<Type>(value: Type):Type{ return value }

解释:
1.语法: 在函数名称的后面添加<>(尖括号), 尖括号中添加类型变量,比如此处的Type
2.类型变量Type, 是一种特殊类型的变量,它处理类型而不是值。
3.该类型变量相当于一个类型容器,能够捕获用户提供的类型(具体是什么类型由用户调用该函数时指定)。
4.因为Type是一个类型,因此可以作为函数参数和返回值的类型,表示参数和返回值具有相同的类型。
5.类型变量Type, 可以是任意合法的变量名称。
.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值