TypeScript学习笔记

一、TypeScript是什么

TypeScript(TS)JavaScript(JS)的超集,TS兼容JS的所有功能,并额外提供了类型检查和基于类的面向对象功能。JS是动态语言,只能在代码运行的时候检查类型错误(Type error),遇到这种错误后,浏览器会抛出异常,并且停止执行后面的 JS 代码,严重降低用户体验。TS是静态语言,可以在代码编写阶段检查类型错误,并且不允许改变变量类型,很大程度上避免了Type error的发生。TS 实现了对 JS的向下兼容,JS代码可以不加修改地运行在TS上。

二、TypeScript类型注解的概念

可以理解为类型声明,语法是:let varName: typeName,为变量添加类型约束。类型注解了以后,就只能将注解类型的值赋予该变量,否则编译器会报错。

多类型注解。用竖线 “|” 连接多种类型来注解变量,就可以使变量接受多种类型。

// 以下类型注解,表示变量a可以接受number和string两种类型数据的赋值
let a: number | string = ""
a = 2
console.log(a)

三、TypeScript的类型推断

TS 尽可能在确保不出错误的情况下,为开发人员减少代码输入。如果在声明类型的时候同时赋值了,TS可以推断该变量的类型,从而可以忽略类型注解。一般在两个地方可以出现类型推断:

  1. 声明变量并立即赋值时;
  2. 函数返回数据时。

四、TypeScript的类型

TypeScript包括以下两种类型:

(一)JS中已有的基础类型

  1. numberstringbooleannullundefinedsymbol

    最常用的就是 numberstringboolean 类型。默认情况下,nullundefined 是所有类型的子类型,可以赋值给numberstringboolean类型。但是如果指定了 --strictNullChecks 标记,null 和 undefined 只能赋值给 void 和它们各自,不然会报错。

  2. arrayobjectfunction

(二)TS新增类型

联合类型、自定义类型(类型别名)、接口、元组、字面量、枚举、voidany

五、TypeScript类型注解语法

Typescript 中实现类型保护的机制,就是通过类型注解语法实现的。它的基本语法结构,就是在需要注解的变量、对象、属性等名称后面用英文冒号:引出注解语句。

(一)原始类型

  • numberstringbooleannullundefinedsymbol类型变量,在变量声明的时候,在变量名后直接加类型,**注意首字母是小写的。**

    // 在变量声明的名称后面,用冒号语法,后面再跟上类型
    let a**:** string = "Hello,TypeScript"
    a = 0 // 编译器将报错:不能将 0 分配给 string
    // 以下正确
    let b**:** number = 2
    let isLoading**:** boolean = true
    

(二)数组类型

  1. 单一类型的注解。表示该数组内所有元素只接受一种数据的赋值。

    let arr1: number[] = [0,2,3]
    let arr2: string[] = ['0','00','000']
    // 另外一种注解方式,实际上是泛型接口。TS 将数组用泛型接口实现了。
    let arr3: Array<string> = ['ddd']
    
  2. 联合类型注解。表示该数组元素可以接受多种类型。

    let arr1: (number | string | null)[] = [1, "2", null]
    arr1 = [33, "ddd", null, null]
    console.log(arr1)
    

(三)类型别名(自定义类型)

对于比较复杂而且多次使用的类型注解,为他起一个别名,便于记忆和书写。使用**type**关键字来创建类型别名,或者说自定义一个类型。

// 1. 定义一个类型
type userDefine = (number | string | null)[]
// 2. 然后用这个类型去注解一个变量
const var1: userDefine = [1, "222", null]
console.log(var1)

type num = 1 extends number?1:0 这个如何理解?在 type 中,extends 的意思是,前者是否属于后者,也就是说,前者是否可以赋值给后者。因此这句话可以断句为:type num = (1 extends number)?1:0

(四)函数类型

函数主要通过参数和返回值的类型类定义类型。有两种定义方式:

  1. 单独定义参数和返回值类型。参数的类型注解直接用冒号跟在参数后面,返回值用冒号跟在参数和函数体之间

    // 函数声明的方式
    function f(a: number, b: string): string {
      console.log(a, b)
      return "sss"
    }
    // 函数表达式的方式
    const f = (a:number,b: string): string => {
    	console.log(a,b)
    	return "ssss"
    }
    
  2. 同时指定参数和返回值类型。当使用表达式的方式定义函数的时候,可以利用箭头函数的形式定义函数参数类型和返回值类型。

    const f2: (a: number, b: number) => number = (a, b) => {
      return a + b // 此处虽然是单行,但仍然要显示调用return,不然类型不匹配。
    }
    f2(3, 4)
    
  3. 没有返回值的函数类型。没有返回值的函数,可以显示说明为void,或者不写

    // void返回类型
    function f3(): void {
      console.log("hello")
    }
    // 或者
    function f3() {
      console.log("hello")
    }
    f3()
    
  4. 可选参数。有的函数的参数,可以指定,也可以不指定,不强制必须赋值。在参数名后加上?

    function f4(a:number,b?:number):number{
    	return a + b
    }
    
  5. 利用箭头函数定义函数类型。这定义了一种函数类型,但没有实现该函数体。参数类型跟定义箭头函数实例一样,但**返回值类型的位置不一样,直接跟在箭头后面。**TS 遇到 : 就知道后面的代码是写类型用的。

    // 定义一个函数类型 type1,它有一个数值类型参数,返回值是数值类型
    type type1 = (a: number) => number
    // 实现这个函数类型的实例
    const ftype: type1 = (ax: number) => { console.log(ax); return 1}
    
  6. 对于默认参数,传递undefined或者不传递数据,TS会采用函数定义时候指定的默认数据。看下面的例子:

    function add(a:number,b:number=3){
    	console.log(a+b)
    	return a+b
    }
    add(1,2) // OK,结果为:3
    add(1)   // OK,结果为:4
    add(1,undefined) // OK,结果为4
    

(五)对象类型

  1. 作用。用来描述对象的结构声明

  2. 方法如下:将对象每个属性像注解原始类型一样,两个属性用分号隔开,与声明变量的逗号不一样。如果用回车分开,就可以不用分号。

  3. 对象也可以定义可选属性,方法是在属性名前加”?“。

    // 用分号分隔:
    let person: {name: string; age: number}
    // 用回车分隔:
    let person2: {
    	name: string
    	age: number
    }
    // 定义可选属性
    let person3: {
    	name: string,
    	age: number,
    	sex?: string
    }
    // 定义的时候立即赋值,这个时候实际上可以不用写类型注解,ts 能够自动推断出来
    let person4: { name: string; age: number } = {
        name: '张',
        age: 24
    }
    

(六)接口

  1. 接口的概念。类似于简单变量的 type,对于相同对象类型多次使用的场景,可以用 interface 来定义一个接口,达到简化代码的目的。

  2. 用法。interface name {…;….},注意与类型别名的声明方法不同,这里不用“=”。

  3. 接口里也可以定义可选属性。

  4. 接口里也可以有方法,要定义一个函数类型,明确参数和返回值类型。

  5. 接口的内的属性和方法是一种类型声明。**不能提供默认值,**否则TS 会把默认值当做一种类型声明(字面量类型),不可更改。

  6. 接口的声明不能用=

  7. 接口可以继承,也就是在一个接口上扩展属性。

    // 定义一个接口
    interface person5 {
        name: string
        age: number
        sex?: string
    		// 以下两种不推荐,TS 会认为将 gender 和 grade 声明为'男'和 '1'类型。
    		gender:'男',
    		grade: 1,
    }
    // 实现一个接口实例
    let p:person5 = {name: '哈哈哈',age:44,sex:'mail'}
    // 继承一个接口
    interface person6 extends person5 {
    	// 在 person5的基础上增加一个属性
        grade: number
    }
    // 实现继承接口的实例
    let p2: person6 = {
        name: 'name',
        age: 444,
        sex: '男',
        grade: 4
    }
    
  8. 用接口定义函数类型

    // 1. 定义函数类型接口
    interface IFn {
        (p:string):string
    }
    // 2. 用函数类型接口实现函数
    // 实现函数类型
    let fn1:IFn = (b:string)=>''
    
  9. 接口的函数属性

    接口与类不一样,只有方法属性。但也可以用两种方式来定义属性的类型,返回值的定义方式的区别,即:和=>。

    
    // 1. 用量方法定义接口的属性
    interface IFn2 {
        fn: () => string
        fn2():void
    }
    // 2. 以上两种方式声明了两个方法属性,而不是类的成员函数
    // 3. 因此尽管以下声明方法的方式不一样,但实现的方法是一样的
    // 以下是第一种方法:
    let fn2imp: IFn2 = {
        fn: () => '',
        fn2: ()=> {}
    }
    // 以下是第二种方法:
    let fn2imp2: IFn2 = {
        fn() {return ''},
        fn2(){}
    }
    

(七)元组

  1. 概念。用来明确定义一个数组,包括它的元素个数,和每个元素的类型。他是一种特殊的数组,也可以理解为固定长度、固定元素类型的数组。

  2. 用法。在类型注解部分直接用方括号。元组成员必须全部具有或全部不具有名称。

  3. 也可以定义一个元组为类型别名。

    // 声明一个元组
    let xy: [x: number, y: number] = [3, 4]
    // 赋值的时候必须严格遵守个数和类型的限制
    xy = [4,4]
    // 可以定义一个元组的类型别名吗?
    type xy2 = [x: number, y: number]
    let xy3:xy2 = [3,4]
    

(八)类型断言

  1. 概念。一般获取变量的时候,可能会获得比较泛型的父类型,通过 as 关键字,可以指定一个更具体的子类型,从而访问到子类型特有的方法和属性。

  2. 用法。as<>操作符。

    const aLink = document.getElementById(’link’) as HTMLAnchorElement
    // 或者
    const aLink = <HTMLAnchorElement>document.getElementById(’link’) 
    aLink.href='ddddd'
    
  3. 类型断言只是欺骗编译器不报错,但代码运行后可能会出现无法预料的结果。因此,除非十分有把握,尽量少用类型断言。例如以下示例。

    type p = number | string
    
    function showp(P: p) {
        console.log((P as string).length)
    }
    showp(2)
    // 输出 undefined,不是所期望的结果。
    

(九)字面量类型

  1. 概念。一个常量,例如一个字符串、一个数字,实际上是一个字面量。例如,用 const 定义了一个变量,并赋值,实际上就是定义了一个字面量。const t = ‘b-737’,t 就是b-737类型的字面量。

  2. 可以通过联合类型将几种字面量结合起来,定义一个类型。从而是该变量的意义更为精确明确易懂。

    // 以下定义的函数的参数,只能选择以下四种字符串。
    function changeDirection(direction: 'up' | 'down' | 'left' | 'right')
    // 定义一种特殊类型,表示性别
    type Sex = 'mail' | 'femail'
    

(十)枚举类型

  1. 概念。类似于字面量与联合类型组合的功能。两者可以完全替代。

  2. 定义方法。用 enum 关键字声明:enum name {var1,var2},内部常量不需要引号,因为他相当于属性名,它有值,默认从 0 开始自增。

  3. 枚举成员的值还可以为字符串。还可以混合。字符串枚举没有自增长行为。定义字符串枚举的时候,必须初始化。

  4. 从左边开始的连续几个枚举成员可以不初始化,系统会自动给他赋值。

  5. 使用方法。枚举定义完成后,可以直接使用,无法使用 let 或 const 进行再赋值。

    enum direction {
        left=10,right=20,up=30,down=33,s="name"
    }
    console.log(direction.s)
    enum enum1{
    	l,r='4'
    }
    

(十一)any类型

他可以接受任何类型。不建议使用这种类型,会失去类型保护功能。any 类型的数据可以赋值给任何类型。

(十二)typeof 操作符

  1. 概念。根据类型的上下文环境,来获取变量或属性的类型。通过这个操作符来动态查询一个对象的类型,然后用他来做类型标注,简化操作。

  2. 注意。他只能获取变量或者对象属性的类型。

    // 1. 先定一个对象
    let p = {x:1 ,y: 2}
    // 2. 用 typeof得到 p 的类型,用来定义
    function f(point: typeof p){}
    f({x:3,y:3}
    

六、TypeScript 的高级类型

(一)class 类

  1. 类的声明和实例化。

    class man{ }
    const people = new man()
    
  2. 类的属性定义。既可以带默认值,也可以不带。两个属性之间,用分号分隔,也可以用回车分开。

  3. 类的成员也可以是可选的;

  4. 如果声明类成员的类型,用,如果初始化类成员,则需要用=,包括初始化函数成员。

    class teacher {
    	age: number     // 没有默认值。用回车分隔
    	gender = '男'   // 初始化默认值注意这里的等于号,不是冒号。后期可以更改。
    	score?:number   // score 是可选属性
    }
    
  5. 类的构造函数。用 constructor 关键字来定义。他很像一个函数。构造函数没有返回值。

    // 声明一个类
    class teacher {
        age: number;   // 没有默认值
        gender = '男'; // 有默认值。注意这里的等于号,不是冒号。后期可以更改。
    		constructor(age: number,gender: string){
    			this.age = age
    			this.gender = gender
    		}
    }
    // 用构造函数实例化
    const t1 = new teacher(44,'男')
    
  6. 类的实例方法。定义方法与对象的方法一样。

  7. 类的继承。可以实现一些公共属性和方法的复用。有两个方法继承类。一种是使用 extend关键字,从父类继承方法和属性,就像接口继承一样。二是通过实现接口implements的方式来,必须实现接口中的所有属性和方法。

    // 通过一个实现接口来继承一个类,要实现接口的所有属性和方法
    // 也可以在实现的时候增加方法和属性
    interface animal{
    		// 声明了一个函数类型的属性 move
        move(): void,
        age:number
    }
    class dog implements animal {
        move() { }
        age=10
    		// 增加一个属性
    		hight=100
    		// 增加一个方法
    		move(){}
    }
    
  8. 类成员的可见性。默认情况下,所有属性和方法对外是可见的。即可以通过 . 运算符访问他们。可见性有三种:publicprivateprotected

    1. public 是默认的属性,在任何场合都可以访问到它。
    2. protected当前类的其他方法子类的方法中可见(就是在定义该类的代码区域),但在所有实例对象中不可见。
    3. private是私有属性,只能在当前类中可见,在子类和实例对象中均不可见。
    4. static静态成员。类的常量,实例不能访问他。在当前类和子类的方法中可以访问它。
  9. readonly 修饰符。只有构造函数可以修改它。防止构造函数以外对属性进行赋值。它只能修饰属性,不能修饰方法。还可以用在接口、对象类型中。

  10. 定义类的时候,所有属性和方法都要初始化,**新版编译器**在编译阶段会出错。要不初始赋值,要不在构造函数内赋值。

(二)抽象类

所谓抽象类,是指只能被继承,但不能被实例化的类,就这么简单。抽象类用一个 abstract 关键字来定义。抽象类有两个特点:

  1. 抽象类不允许被实例化。

  2. 抽象类中的抽象方法必须被子类实现。

    abstract class CPerson {
        name='';
        // 注意观察两种定义方法类型的方法,性质不一样,在继承类中实现的方法也不一样
        **abstract fn(): void; // 这是成员函数**
        **abstract fn2:()=>void  // 这是成员属性**
    }
    
    //const p1 = new CPerson;  // Error。抽象类不能直接实例化。
    
    class CMan extends CPerson { // OK.抽象类只能被继承
        // 必须实现抽象类的 abstract 成员
        constructor() {
            super()
        }
    		// 成员方法的实现方式,要用到**声明**方式
        fn(): void {
            
        }
    		// 成员属性的实现方式,要用**赋值**的方式
        fn2 = () => { }
    }
    

(三)类型兼容性

  1. 类的兼容性。如果两个类的成员结构完全一样,则认为他们两个是同一类型。属性名称和类型必须一样,属性值和方法体可以不一样。如果一个类A包含了另一个类B的所有属性和方法,那么可以认为 A 兼容 B,成员多的可以赋值给成员少的:

    class a {
        b: number;
        c() { };
    }
    class b {
        a: number;
        b: number;
        c(){}
    }
    // class b 的属性完全包含 a,所以可以兼容 a
    let a1: a = new b;
    
    // 以上代码在编码阶段,不会收到错误提示。但编译后会报错。因为有成员没有初始化。做如下修改:
    // class类型兼容性的演示
    class a {
        b: number = 1;
        c() { };
    }
    class b {
        a: number = 44;
        b: number =1 ;
        c(){console.log('d')}
    }
    // class b 的属性完全包含 a,所以可以兼容 a
    let a1: a = new b;
    console.log(a1)
    // 打印 a1 的结果如下:b { a: 44, b: 1 },类型 b 的值赋值给了 a,多的没有接受。
    
  2. 接口的兼容性。类似于类的兼容性,并且两者之间可以兼容。看以下代码:

    interface IF1 {
        name: string
        age: number
    }
    // 以上代码定义了接口IF1。使用两种方法使用它。
    // 第一种方法,等于号赋值,必须完全一致,属性的个数和类型必须完全一致。
    let person1: IF1 = { name: '', age: 4 }
    // 第二种方法:定义一个带IF1型接口参数的函数。
    function iffunc(p: IF1) {
    
    }
    // 单独定义一个对象,没有指定类型,这个包含了IF1参数的所有属性,并且多了一个height属性
    let pp = { name: '', age: 3, heigh: 3 }
    // 把他作为参数传递给函数,没有问题。
    iffunc(pp)  // OK
    // 直接传递一模一样的字面量对象,也是没问题的
    iffunc({ name: '', age: 10 })
    // 但是在字面量对象中增加一个属性,就不行了
    iffunc({ name: '', age: 10 ,height:30})
    // 但是,如果像这样调用,就会出错:Argument of type '{ name: string; age: number; heigh: number; }' is not assignable to parameter of type 'IF1'.
      Object literal may only specify known properties, and 'heigh' does not exist in type 'IF1'.
    iffunc({ name: '', age: 3, heigh: 3 }) // Error
    

    什么原因呢?网上找的解释:原来的变量的约束外属性还是可以被其他代码使用的, 但是如果是literal的话就因为完全不会被其他地方使用而让额外属性完全没有意义.因此,可以这样理解:第二种函数调用方法,其实也是一种赋值。

    官方的解释是:对象字面量会被特殊对待而且会经过_额外属性检查_,当将它们赋值给变量或作为参数传递的时候。 如果一个对象字面量存在任何“目标类型”不包含的属性时,你会得到一个错误。

    绕开这些检查非常简单。 最简便的方法是使用类型断言:

    iffunc(<IF1>{ name: '', age: 3, heigh: 3 }) // OK
    
  3. 函数参数的兼容性。两个函数,在返回值一样的情况下,如果一个函数a的参数完全包含另一个函数b的所有参数,那么b可以兼容a,也就是说函数a可以赋值给b。参数少的可以赋值给参数多的。

(四)type和interface的异同

1. 相同点

  • 都可以定义一个对象或函数
  • 都允许继承。type的继承方式是交叉类型,用&interface的继承方式是用extends

2. 不同点

  • interface(接口) 是 TS 设计出来用于定义对象类型的,可以对对象的形状进行描述。
  • type类型别名,用于给各种类型定义别名,让 TS 写起来更简洁、清晰。
  • type 可以声明基本类型、联合类型、交叉类型、元组,interface 不行。
  • interface可以合并重复声明,type 不行。

七、泛型

(一)泛型的概念和定义方法

  1. 泛型中的 T 就像一个占位符、或者说一个变量,也可以理解为类型变量动态类型。在定义使用泛型的类型的时候,把后期希望动态变化的类型用泛型类标注。在使用的时候,把需要使用的类型像参数一样传入,它可以原封不动地在使用该类型变量(泛型)的地方自动替换成需要的类型。也就是说,对需要固定的类型暂时不指定,一旦程序运行起来后就会自动固定。提供了较强的灵活性。

  2. 泛型的语法是 <> 里写类型参数,一般可以用 T 来表示,放在函数名、类型名和接口名之后。如下例,在函数名后面的<t>声明了泛型类型名 t,然后在参数、返回值等处应用了他。

  3. 对于一些对于多种类型数据都可以通用的功能,可以用泛型来定义一个类型变量,在实际使用该功能的时候再来确定泛型的具体类型,从而实现了类型的灵活性,又确保的类型的保护,提高功能的数据安全性和灵活性,实现功能的灵活复用。

  4. 泛型是可以在代码库中定义和重复使用的代码模板。 它们提供了一种方法,可用于指示函数、类或接口在调用时要使用的类型。 可以通过将参数传递给函数的方式来理解,不同之处是使用泛型可以指示组件在被调用时应该使用哪种类型。

    // 泛型声明一个类型变量,等待用户给他指定具体类型,他接受这个类型。
    // 1. 箭头函数的形式声明泛型函数,将类型变量放在参数前面
    const f1 = <T> (b:T):T => {
        return b
    }
    // 2. 普通声明方式,将类型变量放在函数名后面
    function f2<T>(a: T) {
        return a
    }
    
  5. 定义泛型的时候,也可以提供默认值,例如以下代码:

    // 定义以下接口的时候,为泛型 T提供了默认类型
    interface CPerson<T=number>{
    	name: string,
    	age:T
    }
    

(二)泛型的调用

调用泛型的时候,需要指定或者被 TS 自动推断出泛型的具体类型。例如以下函数的调用:

  1. 既可以使用 print<number>的方法显示指定泛型的具体类型;

  2. 也可以忽略具体类型的指定,通过传递合法的参数,让TS 自动推断出泛型t的具体类型为 number。一般情况下,可以省略这个具体类型指定。

    // 以下函数中,arg1 的类型可以随便给,arg2只能是 number
    function print<t>(arg1: t, arg2: number): t {
        console.log(`${arg1},${arg2}`)
        // 由于在函数声明的时候,返回值为泛型t,所以只能返回 arg1 类型的
        return arg1
    }
    // 以下调用语句,指定了泛型类型为 number,那么第一个参数必须为 number 类型,否则会报错。
    print<number>(4,3)
    // 以下语句中,r 的类型会随着 arg1 的类型而推论确定
    let r = print(2, 2)
    

这样,我们就做到了输入和输出的类型统一,且可以输入输出任何类型。如果类型不统一,就会报错。

(三)泛型约束

  1. 依靠外部约束泛型

    由于泛型的千变万化,所以在实际使用中有时候会因为这个变化带来烦恼。例如以下代码,有时候希望这个变量是有 length 这个属性的,但 TS 会报错,因为泛型的原因,不可能保证所有参数都有 length 属性。

    // 程序员想以字符串的形式调用这个参数的时候,会报错:
    function f1<T>(a:T){
    	return a.length
    }
    

    解决这个问题,用类型变量的收窄,可以用 interface 来解决:

    interface ILength {length:number}
    // 此处的extends是用来约束 T 的类型,不是继承的意思
    function f3<T extends ILength>(c:T):number {
        return c.length    
    }
    
  2. 泛型之间约束

    例如以下需求,需要获取某个变量的属性,keyof 关键字得到了 T 的所有属性名的联合类型,例如以下案例,keyof 得到一个联合类型是:name|age

    function getProp<T, key extends keyof T>(p:T,k:key){
        return p[k]
    }
    const prop1 = {
        name: '张景平',
        age: 43
    }
    getProp(prop1, 'name')
    getProp(prop1,'age')
    

(四)接口泛型

  1. 声明方法。在接口名后声明泛型,在泛型内部就可以使用它。
interface IPerson<T>{
	a: T,
	b: T
}
// 也可以进行泛型约束
interface IPerson<T,U extends keyof T>{
    a: T,
    b: T,
    c: U
}
  1. 使用方法。与其他泛型不同,例如使用了泛型的函数,使用泛型接口的时候,**必须显示指定泛型的类型,因为接口没有类型推断功能。**以下是定义和调用泛型接口的示例::
// 以下定义了一个泛型接口
interface IPerson<T,U extends keyof T>{
    a: T,
    b: T,
    c: U
}
// 以下两处使用泛型接口,都要显示指定泛型的类型
function f4(b: IPerson<string,number>) {
    
}
// 显示指定
let IPerson1: IPerson<string, number> = {
    a: 'heelo',
    b: 'heell',
    c: 3
}

(五)类型泛型

  1. 声明方法。在类名后加<>,类就成了泛型类;

  2. 使用方法。与泛型接口类似,一般情况下,在使用泛型类的时候也要显示指定泛型的类型。

  3. 具体使用泛型类的时候可以省略具体类型指定的情况。如果有构造函数,且构造函数的参数使用了泛型,那么可以在给构造函数传递参数的情况下,不用显示指定泛型的类型。

    // 定义一个泛型类,将泛型放在类名后面
    class cla<T>{
        a: T;
        b: T
        c = 8
        d = "string"
        e: (b: T) => T
        constructor(a:T,b:T) {
            this.a = a
            this.b = b
            this.e = (b:T) => b
        }
    }
    const cla1 = new cla(1,3);
    cla1.e(1)
    

(六)签名、映射

  1. 签名属性。表示不知道接口的属性有多少个,提供一个占位符。

    // 使用单一类型
    interface IKey<t> {
        [index:number]:t
    }
    const s:IKey<string> = "ddd"
    
    // 使用联合类型
    interface IKey<t> {
        [index:number]:t|number
    }
    const s:IKey<string> = [0,3,3,'33']
    
  2. 在 type 声明中,可以使用[]查询对象属性的类型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值