TypeScript三:接口和高级类型

接口定义 Interface

接口:对复杂的对象类型进行标注的一种方式,接口是一种类型 ,不能作为值使用。

基础语法定义结构
interface Point {
  x: number; 
  y: number;
}
let p1 = Point; //错误
let p1: Point = { x: 100, y: 100 };
可选属性
interface Point { 
  x: number; 
  y: number;
  color?: string; //该属性是可选的
}
只读属性
interface Point { 
  readonly x: number;//除了初始化以外,不能被再次赋值
  readonly y: number; //除了初始化以外,不能被再次赋值
}
任意属性

索引只可以是数字或字符串

若同时存在,数字类型的值类型必须是字符串类型的值类型或子类型

interface Point1 {
  [prop1: string]: string;
  [prop2: number]: number; // 错误 
}
interface Point2 { 
  [prop1: string]: Object;
  [prop2: number]: Date; // 正确 
}

使用接口描述函数

interface IFunc {
    (x: number, y: number): number
}
let fn1: IFunc = function(a: number, b: number): number {
    return a + b;
}
let fn12: IFunc = function(a: number, b: number): number {
    return a + b;
}

接口合并

相同命名的接口会自动合并

如果存在同名的非函数成员,则必须保证他们类型一致,否则编译报错

接口中的同名函数则是采用重载。

interface Box {
    height: number;
    width: number;
    fn(a: string): string;
}

interface Box {
    height: number;
    scale: number;
    // width: string; //若放开,会因为和上一个interface定义不一致而报错
    fn(a: number): number;
}

let box: Box = {
    height: 5,
    width: 6, 
    scale: 10,
    fn: function(a:any):any {
        return a;
    },
}

联合类型

也可以称为多选类型,当我们希望标注一个变量为多个类型之一时可以选择联合类型标注,或的关系

function fn(value: string | number) {
    return value
}
fn(1)
fn('1')
fn(true)//error

交叉类型

也可以称为合并类型,可以把多种类型合并到一起成为一种新的类型,并且的关系

interface o1 { x: number; y: string }
interface o2 { z: number }
let obj1: o1 & o2 = { x: 1, y: 'zmouse' } //error
let obj2: o1 & o2 = { x: 1, y: 'zmouse', z: 2 }

字面量类型

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

function fn(direction: 'left' | 'right') {
    return direction
}
fn('left')
fn('right')
fn('top')//error

类型别名type

有的时候类型标注比较复杂,这个时候我们可以类型标注起一个相对简单的名字

type direction = 'left' | 'right'
function fn(direction: direction) {
    return direction
}
使用类型别名定义函数类型
type callback = (a: string) => string;
let fn1: callback = (a) => a;
// 或者直接
let fn2: (a: string) => string = (a) => a;

interface type 的区别

interface

只能描述 object / class / function 的类型

同名 interface 自动合并,利于扩展

type

不能重名

能描述所有数据

类型断言

有的时候,我们可能标注一个更加精确的类型,或者确定某个元素类型,以使用某些属性。

img 的默认推断类型为 Element,只是元素类型的通用类型,如果我们去访问 src 这个属性是有问题的。

我们需要把它的类型标注得更为精确:HTMLImageElement 类型,这个时候,我们就可以使用类型断言,

它类似于一种类型转换,但只是一种预判,并不会数据本身产生实际的作用
let img = document.querySelector('#img');
img && img.src//类型“Element”上不存在属性“src”
//修改方案一
let img = document.querySelector('#img'); 
img && (<HTMLImageElement>img).src;
(img as HTMLImageElement).src;
//修改方案二
let img = <HTMLImageElement>document.querySelector('#img'); 
let img = document.querySelector('#img') as HTMLImageElement;
img && img.src

类型操作

typeof

在 TypeScript 中,typeof获取数据的类型,也可以用来生成类型标注。

let str1 = 'TypeScript';

// 获取数据类型,程序运行时也存在
let t = typeof str1;

// type 声明的是类型名称,只是在ts编译检测阶段使用
type myType = typeof str1;

let a = t;
let b: myType = 'TypeScript2';

keyof

interface Person {
    name: string;
    age: number;
};

type personKeys = keyof Person;// 等同:type personKeys = "name" | "age"


let p1:Person = {
    name: 'zMouse',
    age: 35
}

function getPersonVal(k: keyof typeof p1) {
    return p1[k];
}

in

interface Person {
    name: string;
    age: number;
}
type personKeys = keyof Person;
type newPerson = {
    [k in personKeys]: string
    // 等同 [k in 'name'|'age']: number;
    // 也可以写成
    // [k in keyof Person]: number;
}
// 等于是
// type newPerson = {
//     name: number;
//     age: number;
// }

自定义类型保护

如下情况会出现报错
function fn2(elements: number[] | number) {
    if (elements.length) { //类型“Element”上不存在属性“length”
        elements.forEach((el: number) => {
            console.log(el);
        });
    } else {
        console.log(elements);
    }
}

可以增加一个自定义类型保护函数,格式为: xx is XX

function canEach(data: any): data is number[] {
    return data.forEach !== undefined;
}

function fn2(elements: number[] | number) {
    if (canEach(elements)) {//使用自定义保护函数
        elements.forEach((el: number) => {
            console.log(el);
        });
    } else {
        console.log(elements);
    }
}

类型兼容

TypeScript 的类型系统是基于结构⼦类型的,只要具有相同子类型的成员,则两种类型即为兼容的。

class Person {
    name: string;
    age: number;
}
class Cat {
    name: string;
    age: number;
}
function fn(p: Person) {
    p.name;
}
let xiaohua = new Cat();

fn(xiaohua);// ok,因为 Cat 类型的结构与 Person 类型的结构相似,所以它们是兼容的
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容