深入理解Typescript系列-高级类型

这是我参与 8 月更文挑战的第 16 天,活动详情查看: 8月更文挑战

前言

在之前的文章中,我们讲述了TS的基础类型,TS除了JS中提供的数据类型外,还额外补充了如enum、never、void等类型。本章节,我们将来探索TS中的高级类型。

交叉类型

交叉类型是将多个类型合并为一个类型。 比如在抽象对interface的抽象颗极度比较小的时候,我们在实现一个对象的时候需要用到混入(mixins)的模式

function extend<T, U>(first: T, second: U): T & U {
    let result = <T & U>{};
    for (let id in first) {
        (<any>result)[id] = (<any>first)[id];
    }
    for (let id in second) {
        if (!result.hasOwnProperty(id)) {
            (<any>result)[id] = (<any>second)[id];
        }
    }
    return result;
}

class Person {
    constructor(public name: string) { }
}
interface Loggable {
    log(): void;
}
class ConsoleLogger implements Loggable {
    log() {
        // ...
    }
}
var jim = extend(new Person("Jim"), new ConsoleLogger());
var n = jim.name;
jim.log();
复制代码

这是官网的例子,在调用extend后,把ConsoleLogger类混入了Person类中。

联合类型

联合类型和我们刚刚提到的交叉类型有很多相似之处,但是在使用方法上却是不同的。

通常我们在写Vue或者React的时候,有控制的css的诉求,遇到一个宽度,我们可以用数字类型,也可以用字符串类型,比如这样:

function setWidth(id:string, width: any) {
    if (typeof width === 'string') {
        return `${id}: ${width}`
    }
    if (typeof width === 'number') {
        return `${id}: ${width}px`
    }
    return new Error('error')
}
复制代码

在上述代码中,有一个问题,width的类型是不确定的,比如我们传一个对象,是应该报错了,但是写成any是无法在TS检查阶段报错的,所以我们用联合类型做一下改造,如下:

function setWidth(id:string, width: string | number) {
    if (typeof width === 'string') {
        return `${id}: ${width}`
    }
    return `${id}: ${width}px` // 必然是number类型
}
复制代码

因为传入的类型的被确定只有number和string,所以判断也可以有所减少。

联合类型表示一个值可以是几种类型之一。 我们用竖线( |)分隔每个类型,所以 number | string | boolean表示一个值可以是 number, string,或 boolean。

类型保护与区分类型

在拥有了联合类型后,也带来的了一些新问题,因为对传入值类型的不确定,需要对值先做判断后做操作。

使用类型断言机制等于显式告诉TS检查器,我很清楚这里的类型是什么,请相信我。

let pet: Fish | Bird = getSmallPet();

if ((<Fish>pet).swim) { 
    (<Fish>pet).swim();
}
else {
    (<Bird>pet).fly();
}
复制代码

在JS中,有提供typeof、instanceof等关键词做类型判断,同样,在TS中,可以使用这些关键字做类型的保护。

类型别名

类型别名可以让我们有一些更加贴近业务语义的类型定义,就像给一个类型起个新名字。和接口很像,但是可以作用于原始值,联合类型,元组以及其它任何你需要手写的类型。我们看下具体的代码:

type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
    if (typeof n === 'string') {
        return n;
    }
    else {
        return n();
    }
}
复制代码

类型别名和接口的使用方式非常相似,对于新手,能用接口的地方用接口,不能用接口的地方用类型别名。

小结

本章主要介绍了交叉类型、联合类型等高级类型,还讲述了如何做类型保护与区分类型,在实际开发中,我们可以根据TS提供的这些特性做灵活的使用,功能特性之间也有很多相似之处,我们需要结合实际的业务场景,做合理的运用。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享