TypeScript学习笔记

一、安装ts

  • 命令: 
npm i -g typescript
  • 查看安装包命令
tsc -v
  • 显示版本
Version 5.1.6

二、ts的常用类型

 1、类型注解

示例代码:

let age:number = 18

注意:说明上序代码中的 :number 就是类型注解

作用:为代码添加类型约束,约定了是什么类型就只能给该类型付同类型的值。否则会报错! 

2、常用基础类型概括

 1、js已有类型

  • 原始类型:number、string、boolean、null、undefined、symbol
  • 对像类型: object(数组、对象、函数)

2、ts新增类型

  • 联合类型、 自定义类型、接口、元组、字面量类型、枚举、void、any等

3、原始类型

  •  原始类型:number、string、boolean、null、undefined、symbol
  • 这些类型完全是按照js原始类型来写的

TypeScript学习笔记_第1张图片

4、数组类型

1、数组类型的两种写法,推荐用第一种

let arrNumber: number[] = [1, 2, 3]
let arrString: Array = ['1', '2', '3']

5、联合类型 

注意:一个数组中存在多个类型的数据

//加上小括号说明,数组内的类型可以是小括号里的任何类型
let arr: (number | string | null)[] = [null, 1, '23']

//不加小括号,说明arr即可以是Number类型,也可以是string类型的数组
let arr2: number | string[] = 123
let arr3: number | string[] = ['1', '2', '3']

 解释:|在ts中叫联合类型

 6、类型别名

  • 类型别名:为任意类型取别名
  • 使用场景:当同一类型被多次复杂使用时,可以通过类型别名,简化该类型的使用。
type CustomArray = (number | string)[]
let arr1: CustomArray = ['1', 1]
let arr2: CustomArray = ['1', 1]

注意:

  1. 使用type关键字来创建类型别名
  2. 类型别名可以是任意合法变量名称        

7、函数类型

注意:

  • 函数类型是指:函数参数类型、函数返回值类型 

1、单独自定参数、返回值的类型

function add(number1:number,number2:number):number{
    return number1 + number2
}

console.log(add(1,2));
let add = (number1: number, number2: number): number => {
    return number1 + number2
}
console.log(add(1, 3));

2、同时指定参数和返回值的类型

注意:这种方法只能用于函数表达式

TypeScript学习笔记_第2张图片

let add: (number1: number, number2: number) => number = (number1, number2) => {
    return number1 + number2
}
console.log(add(1, 3));

8、 void类型

注意:如果函数没有返回值,那么函数返回值就是void(就是空值的意思)

function onVoid(num:number):void{
    console.log(num);//执行结果:12
}

onVoid(12)

9、函数可选参数 

注意:使用函数实现某个功能时,函数参数可传可不传。这种情况在给函数指定参数类型时,就用到可选参数,

 可选参数:在可传可不传的参数后面加

注意:可选参数后面不能再写必传参数 

function mySlice(start?: number, end?: number) {
    console.log('开始', start, '结束', end);
}

mySlice()//开始 undefined 结束 undefined
mySlice(1)//开始 1 结束 undefined
mySlice(1, 2)//开始 1 结束 2

10、对象类型

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

解释:

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

对象类型写法:

let obj: { name: string; age: number; onSay(): void } = {
    name: '李玉',
    age: 21,
    onSay() {
        console.log('111');
    }
}

let obj2: {
    name: string
    age: number
    onSay: (e:number) => void
} = {
    name: '简隋英',
    age: 28,
    onSay(e) {
        console.log(e);
    }
}

11、可选属性

注意:

  1. 对象的属性或方法也是可选的,此时就用到了可选属性了。
  2. 可选属性的语法与可选参数的语法是一致的,都使用来表示 
function myAxios(config: { url: string, methods?: object }) {
    
}
let config = {
    url:'https://www.baidu.com'
}
myAxios(config)

12、接口

一、基础用法 

注意:当一个对象类型被多次使用时,一般会使用接口interface)来描述对象的类型,达到复用的效果。

解释:

  • 使用interface关键字来声明接口
  • 接口名称可以是任意合法的名称
  • 声明接口后可以直接使用变量的名称作为变量的类型
  • 因为每一行只有一个属性类型,因此属性类型没有{分号} 
interface IPeron {
    name: string
    age: number
    onSay: () => void
}

let obj: IPeron = {
    name: '李玉',
    age: 21,
    onSay() {

    }
}

二、接口和类型别名对比

interface(接口)和type(类型别名)的对比

  • 相同点:都可以给对象指定类型
  • 不同点

        * 接口interface只能为对像指定类型

        * 类型别名type,不仅可以对对象指定类型,实际上可以为任意类型指定别名

        *type用=(等号),interface不用= (等号)

interface例子:

interface IPeron {
    name: string
    age: number
    onSay: () => void
}

let obj: IPeron = {
    name: '李玉',
    age: 21,
    onSay() {

    }
}

type例子:

type IPeron = {
    name: string
    age: number
    onSay: () => void
}

let obj: IPeron = {
    name: '李玉',
    age: 21,
    onSay() {

    }
}

type例子: 

type NumStr = number | string

三、接口的继承

注意: 如果两个接口之间有相同的属性或方法,可以将公共属性和方法抽离出来,通过继承来实现复用。

如下列,这两个接口都有x,y属性,重复写两次太过繁琐。

interface IPearn1 {
    x:string,
    y:number
}

interface Ipearn2{
    x:string,
    y:number,
    id:number
}

更好的方式:使用extends关键字的方法去实现继承

interface IPearn1 {
    x:string,
    y:number
}

interface Ipearn2 extends IPearn1{
    id:number
}

13、元组

 场景:

        在地图中使用经纬度来标记坐标信息。

        可以使用数组来标记坐标,那么,该数组中只有两个元素,并且这两个元素都是数值类型。

let position: number[] = [12, 23]
  • 使用number[]的缺点:不严谨,因为该类型的数组中可以出现任意多个数字。

注意:更好的方法是使用元组(Tuple),元组类型是另一种类型的数组,它确切的知道包含多少个元素,以及特定索引对应的类型

let position: [number, number] = [12, 45]

解释:

  • 元组类型可以确切的标记出有多少个元素,以及每个元素的类型

14、类型推论

注意:在TS中,某些没有声明却指出类型的地方,TS的类型推论机制会帮助提供类型。所以,因为有类型推论的存在,有的地方可以沈略类型注解;

 1、声明变量并且初始化时

let aa = 12
let bb = 'dddd'

2、决定函数返回值时

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

add(1, 2)

15、类型断言

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

比如:

TypeScript学习笔记_第3张图片

注意:getElementById方法返回值的类型是HTMLElement,该类型只包含所有标签的公共属性或方法,不包含a标签特有的href等属性。

因此,这个类型太宽泛(不具体),无法超做href等a标签特有的属性的方法。

解决方式:这种情况下就需要使用类型断言指定更加具体的类型。 

1、类型断言语法1 

解释:

  • 使用as关键字实现
  • 关键字as后面的类型是一个更加具体的类型(HTMLAnchorElement是HTMLElement的子类型)
  • 通过类型断言,aLink的类型变得更加的具体,这样就可以访问a标签特有的类型或方法了 

 1、类型断言语法2

 注意:另一种写法是,使用<>语法 

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

16、字面量类型

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

TypeScript学习笔记_第4张图片

通过TS类型推论机制,可以得到答案:

  1. 变量str1的类型为:string
  2. 变量str2的类型为:'Hello TS'

解释:

  1. str1是一个变量(let),它的值可以是任意字符串,所以类型是string
  2. str2是一个常量(const),它的值不能变化,所以类型是'Hello TS'

注意:此处的 'Hello TS'就是字面量类型。也就是说某个特定字符串也可以作为TS中的类型。

除字符串外,任何的JS字面量(比如、对象、数字等)都可以作为类型使用。

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

使用模式:字面量类型配合联合类型(|)一起用

使用场景:用来表示一组明确的可选值列表。 

比如贪吃蛇游戏中,游戏的方向就只有(上下左右)任意一个

 解释:参数direction的值只能是up/down/left/right中的任意一个。

优势:相比于string类型,使用字面量类型更加精确、严谨。

 17、枚举

1.1、基本枚举

注意:形参direction的类型为枚举Direction,那么,实参的值就应该是枚举Direction成员中的任意一个

enum Direction { UP, Down, Left, Right }

function changeDirection(direction: Direction) {
    console.log(direction);
}

changeDirection(Direction.UP)
changeDirection(Direction.Down)
changeDirection(Direction.Left)
changeDirection(Direction.Right)

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

1.2、 枚举成员的值以及数字枚举

问题: 我们将枚举成员作为函数的实参,它的值是什么?

解释:通过将鼠标移入Direction.UP可以看到枚举成员UP的值是0

注意:枚举成员是有值的,默认为:从0开始自增的数字 

我们把,枚举成员的值为数字枚举

当然也可以给枚举中的成员初始化值:

//UP=10, Down=11, Left=12, Right=13
enum Direction { UP = 10, Down, Left, Right }
//UP=10, Down='abc', Left=8, Right=4
enum Direction { UP = 10, Down = 5, Left = 8, Right = 4 }

1.3、字符串枚举

 注意:枚举成员的值是字符串

//UP=UP, Down='Down', Left='Left', Right='Right'
enum Direction { UP = 'UP', Down = 'Down', Left = 'Left', Right = 'Right' }

function changeDirection(direction: Direction) {
    console.log(direction);
}

changeDirection(Direction.UP)
changeDirection(Direction.Down)
changeDirection(Direction.Left)
changeDirection(Direction.Right)

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

 1.4、枚举的特点及其原理

枚举是TS为数不多的非JS类型级拓展(不仅仅是类型)的特征之一

因为:其他类型不仅仅被当做类型,而是枚举不仅用于类型还提供值(枚举成员都是有值的)

也就是说,其他类型会在编译成JS代码时直接移除,但是,枚举类型会被编译成JS代码

 TS:

//UP=UP, Down='Down', Left='Left', Right='Right'
enum Direction { UP = 'UP', Down = 'Down', Left = 'Left', Right = 'Right' }

function changeDirection(direction: Direction) {
    console.log(direction);
}

changeDirection(Direction.UP)
changeDirection(Direction.Down)
changeDirection(Direction.Left)
changeDirection(Direction.Right)

JS: 

//UP=UP, Down='Down', Left='Left', Right='Right'
var Direction;
(function (Direction) {
    Direction["UP"] = "UP";
    Direction["Down"] = "Down";
    Direction["Left"] = "Left";
    Direction["Right"] = "Right";
    // Direction为对象,UP,Down。。。为属性加赋值
})(Direction || (Direction = {}));
//传入Direction=understand||Direction = {}

function changeDirection(direction) {
    console.log(direction);
}
changeDirection(Direction.UP);
changeDirection(Direction.Down);
changeDirection(Direction.Left);
changeDirection(Direction.Right);

说明:枚举类型和前面说到的字面量类型+联合类型组合功能类似,都用来表示一组明确的可选值列表。

一般情况下,推荐使用字面量类型+联合类型,因为相比于枚举这种方式更加简洁、高效。

18、any类型 

原则:不推荐使用any!这会让TS变成"AnyScript"(失去类型保护的优势)

因为当值的类型为any时,可以对该值进行任意超做,并且不会有任何提示。

TypeScript学习笔记_第5张图片

其他隐式具有any类型的情况:

  • 申明变量不提供类型也不提供默认值
  • 函数参数不加类型

19、TS中的typeof

 众所周知,JS提供了typeof运算符,用来在js中获取数据的类型

console.log(typeof 'hello'); //打印 string

实际上TS也提供了typeof操作符:可以在类型上下文中引用变量或属性的类型(类型查询)。

使用场景:根据已有变量的值,来获取该值的类型,来简化类型书写。

let parameter = {
    x: 10,
    y: 20
}

function forMater(point: { x: number, y: number }) {}

forMater(parameter)
let parameter = {
    x: 10,
    y: 20
}

let p = {
    x: 0,
    y: 0
}

function forMater(point: typeof p) {}

forMater(parameter)

三、TypeScript高级类型

 class

 1、calss的基本使用

TS全面支持ES5中引入的class关键字

  • 添加属性的方法有以下两种:
class Person{
    //添加属性的方法有以下两种
    name1:string
    name2 = '简隋英'
}

const p = new Person()

p.name1//属性是string
p.name2//属性是string

解释:

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

 2、calss的构造函数

class Person{
    //添加属性的方法有以下两种
    name1:string
    name2 = '简隋英'

    constructor(str1:string,str2:string){
        this.name1 = str1
        this.name2 = str2
    }
}

const p = new Person('李玉','简隋英')

p.name1//属性是string
p.name2//属性是string

解释:

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

3、class实例方法 

TypeScript学习笔记_第6张图片

class Person {
    x = '简隋英'
    y = '李玉'

    scale(name1: string, name2: string): void {
        this.x = name1
        this.y = name2
    }
}

const p = new Person()
p.scale('江停', '严峫')

console.log(p.x, p.y);

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

4、class的继承 

注意:类的继承有两种方式extends(继承父类)implements(实现接口)

 extends(继承父类) 
class Name{
    onBackName(){
        console.log('江停');
    }
}

class Person extends  Name{

}

const p = new Person()
p.onBackName() //结果打印:江停

 解释:

  1. 通过extends关键字实现继承
 implements(实现接口)
interface Singable{
    name:string
    sing():void
}

class Person implements Singable{
    name: string
    sing(): void {
        console.log('夫妻双双把家还!');
    }
}

解释:

  1. 通过关键字implements让class实现继承
  2. implements实现继承,要继承接口所有的属性和方法,少一个都会报错! 

5、class类的可见性

类成员的可见性,可以使用TS来控制class的方法或属性对于class外的代码是否可见。

可见性修饰符包括:1、public(公有的)2、protected(受保护的)3、privated(私有的)

public(公有的)

class Person {
    name = '江停'
    public sing(name: string): void {
        console.log(this.name + name);
    }
}

const p = new Person()
p.sing('严峫')//结果打印:江停严峫

注意:

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

protected(受保护的)
class Name {
    protected member() {
        console.log('吴雩');
    }
    cole() {
        this.member//这里可以调用
    }
}

const nameClass = new Name()
nameClass.cole()//这个方法可以调用
nameClass.member()//这个方法不可以调用

class Person extends Name {
    name = '江停'
    sing(name: string): void {
        this.member()
    }
}

const p = new Person()
p.sing('严峫')//结果打印:江停严峫
p.cole()//这个方法可以调用
p.member()//这个方法不可以调用

 注意:

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

privated(私有的)
class Name {
    private member() {
        console.log('吴雩');
    }
    cole() {
        this.member//这里可以调用
    }
}

const nameClass = new Name()
nameClass.cole()//这个方法可以调用
nameClass.member()//这个方法不可以调用

class Person extends Name {
    name = '江停'
    sing(name: string): void {
        this.member()//这个方法不可以调用
    }
}

const p = new Person()
p.sing('严峫')//结果打印:江停严峫
p.cole()//这个方法可以调用
p.member()//这个方法不可以调用

注意:

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

6、readonly只读修饰符 

出来可见修饰符以外,还有一个常见修饰符就是:readonly(只读修饰符)

readonly:表示只读,用来防止构造函数以外的属性对他进行赋值

class中

注意:只能在constructor中修改属性,其他地方不能。且readonly只能用于属性不能用于方法。

class Person{
    readonly age:number = 18
    constructor(age1:number){
        this.age = age1 //只能在这里改
    }
    changAge(){
        this.age = 23 //修改属性会报错
    }
}

const p = new Person(21)

class中 

注意:如果 readonly的属性没有增加类型注解,那么值的属性就是他的初始值,相当于一个常量。就算是在constructor里也不能改。

class Person{
    readonly age = 18
    constructor(age1:number){
        this.age = age1 //这里也不能改
    }
    changAge(){
        this.age = 23 //修改属性会报错
    }
}

const p = new Person(21)

interface中 
interface IPerson{
    readonly name:string
}

let obj:IPerson ={
    name:'严峫'
}

obj.name = '江停'//name属性标记只读,所以不能被复制

{}中

let obj: { readonly name: string } = {
    name: '严峫'
}

obj.name = '江停'//name属性标记只读,所以不能被复制

 解释: 

  1. 使用readonly关键字修饰该属性是只读的,注意只能修饰属性不能修饰方法
  2. 注意:在class属性中如果不加类型注解,则属性的类型是初始化时的常量。
  3. 接口或{}表示的对象类型,也可以使用readonly

类型兼容性

1、基本理解 

两种类型系统:结构化类型系统和标明类类型系统

结构化类型系统 

TS采用的是结构化类型系统,也叫做duck typing(鸭子类型),类型检查关注的是值所具备的性状

也就是说,结构化类型系统中,如果两个对象具有相同的形状,则认为他们属于同一类型。

class Name {
    x:number
    y:number
}

class Name2{
    x:100
    y:200
}

const p:Name = new Name2()

解释:

  1. Name和Name1是两个名称不相同的类
  2. 变量p的类型被显示标注为Name类型,但是她的值却是Name1的实例,并没有类型错误
  3. 因为TS是结构化类型系统,只检查Name和Name1的结构是否相同
  4. 但是如果在标明类类型系统中,他们是不同的类,类型无法兼容

 2、对象类型兼容性

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

更准确的说法是:对于对象类型来说,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

3、接口之间的兼容性

除了class之外,TS中的其他类型也存在相互兼容的情况,包括:1、接口兼容 。2、函数兼容性等。

接口之间的兼容性,类似于class,并且,class和interface之间也可以兼容

TypeScript学习笔记_第7张图片

4、函数之间的类型兼容性 

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

参数个数

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

type F1 = (a:number) =>void
type F2 = (a:number, b:number)=>void

let a:number = 12
let f1:F1 = (a) => {
    return null
}

let f2:F2 = f1

const arr = ['a', 'b', 'c']
arr.forEach(() => { })
arr.forEach((item) => { })

解释:

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

类型属性

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

  •  原始类型
type F1 = (a: number) => void
type F2 = (a: number) => void
let f1:F1
let f2:F2
let num = 12
f2 = (num) => {}

f1 = f2
f2 = f1
  • 对象类型
interface Point2D {
    x: number
    y: number
}
interface Point3D {
    x: number
    y: number
    z: number
}
type F2 = (p: Point2D) => void
type F3 = (p: Point3D) => void

let p1 = { x: 12, y: 10 }
let f2: F2 = (p1) => { }
let f3: F3 = f2
f2 = f3//会报错,多的不能赋值给少的

解释:

  1. 注意,此处于前面讲的接口兼容性冲突
  2. 技巧,将对象拆开,把每个属性看做一个个参数,则,参数少的f2可以赋值给参数多的f1

返回值
  •  例子1:
type F5 = () => string
type F6 = () => string
let f5:F5 = () => {return '111'}
let f6:F6 = f5
  • 例子2:
type F7 = () => { name: string }
type F8 = () => { name: string, age: number }
let f7: F7 = () => {
    return {
        name:'李玉'
    }
}
let f8: F8 = () => {
    return {
        name: '简隋英',
        age: 28
    }
}
f7 = f8

console.log(f7);//打印结果:[Function: f8]

解释:

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

 交叉类型

 1、基本介绍

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

  •  extends:
interface Person{
    name:string
}
interface Content extends Person{
    age:number
}
let PersonContent:Content = {
    name:'简隋英',
    age:27
}
  •  交叉类型(&):
interface Person{
    name:string
}
interface Content{
    age:number
}

type PersonContent = Person & Content

let obj:PersonContent = {
    name:'简隋英',
    age:27
}

 解释:使用交叉类型后,新的类型PersonContent就同时具备了Person 和 Content的所有属性类型。

  • 相当于:
type PersonContent = { name: string, age: number }

2、交叉型和接口型的对比说明 

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

  • 相同点:都可以实现对象类型的组合
  • 不同点:两种方法实现实现类型组合时,对于同名属性之间,处理类型冲突的方式不同
  1. 接口型(extends):
interface A {
    fn: (value: number) => string
}

interface B extends A{//B不兼容
    fn: (value: string) => string
}
  1. 交叉型(&) :
interface A {
    fn: (value: number) => string
}

interface B {//B不兼容
    fn: (value: string) => string
}

type C = A & B

说明:以上代码,接口继承会报错(类型不兼容);交叉类型没有错误,可以简单理解为:

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

泛型和keyof

1、泛型的基本使用 

泛型是在保证类型安全的前提下,让函数等与多种类型一起工作,从而实现复用,常用于:函数、接口、class中。

需求 :创建一个id函数,传入什么数据就返回该数据本身。(也就是说参数和返回值类型相同)

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

 比如:id(10)调用以上函数就会直接返回10本身。但是,该函数只能用于数值类型,无法用于其他类型。

为了能让函数接收任意类型,可以将参数类型修改为any,但是,这样就会失去TS的类型保护,类型不安全。

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

解释:泛型在保证类型安全(不丢失类型信息)的同时,可以让函数等与多种不同的类型一起工作,灵活复用。

创建泛型函数 
function id(value: Type): Type {
    return value
}

解释:

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

const num1 = id(10)
const str = id('江停')
const beal = id(true)

解释:

  1. 语法:在调用函数的后面加<>(尖括号),尖括号中指定具体的类型,比如,此处的number
  2. 当传入类型number后这个类型就会被函数声明时指定的类型变量Type捕获。
  3. 此时,Type的类型就是number,所以函数id参数和返回值的类型都是number。

同样,如果传入类型string,函数id参数和返回值的类型就是string

这样,通过泛型就做到了让id函数于多种不同的类型一起工作,实现了复用的同时保证了类型安全

 2、简化泛型函数调用

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

const num1 = id(10)
const str = id('江停')
const beal = id(true)

解释:

  1. 在调用泛型函数时,可以省略<类型>来简化泛型函数的调用
  2. 此时,TS内部会采用一种叫做类型参数推断的机制 ,来根据传入的实参自动推断出类型变量Type的类型。
  3. 不如,传入实参10,Ts会自动推断出变量num的类型为number,并且作为Type的类型

推荐:使用这种简化的方式调用泛型函数,使代码变得更短,更加易读

说明:当编译器无法推断类型或者类型不准确时,就需要显式的传入类型参数

3、泛型约束

泛型约束:默认情况下,泛型函数的类型变量Type可以代表多个类型,这导致无法访问任何属性。

比如:id('a')调用函数时获取参数长度:

function id(value: Type): Type {
    console.log(value.length);//这个代码报错
    return value
}

const num1 = id([10,12])

解释:Type可以代表任意类型,无法保证一定存在length属性,比如number类型就没有length。

此时,就需要为泛型添加约束收缩类型(缩小类型的取值范围)

添加泛型的收缩属性,主要有以下两种方式:指定更加具体的类型 和 添加约束

指定更加具体的类型 
function id(value: Type[]): Type[] {
    console.log(value.length);//这个代码报错
    return value
}

const num1 = id([10,12])

 比如:将类型修改为Type[](Type类型的数组)。因为只要是数组就一定存在length属性,因此就可以访问了

添加约束extends
interface ILength{
    length:number
}
function id(value: Type): Type {
    console.log(value.length);//这个代码报错
    return value
}

const num1 = id([10,12])
const str = id('江停')
const beal = id({length:12,name:'江停'})

 解释:

  1. 创建描述约束的接口ILength,该接口要提供ILength属性
  2. 通过extends关键字使用该接口,为泛型(类型变量)添加约束
  3. 该约束表示:传入的类型必须具有length属性

 注意:传入的实参(比如,数组)只要有length属性即可,这也符合前面讲到的接口的类型兼容性

多个泛型变量的情况 

 泛型的类型变量可以有多个,并且类型变量之间还可以约束(比如:第二个类型变量受第一个类型变量约束)。

比如,创建一个函数来获取对象中属性的值:

function getProp(obj:Type,key:Key){
    return obj[key]
}

let person = {name:'江停',age:31}
console.log(getProp(person,'name'));

解释:

  • 添加了第二个类型变量Key,两个类型变量之间使用(,)逗号分隔。
  • keyof关键字接收一个对象类型,生成其键名称(可能是字符串或数字)的联合类型
  • 本实例中keyof Type实际上获取的是person对象所有联合类型,也就是:‘name’|‘age’
  • 类型变量Key受Type约束,可以理解为:key只能是Type所有键中的任意一个,或者说只能访问对象中纯在的属性

索引签名类型和索引查询类型 

映射类型 

你可能感兴趣的:(ts,typescript)