高级类型

交叉类型

把多个类型合并成一个大而全的类型, 运算符为 &

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

interface obj2 {
  addr: string,
  fav: string[]
}

type obj = obj1 & obj2
/**
 * {
 *  name: string
 *  age: number
 *  addr: string
 *  fav: string[]
 * }
*/

联合类型

多个类型的"或"关系, 表示一个值可以是几种类型之一, 运算符为 |

type UnionType = string | number

const num: UnionType = 123 // OK
const str: UnionType = 'I am string' // OK

类型保护

why: 联合类型带了一个问题, 即我们只能访问此联合类型的所有类型里共有的成员

interface Bird {
    fly();
    layEggs();
}

interface Fish {
    swim();
    layEggs();
}

function getSmallPet(): Fish | Bird {
    // ...
}

let pet = getSmallPet();
pet.layEggs(); // okay
pet.swim();    // errors

这很好理解, 因为不知道用户调用时会用到具体哪个类型,所以规定只能使用共同成员.

你可能会说我们可以用 if 条件判断啊. 是的,可以!

let pet = getSmallPet();

// 每一个成员访问都会报错
if (pet.swim) {
    pet.swim();
}
else if (pet.fly) {
    pet.fly();
}

正确姿势是使用类型断言:

let pet = getSmallPet();

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

既然有了 if 的类型判断, else 里面是不是可以省略了? 这就用到类型保护.

自定义类型保护

类型保护就是一些表达式, 它们会在运行时检查以确保在某个作用域里的类型. 要定义一个类型保护, 我们只要简单地定义一个函数, 的返回值是一个 类型谓词:

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

typeof 类型保护

类型保护虽然香, 但要多写一个判断函数太费力了, 况且我们平时用到的联合类型大多是基本数据类型. 幸运的是, 现在我们不必将 typeof x === "number" 抽象成一个函数, 因为TypeScript 可以将它识别为一个类型保护.

function getLength(value: string | number): number {
  if (typeof value === 'string') return value.length
  else return value.toString().length
}

这些 typeof类型保护只有两种形式能被识别:

  • typeof v === "typename"
  • typeof v !== "typename"

typename 必须是

  • number
  • string
  • boolean
  • symbol

但是TypeScript 并不会阻止你与其它字符串比较, 语言不会把那些表达式识别为类型保护.

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

null 类型保护

由于可以为 null 的类型是通过联合类型实现, 那么你需要使用类型保护来去除 null. 幸运地是这与在JavaScript 里写的代码一致:

function f(sn: string | null): string {
    if (sn == null) {
        return "default";
    } else {
        return sn;
    }
}

这里很明显地去除了 null, 你也可以使用短路运算符:

function f(sn: string | null): string {
    return sn || "default";
}

如果编译器不能够去除 nullundefined, 你可以使用类型断言手动去除. 语法是添加 ! 后缀: identifier!identifier 的类型里去除了 nullundefined:

// ts报错
function broken(name: string | null): string {
  function postfix(epithet: string) {
    return name.charAt(0) + '.  the ' + epithet; // error, 'name' is possibly null
  }
  name = name || "Bob";
  return postfix("great");
}
// 修改后
function fixed(name: string | null): string {
  function postfix(epithet: string) {
    return name!.charAt(0) + '.  the ' + epithet; // ok
  }
  name = name || "Bob";
  return postfix("great");
}

枚举成员类型

当每个枚举成员都是用字面量初始化的时候枚举成员是具有类型的.

索引类型

通过 索引类型查询索引访问操作符

function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
  return names.map(n => o[n]);
}

interface Person {
    name: string;
    age: number;
}
let person: Person = {
    name: 'Jarid',
    age: 35
};
let strings: string[] = pluck(person, ['name']); // ok, string[]

keyof T 索引类型查询操作符

对于任何类型 T, keyof T 的结果为 T 上已知的公共属性名的联合. 例如:

let personProps: keyof Person; // 'name' | 'age'

T[K] 索引访问操作符

这里, 类型语法反映了表达式语法. 这意味着 person['name'] 具有类型 Person['name'] — 在我们的例子里则为 string类型. 然而, 就像索引类型查询一样, 你可以在普通的上下文里使用 T[K], 这正是它的强大所在. 你只要确保类型变量 K extends keyof T 就可以了. 例如下面 getProperty函数的例子:

function getProperty<T, K extends keyof T>(o: T, name: K): T[K] {
    return o[name]; // o[name] is of type T[K]
}

getProperty 里的 o: Tname: K, 意味着 o[name]: T[K]. 当你返回 T[K]的结果, 编译器会实例化键的真实类型, 因此 getProperty 的返回值类型会随着你需要的属性改变。

let name: string = getProperty(person, 'name');
let age: number = getProperty(person, 'age');
let unknown = getProperty(person, 'unknown'); // error, 'unknown' is not in 'name' | 'age'

索引类型和字符串索引签名

keyofT[K] 与字符串索引签名进行交互. 如果你有一个带有字符串索引签名的类型, 那么 keyof T 会是 string . 并且 T[string] 为索引签名的类型:

interface Map<T> {
    [key: string]: T;
}
let keys: keyof Map<number>; // string
let value: Map<number>['foo']; // number

类型映射

见之前总结的 "内置类型", 是同一个概念

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,039评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,223评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,916评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,009评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,030评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,011评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,934评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,754评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,202评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,433评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,590评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,321评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,917评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,568评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,738评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,583评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,482评论 2 352