TypeScript 学习笔记--高级类型(一)

194 阅读2分钟

一、交叉类型

定义:将多个类型合并为一个类型,这个类型包含了所有类型的特性

使用符号:&

示例:

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

二、联合类型

定义:表示一个值可以是几种类型之一

使用符号:竖线( |)分隔每个类型

注意事项:如果一个值是联合类型,我们只能访问此联合类型的所有类型里共有的成员

示例

number | string | boolean表示一个值可以是 number, string,或 boolean

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

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

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

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

三、类型保护

1、用户自定义的类型保护

定义:定义一个函数,它的返回值是一个 类型谓词,谓词为 parameterName is Type这种形式, parameterName必须是来自于当前函数签名里的一个参数名

示例

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

// 'swim' 和 'fly' 调用都没有问题了
// 使用变量调用 isFish时,TypeScript会将变量缩减为那个具体的类型,
// 只要这个类型与变量的原始类型是兼容的

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

2、typeof类型保护

(1)TypeScript可以将typeof识别为一个类型保护。 可以直接在代码里检查类型

(2)能被识别为类型保护形式

typeof v === "typename"

typeof v !== "typename"

"typename"必须是 "number""string""boolean""symbol"s

示例:

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}'.`);
}

3、instanceof类型保护

instanceof的右侧要求是一个构造函数

object instanceof constructor

instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上

示例

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

四、类型别名

定义:创建了一个新名字来引用那个类型

类型别名使用有时和接口很像

使用规则: 能用 interface 实现,就用 interface , 如果不能就用 type

接口 vs 类型别名:

(1)不同:

类型别名不能被 extends和 implements(自己也不能 extends和 implements其它类型)

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

(一)type 可以而 interface 不行

type 可以声明基本类型别名,联合类型,元组等类型

// 基本类型别名
type Name = string

// 联合类型
interface Dog {
    wong();
}
interface Cat {
    miao();
}

type Pet = Dog | Cat

// 具体定义数组每个位置的类型
type PetList = [Dog, Pet]

type 语句中还可以使用 typeof 获取实例的 类型进行赋值

// 当你想获取一个变量的类型时,使用 typeof
let div = document.createElement('div');
type B = typeof div

其他操作:

type StringOrNumber = string | number;  
type Text = string | { text: string };  
type NameLookup = Dictionary<string, Person>;  
type Callback<T> = (data: T) => void;  
type Pair<T> = [T, T];  
type Coordinates = Pair<number>;  
type Tree<T> = T | { left: Tree<T>, right: Tree<T> };

(二)interface 可以而 type 不行

interface 能够声明合并

// interface 能够声明合并
interface User {
  name: string
  age: number
}

interface User {
  sex: string
}

/*
User 接口为 {
  name: string
  age: number
  sex: string 
}
*/

(2)相同点:

都可以描述一个对象或者函数

interface User {
  name: string
  age: number
}
interface SetUser {
  (name: string, age: number): void;
}

type User = {
  name: string
  age: number
};
type SetUser = (name: string, age: number)=> void;

都允许拓展, 扩展效果差不多,但是两者语法不同

// interface extends interface
interface Name { 
  name: string; 
}
interface User extends Name { 
  age: number; 
}

// type extends type
type Name = { 
  name: string; 
}
type User = Name & { age: number  };

// interface extends type
type Name = { 
  name: string; 
}
interface User extends Name { 
  age: number; 
}

// type extends interface
interface Name { 
  name: string; 
}
type User = Name & { 
  age: number; 
}