TypeScript基础知识点集合(二)

126 阅读4分钟

TS的核心之一就是对数据所具有的结构进行类型检查,除了一些上篇提到的基本的类型标注,针对对象类型,还可以使用接口interface进行标注。

而且,TS除了一些基础的类型之外,还有一些高级的类型。

本篇就针对 接口interface 和 高级类型 进行讨论。

接口 interface

接口:对复杂的对象类型进行标注的一种方式 或者 给其他代码定义一种契约(比如:类)。

interface Obj {

    x: number;
    
    y: number;
    
};

let obj: Obj = {

    x: 100,

    y: 100,

};

注意:接口是一种类型,不能作为值使用。

可选属性

接口中可以定义属性是否可选,通过?进行标注。

interface Obj {

    x: number;

    y: number;

    color?: string;

}

只读属性

只读属性readonly可以标注属性为只读,标注只读之后,除了初始化都不能给该属性赋值。

interface Obj {

    readonly x: number;

    readonly y: number;

}

任意属性

我们可以通过需求给接口添加任意的属性,可以通过索引类型来实现。

数字类型索引
interface Obj {

    x: number;

    y: number;

    [prop: number]: number;

}
字符串类型索引
interface Obj {

    x: number;

    y: number;

    color?: number;

    [prop: string]: number | undefined;

}
数字类型索引是字符串类型索引的子类型

索引只能是数字或者是字符串,两者也可以同时出现。(数字类型索引中的值类型必须要是要么与字符串类型索引一致,要么是字符串类型索引的子类型)

interface Obj {

    [prop1: string]: string;

    [prop2: number]: string;

}

使用接口interface描述函数

比如:

interface IFunc {

    (a: string, b: number): string;

}

let fn: IFunc = function(x: string, y: number): string {

    return x + y;

};

function todo(callback: IFunc) {
    // ....
    let v = callback('1', 2);
    // ....
}

todo(function(a: string, b: number): string {

    return a + b;

});

interface IEventFunc {

    (e: MouseEvent): void

};

function on(el: HTMLElement, evname: string, callback: IEventFUnc) {

};

let div = document.querySelector("div");

if(div) {

    on(div, 'click', function(e) {});

}

接口合并

接口合并 即 多个同名的接口 合并成 一个接口。

  • 如果合并的接口存在同名的非函数成员,则必须保证他们类型一致,否则编译报错;
  • 接口中的同名函数则是采用重载
interface Box {

    height: number;

    width: number;

    fn(a: string): string;

};

interface Box {

    scale: number;

    fn(a: number): number;

};

let box: Box = {

    height: 5,

    width: 6,

    scale: 10,

    fn: function(a: any): any {

        return a;

    },

};

高级类型

联合类型

联合类型可以实现让我们选择多个类型的其中一种。(= 多选类型)

function css(ele: Element, attr: string, value: string | number) {
    // ...
}

let box = document.querySelector('.box');

// document.querySelector 方法返回值就是一个联合类型

if(box) {
    // ts 会提示有 null 的可能性,加上判断更严谨
    css(box, 'width', '100px');
    css(box, 'opacity', 1);
}

交叉类型

交叉类型可以把多种类型合并在一起拼成一种新的类型。(= 合并类型)

interface o1 {

    x: number,

    y: string,

};

interface o2 {

    z: number,

};

let o: o1 & o2 = Object.assign({}, {x: 1, y: '2'}, {z: 100});

字面量类型

有的时候,我们希望标注的不是某个类型,而是一个固定值,就可以使用字面量类型,配合联合类型会更有用。

function setPosition(ele: Element, direction: 'left' | 'top' | 'right' | 'bottom') {}

// ok
box && setPosition(box, 'bottom');

// error
box && setPosition(box, 'here');

类型别名 type

相对于比较复杂的类型标注,我们可以用类型别名type对其进行标注,起一个简单的名字,然后再应用到其他地方。

type dir = 'left' | 'top' | 'right' | 'bottom';

function setPosition(ele: Element, direction: dir) {}
使用类型别名 type 定义函数类型
type callback = (a: string) => string;

let fn: callback = function(a) {};

// 或者直接可以表示成
let fn: (a: string) => string = function(a) {};
interface 和 tyep 的区别

interface只能描述object | class | function三种类型,且同名的interface可以合并。

而,type可以描述所有的类型数据,但是不能够重名。

类型推导

TS不需要每次都显式标注类型,可以通过类型推导根据上下文推导出相对应的类型。

// 自动推断 x 为 number
let x = 1;
// 不能将类型“"a""分配给类型"number"
x = "a";

// 函数参数类型、函数返回值会根据对应的默认值和返回值进行自动推断
function fn(a = 1) {

    return a * a; // number

}

类型断言

有时候,我们标注的类型不够精确,不能使用它们的一些属性和方法,需要通过类型断言精确类型,这种类型断言,类似于一种类型转换。比如,

let img = document.querySelector("#img");

img类型被限制在Element中,不能访问src属性,我们需要标注的更为精确:HTMLImageELement类型,这个时候需要类型断言,它类似于一种类型转换:

let img = <HTMLImageElement>document.querySelector("#img");

或者

let img = document.querySelector("#img") as HTMLImageElement;

注意: 断言只是一种预判,并不会给数据本身产生实际的作用,即:类似转换,但并非真的转换了