TypeScript----高级类型(第二篇)

本文详细介绍了TypeScript中的类型别名,对比了它与接口的区别,并展示了如何使用字符串文本类型和数字文本类型来限制参数。此外,还深入探讨了枚举成员类型和可辨识联合的概念,以及如何利用它们进行完整性检查。文章通过实例代码解释了这些概念在实际编程中的应用。
摘要由CSDN通过智能技术生成

目录

一、类型别名

1、接口VS类型别名

二、字符串文本类型

三、数字文本类型

四、枚举成员类型和可辨识联合

1、枚举成员类型

         2、可辨识联合

3、完整性检查


一、类型别名

类型别名会给一个类型起个新名字。类型别名有时和接口很像,但是可以作用于原始值,联合类型,元组以及其他任何你需要手写的类型。

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

 别名不会新建一个类型,它创建一个新名字来引用那个类型。给原始类型起别名通常没什么用,尽管可以作为文档的一种形式使用。

和借口一样,类型别名也可以是泛型-我们可以添加类型参数并且在别名声明的右侧传入:

type Container<T> = { value: T };

注意:类型别名可以引用自己,也可以于交叉类型一起使用,但是不能出现在声明右侧的位置。

 type Yikes = Array<Yikes>; 这是错误的。

1、接口VS类型别名

类型别名可以像接口一样,但是还有一些差别。

一个是,接口创建了一个新的名字,可以再其他任何地方使用,类型别名并不创建新名字,比如。错误信息就不会使用别名,举例:在下面的示例代码中,在编译器中将鼠标停在interfaced上,显示它返回的是Interface,但是悬停在aliased上时,现实的确实对象文本类型。

type Alias = { num: number }
interface Interface {
    num: number;
}
declare function aliased(arg: Alias): Alias;
declare function interfaced(arg: Interface): Interface;

另一个重要区别是类型别名不能被 extends和 implements(自己也不能 extends和 implements其它类型)。 因为软件中的对象应该对于扩展是开放的,但是对于修改是封闭的,你应该尽量去使用接口代替类型别名。

另一方面,如果你无法通过接口来描述一个类型并且需要使用联合类型或元组类型,这时通常会使用类型别名。

二、字符串文本类型

字符串文本类型允许你指定字符串必须的固定值。在实际应用中,字符串文本类型可以于联合类型,类型保护和类型别名很好的配合。通过结合使用这些特性,你可以实现类似枚举类型的字符串。

type Easing = "ease-in" | "ease-out" | "ease-in-out";
class UIElement {
    animate(dx: number, dy: number, easing: Easing) {
        if (easing === "ease-in") {
            // ...
        }
        else if (easing === "ease-out") {
        }
        else if (easing === "ease-in-out") {
        }
        else {
            // error! should not pass null or undefined.
        }
    }
}

let button = new UIElement();
button.animate(0, 0, "ease-in");
button.animate(0, 0, "uneasy"); // error: "uneasy" is not allowed here

你只能从三种允许的字符中选择其一来做为参数传递,传入其它值则会产生错误。

function createElement(tagName: "img"): HTMLImageElement;
function createElement(tagName: "input"): HTMLInputElement;
// ... more overloads ...
function createElement(tagName: string): Element {
    // ... code goes here ...
}

三、数字文本类型

function rollDie(): 1 | 2 | 3 | 4 | 5 | 6 {
    // ...
}

我们很少直接这样使用,但它们可以用在缩小范围调试bug的时候:

function foo(x: number) {
    if (x !== 1 || x !== 2) {
        //         ~~~~~~~
        // Operator '!==' cannot be applied to types '1' and '2'.
    }
}

换句话说,当 x与 2进行比较的时候,它的值必须为 1,这就意味着上面的比较检查是非法的。 

四、枚举成员类型和可辨识联合

1、枚举成员类型

当每个枚举成员都是用文本初始化的时候枚举成员是具有类型的。

2、可辨识联合

你可以合并单例类型,联合类型,类型保护和类型别名来创建一个叫做可辨识联合的高级模式,它也称做标签联合或代数数据类型。 可辨识联合在函数式编程很有用处。 一些语言会自动地为你辨识联合;而TypeScript则基于已有的JavaScript模式。 它具有3个要素:

  1. 具有普通的单例类型属性— 可辨识的特征
  2. 一个类型别名包含了那些类型的联合— 联合
  3. 此属性上的类型保护。
interface Square {
    kind: "square";
    size: number;
}
interface Rectangle {
    kind: "rectangle";
    width: number;
    height: number;
}
interface Circle {
    kind: "circle";
    radius: number;
}

首先我们声明了将要联合的接口,每个接口都有kind属性但有不同的字符串文本类型,kind属性称作可辨识的特征和标签,其他的属性则特定于各个接口。注意,目前各个接口之间是没有联系。下面我们把他们联合到一起:

type Shape = Square | Rectangle | Circle;

现在我们使用可辨识联合:

function area(s: Shape) {
    switch (s.kind) {
        case "square": return s.size * s.size;
        case "rectangle": return s.height * s.width;
        case "circle": return Math.PI * s.radius ** 2;
    }
}

3、完整性检查

当没有涵盖所有可辨识联合的变化时,我们想让编译器可以通知我们,比如,如果我们添加了Triangle到shape,我们同时需要更新area:

type Shape = Square | Rectangle | Circle | Triangle;
function area(s:Shape) {
    switch (s.kind){
        case "square": return s.size * s.size;
        case "rectangle": return s.height* s.width;
        case "circle": return Math.PI * s.radiue **2;
    }
//应该出错-我们没有处理“三角形”情况  
}

有两种方式可以实现。 首先是启用 --strictNullChecks并且指定一个返回值类型:

function area(s: Shape): number { // 错误:返回 number|未定义
    switch (s.kind) {
        case "square": return s.size * s.size;
        case "rectangle": return s.height * s.width;
        case "circle": return Math.PI * s.radius ** 2;
    }
}

因为 switch没有包涵所有情况,所以TypeScript认为这个函数有时候会返回 undefined。 如果你明确地指定了返回值类型为 number,那么你会看到一个错误,因为实际上返回值的类型为 number | undefined。 然而,这种方法存在些微妙之处且 --strictNullChecks对旧代码支持不好。

第二种方法使用 never类型,编译器用它来进行完整性检查:

function assertNever(x: never): never {
    throw new Error("Unexpected object: " + x);
}
function area(s: Shape) {
    switch (s.kind) {
        case "square": return s.size * s.size;
        case "rectangle": return s.height * s.width;
        case "circle": return Math.PI * s.radius ** 2;
        default: return assertNever(s); // 如果缺少案例,此处错误
    }
}

这里, assertNever检查 s是否为 never类型—即为除去所有可能情况后剩下的类型。 如果你忘记了某个case,那么 s将具有一个真实的类型并且你会得到一个错误。 这种方式需要你定义一个额外的函数,但是在你忘记某个case的时候也更加明显。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是小先生

知识是无价的,白嫖也可以的。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值