高级类型

交叉类型

交叉类型,就是将多个类型合并为一个新的类型,这个新的类型具有这多个类型的成员,含有这几个类型的所有特性,是他们的综合体,像是集合的并集

例子:

function extend<T,U>(first: T, second: U): T&U {
let result = <T & U>{};
for (let id in first) {
(<any>result)[id] = first[id];
}
for (let id in second) {
if (!result.hasOwnProperty(id)) {
(<any>result)[id] = second[id];
}
}
return result;
} class Person {
constructor(public name: string) {
}
} interface Loggable {
log(): void;
} class myLoggable implements Loggable {
log() {
console.log('qwe');
}
} let jim = extend(new Person('qq'), new myLoggable());
console.log(jim.name);
jim.log();

例子中jim有Person中的name属性也有myLoggable中的log()方法

联合类型

联合类型,不像是交叉类型是多个类型的合集,表示是这多个类型中的一种类型,像是集合中的交集,只有多个类型中共有的特性才可以被调用

例如要向一个函数传递参数,这个参数可能是number也可能是string

function padLeft(value: string, padding: any) {
if (typeof padding === "number") {
return Array(padding + 1).join(" ") + value;
}
if (typeof padding === "string") {
return padding + value;
}
throw new Error(`Expected string or number, got '${padding}'.`);
} padLeft("Hello world", 4); // " Hello world"

这里存在一个问题,将padding定义为any,表示我们可以传递任何值给padding,这个错误TypeScript是不会报错的,只有在编译时才会报错

解决这个问题,可以采用联合类型,用竖线分割每个类型,表示是这几个类型中的一种

function padLeft(value: string, padding: string | number) {
........
}
let f = padLeft("Hello world", true); // error

如果一个值是联合类型,就只能访问这多个类型所共有的成员

interface Bird {
fly();
layEggs();
} interface Fish {
swim();
layEggs();
} function getSmallPet(): Fish | Bird {
...
} let pet = getSmallPet();
pet.layEggs(); // okay
pet.swim(); // errors

getSmallPet的返回类型就是一个联合类型,所以pet就只能访问Bird和Fish的共有成员layEggs()

在上面的例子中,我们不知道pet到底是那种类型,所以就不可能去访问哪些不是公共的成员,如果我们知道了pet的类型,就可以访问该类型的所有成员了

类型保护和区分类型

为了解决上面提到的具体类型的确定问题,需要引入类型断言(类型转换)

let pet = getSmallPet();

if ((<Fish>pet).swim) {
(<Fish>pet).swim();
} else {
(<Bird>pet).fly();
}

问题显而易见,每次都需要对pet进行类型转换,麻烦

用户自定义的类型保护

类型保护就可以解决上述每次都要进行类型断言的缺点,类型保护就是一些表达式,它们会在运行时检查以确保在某个作用域里的类型。 要定义一个类型保护,我们只要简单地定义一个函数,它的返回值是一个类型断言

function isFish(pet: Fish | Bird): pet is Fish {
return (<Fish>pet).swim !== undefined;
}

pet is Fish就是类型断言。 一个断言是 parameterName is Type这种形式,parameterName必须是来自于当前函数签名里的一个参数名。

// 'swim' 和 'fly' 调用都没有问题了

if (isFish(pet)) {
pet.swim();
}
else {
pet.fly();
}

TypeScript不仅知道在if里是Fish,而且还知道在else里是Bird类型

 

typeof类型保护

我们将之前的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 (isNumber(padding)) {
return Array(padding + 1).join(" ") + value;
}
if (isString(padding)) {
return padding + value;
}
throw new Error(`Expected string or number, got '${padding}'.`);
}

如果要按这样写就要为每个原始类型写一个函数,麻烦。TypeScript会把"typeof v === typeofname"和"typeof v !== typeofname"看做是类型保护,所以就不必为一个原始类型写一个函数,直接用typeof就可以了

function padLeft(value: string, padding: string | number) {
if (typeof padding === "number") {
return Array(padding + 1).join(" ") + value;
}
if (typeof padding === "string") {
return padding + value;
}
throw new Error(`Expected string or number, got '${padding}'.`);
}

instanceof类型保护

instanceof类型保护是通过构造函数来细化类型的一种方式

interface Padder {
getPaddingString(): string
} class SpaceRepeatingPadder implements Padder {
constructor(private numSpaces: number) { }
getPaddingString() {
return Array(this.numSpaces + 1).join(" ");
}
} class StringPadder implements Padder {
constructor(private value: string) { }
getPaddingString() {
return this.value;
}
} function getRandomPadder() {
return Math.random() < 0.5 ?
new SpaceRepeatingPadder(4) :
new StringPadder(" ");
} // 类型为SpaceRepeatingPadder | StringPadder
let padder: Padder = getRandomPadder(); if (padder instanceof SpaceRepeatingPadder) {
padder; // 类型细化为'SpaceRepeatingPadder'
}
if (padder instanceof StringPadder) {
padder; // 类型细化为'StringPadder'
}

类型别名

类型别名就是给类型起一个别名,而且可以用于基础的数据类型

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 Alias = { num: number }
interface Interface {
num: number;
}
declare function aliased(arg: Alias): Alias;
declare function interfaced(arg: Interface): Interface;

在上面的代码中interfaced返回值的类型是Interface,而aliased返回值的类型是对象字面量

类型别名与接口还有一个地方不同的是类型别名不可以被extends和implements

上面说了类型别名与接口的两个不同点,当然也有相同点,即都可以使用泛型

type Tree<T> = {
value: T;
left: Tree<T>;
right: Tree<T>;
}

字符串字面量类型

字符串字面量类型可以与联合类型,类型保护和类型别名很好的配合

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

只能从规定的三种类型中选择一种类型进行传递,其他的类型会报错

可辨识联合

可以合并字符串字面量类型,联合类型,类型保护和类型别名来创建一个叫做可辨识联合的高级模式

先定义三个将要联合的接口,每个接口都有kind属性,但是值不同,king属性可以作为可辨识的特征和标识

interface Square {
kind: "square";
size: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
interface Circle {
kind: "circle";
radius: number;
}

然后将他们联合到一起

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;
}
}

注意:如果在Shape中添加新的类型,那么在switch下也要添加相应的判断

参考资料:

TypeScript中文网 · TypeScript——JavaScript的超集

TypeScript入门-高级类型的更多相关文章

  1. 为vue3.0学点typescript, 解读高级类型

    知识点摘要 本节课主要关键词为: 自动类型推断 / 类型断言 / 类型别名(type) / 映射类型(Pick/Record等...) / 条件类型(extends) / 类型推断(infer) 自动 ...

  2. 学习笔记:TypeScript入门——基础类型

    前言: TypeScript官网断断续续看过几遍,不知道项目中如何使用,有机会还是要实践一下.现在再把文档上不懂的知识点理一遍. 基础类型 1.什么是元组Tuple? 元组类型允许表示一个已知元素数量 ...

  3. TypeScript 素描 - 高级类型、迭代器

    /* 交叉类型,在TypeScrpt中是很特有的.所以值得认真学习 交叉类型是将多个类型合并为一个类型,这让我们可以把现有的多种类型叠加到一起成为一种 类型 交叉类型同时拥有 Person 和 Emp ...

  4. TypeScript 之 基础类型、高级类型

    基础类型:https://m.runoob.com/manual/gitbook/TypeScript/_book/doc/handbook/Basic%20Types.html 高级类型:https ...

  5. C# vs TypeScript - 高级类型

    总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...

  6. 从C#到TypeScript - 高级类型

    C# vs TypeScript - 高级类型 上一篇讲了基础类型,基本上用基础类型足够开发了,不过如果要更高效的开发,还是要看下高级类型,这篇和C#共同点并不多,只是延用这个主题. 联合类型 可以从 ...

  7. Typescript高级类型与泛型难点详解

    最近做的TS分享,到了高级类型这一块.通过琢磨和实验还是挖掘出了一些深层的东西,在此处做一下记录,也分享给各位热爱前端的小伙伴.   其实在学习TS之前就要明确以下几点:   1. typescrip ...

  8. typescript枚举,类型推论,类型兼容性,高级类型,Symbols(学习笔记非干货)

    枚举部分 Enumeration part 使用枚举我们可以定义一些有名字的数字常量. 枚举通过 enum关键字来定义. Using enumerations, we can define some ...

  9. TypeScript完全解读(26课时)_12.TypeScript完全解读-高级类型(1)

    12.TypeScript完全解读-高级类型(1) 高级类型中文网的地址:https://typescript.bootcss.com/advanced-types.html 创建新的测试文件 ind ...

随机推荐

  1. PHP随机生成随机个数的字母组合示例

    在很多系统环境下大家都会用到字母组合各种编码,下面推荐大家非常实用的PHP代码. $num由几个字母组合. $s字母包含大小写,可以自己调配大写还小写. <?php function makec ...

  2. 适用MySQL Migration Toolkit 1.0 将oracle迁移到mysql中遇到的问题

    这里主要说一下我在适用中碰到的问题,主要过程参考 http://www.cnblogs.com/duwenlei/p/3520759.html. 首先启动MySQLMigrationTool.exe ...

  3. 免安装版Tomcat配置内存

    去安装目录下 找 bin 目录找到这个文件 catalina.bat在文件的头部加上 set JAVA_OPTS=-Xms512m -Xmx512m -Xss1024k具体大小自己调整

  4. taobao-pamirs-proxycache开源缓存代理框架实现原理剖析

    写在前面 taobao-pamirs-proxycache 是一款开源缓存代理框架, 它将 缓存代码 与 业务代码 解耦.让开发专注coding业务, 缓存通过xml配置即可实现.本文先从此工具如何使 ...

  5. 学习Jammendo代码的心路历程(二)ViewFlipper数据的填充

    打开Jammendo进入到首页之后,会看到这样一个界面.可以看到下左效果,我们可以看到,他是上部分的ViewFlipper模块和下半部分的listview模块构成的,今天就简单的说一下Jammendo ...

  6. Kickstart无人值守安装系统

    1.导言 已经或未来将从事Linux系统运维工作的读者,经常会遇到一些机器式的重复的共走,例如:有时间同时上线几十甚至上百台服务器,而且需要我们在短时间内完成系统安装. q  光盘安装系统===> ...

  7. 如何使用LightningChart拖放功能进行数据转移 ?

    本文主要介绍如何使用LightningChart扩展拖放功能为所有图表组件创建图表,如:系列,标题,轴线等等.支持用鼠标放置自定义对象到另一个图表中,如:可以添加或修改JSON/CSV或其他格式的数据 ...

  8. 老李推荐: 第3章2节《MonkeyRunner源码剖析》脚本编写示例: MonkeyDevice API使用示例 4

    第七步:保存新增加日记 代码3-2-7 增加日记-保存日记 #Step7: Save the note by touch on the "save" menu entry by c ...

  9. JavaWeb总结(十)—文件上传和下载

    一.文件的上传 1.文件的基本上传 对于文件上传,浏览器在上传的过程中是将文件以流的形式提交到服务器端的,如果直接使用Servlet获取上传文件的输入流然后再解析里面的请求参数是比较麻烦,所以一般选择 ...

  10. C++11 新特性总结

    前言 转载请注明出处,感谢! C++11 的新特性 1 变量和基本类型 1.1 long long 类型 扩展精度浮点数,10位有效数字 1.2 列表初始化 初始化的几种不同形式,其中用花括号来初始化 ...