TypeScript基础入门之高级类型的类型保护与区分类型

转发node

TypeScript基础入门之高级类型的类型保护与区分类型

 

项目实践仓库git

https://github.com/durban89/typescript_demo.git
tag: 1.4.3

为了保证后面的学习演示须要安装下ts-node,这样后面的每一个操做都能直接运行看到输出的结果。程序员

npm install -D ts-node

后面本身在练习的时候能够这样使用github

npx ts-node 脚本路径

继续分享高级类型相关的基础知识,有人说我是抄官网的,我想说,有多少人把官网的例子从头看了一边而后又照着抄了一遍,虽然效率很慢,可是我在这个过程当中可以知道官网写例子跟说的话是否都是正确的,须要本身去验证下,咱们就是由于太多的照猫画虎、依葫芦画瓢致使咱们今天技术上没有太多的成就,我但愿你们在学习技术的时候,可以踏踏实实的学习一下,知识学到了是你本身的,尤为是写代码,不要让别人以为今天的程序员没有价值。
打个比方,有人以为写代码实现功能就能够了,可是我想说,这个是做为一个非技术公司的结果,但愿你们去技术型公司,否则老板今天让你改明天让你改,改到最后,你都不知道本身在作神马,并且你写的代码给谁看?写的东西就是垃圾,不说写的多漂亮,至少咱们能够对得起本身的花的时间,不要以为去网上找个例子就抄抄,写写东西就很牛了,其实里面的东西差的不仅是表面,这个年代,该让本身沉淀一下了,别太浮躁,如今不是战争年代,咱们要有更高的追求。废话很少说继续基础分享。typescript

高级类型

类型保护与区分类型(Type Guards and Differentiating Types)

从上一篇文章【TypeScript基础入门之高级类型的交叉类型和联合类型】的分享中咱们能够了解到,联合类型适合于那些值能够为不一样类型的状况。 但当咱们想确切地了解是否为某个类型时怎么办? JavaScript里经常使用来区分2个可能值的方法是检查成员是否存在。 如上一篇文章文章【TypeScript基础入门之高级类型的交叉类型和联合类型】中以前说起的,咱们只能访问联合类型中共同拥有的成员。以下实例,访问任何一个非公有成员,程序编译的时候都会报错npm

interface Type1 {
    func1(): void;
    func2(): void;
}

interface Type2 {
    func3(): void;
    func2(): void;
}

class Type1Class implements Type1 {
    func1(): void {
        console.log('func1 run');
    }

    func2(): void {
        console.log('func2 run');
    }
}

class Type2Class implements Type2 {
    func3(): void {
        console.log('func1 run');
    }

    func2(): void {
        console.log('func2 run');
    }
}

function getSomeType(type: string): Type1Class | Type2Class {
    if (type === '1') {
        return new Type1Class();
    }

    if (type === '2') {
        return new Type2Class();
    }

    throw new Error(`Excepted Type1Class or Type2Class, got ${type}`);
}

let type = getSomeType('1');
type.func2();
if (type.func1) {
    type.func1(); // 报错
} else if (type.func3) {
    type.func3(); // 报错
}


编译并运行后获得以下结果dom

$ tsc ./src/advanced_types_2.ts
src/advanced_types_2.ts:45:10 - error TS2551: Property 'func1' does not exist on type 'Type1Class | Type2Class'. Did you mean 'func2'?
  Property 'func1' does not exist on type 'Type2Class'.

45 if (type.func1) {
            ~~~~~

src/advanced_types_2.ts:46:10 - error TS2551: Property 'func1' does not exist on type 'Type1Class | Type2Class'. Did you mean 'func2'?
  Property 'func1' does not exist on type 'Type2Class'.

46     type.func1(); // 报错
            ~~~~~

src/advanced_types_2.ts:47:17 - error TS2551: Property 'func3' does not exist on type 'Type1Class | Type2Class'. Did you mean 'func2'?
  Property 'func3' does not exist on type 'Type1Class'.

47 } else if (type.func3) {
                   ~~~~~

src/advanced_types_2.ts:48:10 - error TS2551: Property 'func3' does not exist on type 'Type1Class | Type2Class'. Did you mean 'func2'?
  Property 'func3' does not exist on type 'Type1Class'.

48     type.func3(); // 报错

为了让这段代码工做,咱们要使用类型断言,以下:函数

interface Type1 {
    func1(): void;
    func2(): void;
}

interface Type2 {
    func3(): void;
    func2(): void;
}

class Type1Class implements Type1 {
    func1(): void {
        console.log('func1 run');
    }

    func2(): void {
        console.log('func2 run');
    }
}

class Type2Class implements Type2 {
    func3(): void {
        console.log('func1 run');
    }

    func2(): void {
        console.log('func2 run');
    }
}

function getSomeType(type: string): Type1Class | Type2Class {
    if (type === '1') {
        return new Type1Class();
    }

    if (type === '2') {
        return new Type2Class();
    }

    throw new Error(`Excepted Type1Class or Type2Class, got ${type}`);
}

let type = getSomeType('1');
type.func2();
if ((<Type1Class>type).func1) {
    (<Type1Class>type).func1();
} else if ((<Type2Class>type).func3) {
    (<Type2Class>type).func3();
}

编译并运行后获得以下结果学习

$ tsc ./src/advanced_types_2.ts && node ./src/advanced_types_2.js
func2 run
func1 run  

用户自定义的类型保护

这里能够注意到咱们不得很少次使用类型断言。 倘若咱们一旦检查过类型,就能在以后的每一个分支里清楚地知道let type = getSomeType('1')的类型的话就行了。this

TypeScript里的 类型保护机制让它成为了现实。 类型保护就是一些表达式,它们会在运行时检查以确保在某个做用域里的类型。 要定义一个类型保护,咱们只要简单地定义一个函数,它的返回值是一个"类型谓词",以下实例

function isType1(type: Type1Class | Type2Class): type is Type1Class {
    return (<Type1Class>type).func1 !== undefined;
}

在这个例子里,"type is Type1Class"就是类型谓词。 谓词为 parameterName is Type这种形式, parameterName必须是来自于当前函数签名里的一个参数名。

每当使用一些变量调用isType1时,若是原始类型兼容,TypeScript会将该变量缩小到该特定类型。以下

if(isType1(type)) {
    type.func1()
} else {
    type.func3();
}

注意意TypeScript不只知道在if分支里Type是Type1Class类型;它还清楚在else分支里,必定不是Type1Class类型,必定是Type2Class类型。

typeof类型保护

我以上篇文章[TypeScript基础入门之高级类型的交叉类型和联合类型]的padLeft代码的代码为例,看看如何使用联合类型来改写padLeft。 能够像下面这样利用类型断言来写:

function isNumber(x: any): x is number {
    return typeof x === "number";
}

function isString(x: any): x is string {
    return typeof x === "string";
}

function padLeft(value: string, padding: string | number) {
    if (isString(padding)) {
        return padding + value;
    }

    if (isNumber(padding)) {
        return Array(padding + 1).join(' ') + value;
    }

    throw new Error(`Excepted string or number, got ${padding}`);
}

console.log("|" + padLeft("string", 4) + "|");
console.log("|" + padLeft("string", "a") + "|");

编译并运行后获得以下结果

$ tsc ./src/advanced_types_2.ts && node ./src/advanced_types_2.js
|    string|
|astring|    

然而,必需要定义一个函数来判断类型是不是原始类型,这太痛苦了。 幸运的是,如今咱们没必要将typeof x === "number"抽象成一个函数,由于TypeScript能够将它识别为一个类型保护。 也就是说咱们能够直接在代码里检查类型了。代码和上篇文章是同样的,省去了定义函数的痛苦。

function padLeft(value: string, padding: string | number) {
    if (typeof padding === 'string') {
        return padding + value;
    }

    if (typeof padding === 'number') {
        return Array(padding + 1).join(' ') + value;
    }

    throw new Error(`Excepted string or number, got ${padding}`);
}

这些**typeof类型保护**只有两种形式能被识别:typeof v === "typename"和typeof v !== "typename", "typename"必须是"number","string","boolean"或"symbol"。 可是TypeScript并不会阻止你与其它字符串比较,语言不会把那些表达式识别为类型保护。


instanceof类型保护

instanceof类型保护是经过构造函数来细化类型的一种方式。 好比,咱们借鉴一下以前字符串填充的例子:

interface PadInterface {
    getPadString(): string;
}

class SpacePad implements PadInterface {
    constructor(private num: number){}
    getPadString(): string {
        return Array(this.num + 1).join(' ');
    }
}

class StringPad implements PadInterface {
    constructor(private string: string) { }
    getPadString(): string {
        return this.string;
    }
}

function getRandomPad() {
    return Math.random() < 0.5 ? 
    new SpacePad(5) :
    new StringPad(" ");
}

let pad: PadInterface = getRandomPad();
if (pad instanceof SpacePad) {
    console.log("|" + pad.getPadString() + "string|")
}

if (pad instanceof StringPad) {
    console.log("|" + pad + "string|")
}


第一次编译并运行后获得以下结果

$ tsc ./src/advanced_types_2.ts && node ./src/advanced_types_2.js
|     string|

第二次编译并运行后获得以下结果

$ tsc ./src/advanced_types_2.ts && node ./src/advanced_types_2.js
| string|

instanceof的右侧要求是一个构造函数,TypeScript将细化为:

  • 此构造函数的prototype属性的类型,若是它的类型不为any的话
  • 构造签名所返回的类型的联合

以此顺序。

本实例结束实践项目地址

https://github.com/durban89/typescript_demo.git
tag: 1.4.4
相关文章
相关标签/搜索