TypeScript 学习笔记(高级篇)泛型和类型断言

泛型

泛型是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。

function createArray(length: number, value: T): Array {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}
createArray(3, 'x'); // ['x', 'x', 'x']

上例中,我们在函数名后添加了 ,其中 T 用来指代任意输入的类型,在后面的输入 value: T 和输出 Array 中即可使用了。
接着在调用的时候,可以指定它具体的类型为 string。当然,也可以不手动指定,而让类型推论自动推算出来

多个类型参数

//类型推断 T为number U为string
function swap(tuple: [T, U]): [U, T] {
    return [tuple[1], tuple[0]];
}

swap([7, 'seven']); // ['seven', 7]

泛型约束
在函数内部使用泛型变量的时候,由于事先不知道它是哪种类型,所以不能随意的操作它的属性或方法

function loggingIdentity(arg: T): T {
    console.log(arg.length);
    return arg;
}

// index.ts(2,19): error TS2339: Property 'length' does not exist on type 'T'.

上例中,泛型 T 不一定包含属性 length,所以编译的时候报错了。这时,我们可以对泛型进行约束,只允许这个函数传入那些包含 length 属性的变量。这就是泛型约束:

interface Lengthwise {
    length: number;
}

function loggingIdentity(arg: T): T {
    console.log(arg.length);
    return arg;
}

loggingIdentity('hello world'); //string 类型是有length属性的 如果传数字类型 则编译仍然会报错

泛型接口

interface CreateArrayFunc {
    (length: number, value: T): Array;
}

let createArray: CreateArrayFunc; 
//这里需要传递参数的
//如果是下面这种形式,则声明变量时不需要传递参数
interface CreateArrayFunc {
    (length: number, value: T): Array;
}
let createArray: CreateArrayFunc;

createArray = function(length: number, value: T): Array {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}

createArray(3, 'x'); // ['x', 'x', 'x']

泛型类

class GenericNumber {
    //还可以指定默认值
    zeroValue: T;
    add: (x: T, y: T) => T;
}

let myGenericNumber = new GenericNumber();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };

类型断言

类型断言可以用来手动指定一个值的类型,即允许变量从一种类型更改为另一种类型
格式:<类型>值 或值 as 类型
在 tsx 语法(React 的 jsx 语法的 ts 版)中必须使用前者,即 值 as 类型
形如 的语法在 tsx 中表示的是一个 ReactNode,在 ts 中除了表示类型断言之外,也可能是表示一个泛型。故建议大家在使用类型断言时,统一使用 值 as 类型 这样的语法

类型断言只能够欺骗TypeScript 编译器,无法避免运行时的错误。
使用场景

  • 将一个联合类型断言为其中一个类型,如:
interface Cat {
    name: string;
    run(): void;
}
interface Fish {
    name: string;
    swim(): void;
}

function isFish(animal: Cat | Fish) {
    if (typeof (animal as Fish).swim === 'function') {
        return true;
    }
    return false;
}
  • 将一个父类断言为更加具体的子类,比如:
interface ApiError extends Error {
    code: number;
}
interface HttpError extends Error {
    statusCode: number;
}

function isApiError(error: Error) {
    if (typeof (error as ApiError).code === 'number') {
        return true;
    }
    return false;
}
  • 将任何一个类型断言为 any 比如:(window as any).foo = 1;
  • 将 any 断言为一个具体的类型 比如:
function getCacheData(key: string): any {
    return (window as any).cache[key];
}

interface Cat {
    name: string;
    run(): void;
}

const tom = getCacheData('tom') as Cat; //断言为 Cat 类型
tom.run();

要使得 A 能够被断言为 B,只需要 A 兼容 B 或 B 兼容 A 即可
双重断言:任何类型都可以被断言为 any,any 可以被断言为任何类型。如:

interface Cat {
    run(): void;
}
interface Fish {
    swim(): void;
}
//Cat 断言为any类型,any类型断言为具体类型 Fish 
//语法上没有错误,可能会出现运行错误,请谨慎使用。
function testCat(cat: Cat) {
    return (cat as any as Fish);
}

类型断言和类型转换
类型断言只会影响 TypeScript 编译时的类型,类型断言语句在编译结果中会被删除,所以类型断言不是类型转换,它不会真的影响到变量的类型

类型断言和类型声明
类型声明比类型断言更加严格,类型断言,只要A、B有兼容即可用,但是类型声明则更加严格。

类型断言和泛型

function getCacheData(key: string): T {
    return (window as any).cache[key];
}

interface Cat {
    name: string;
    run(): void;
}

const tom = getCacheData('tom');
tom.run();

声明文件

通常我们会把声明语句放到一个单独的文件中,这就是声明文件,声明文件必需以 .d.ts 为后缀。

内置对象

JavaScript 中有很多内置对象,它们可以直接在 TypeScript 中当做定义好了的类型。
ECMAScript 标准提供的内置对象有:Boolean、Error、Date、RegExp 等。
DOM 和 BOM 提供的内置对象有:Document、HTMLElement、Event、NodeList 等。
如:

let b: Boolean = new Boolean(1);
let e: Error = new Error('Error occurred');
let d: Date = new Date();
let r: RegExp = /[a-z]/;
let body: HTMLElement = document.body;
let allDiv: NodeList = document.querySelectorAll('div');
document.addEventListener('click', function(e: MouseEvent) {
  // Do something
});

声明合并:http://ts.xcatliu.com/advanced/declaration-merging.html

你可能感兴趣的:(TypeScript 学习笔记(高级篇)泛型和类型断言)