TypeScript学习备忘——进阶

141 阅读7分钟

类型别名

类型别名用关键字type定义别名,给类型起一个新名字,和接口很像,但是可以作用于原始值,联合类型,元组等等

type Name = string; // 给原始类型起别名这样的其实没什么意义 
type NameResolver = () => string; //仍然注意箭头符号此时表示,左边代表输入是(),右边代表输出是string类型 
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {    
    if (typeof n === 'string') {
        return n;     
    } else {         
        return n();     
    } 
}

类型别名不能出现在声明右侧的任何地方

type Yikes = Array<Yikes>; // error

类型别名与接口的差别:

接口创建了一个新名字,可以在任何地方使用,但是类型别名并不创建新名字。(不太理解这点)

类型别名不能被其他类型extends和implements,同时自己也不能extends和implements其他类型。

字面量类型

字面量类型vs类型别名

字面量类型和类型别名都是用type进行定义,但是类型别名是给类型起新名字,字面量类型可以在好几个字符串里面选一个值,也可以是boolean,number类型,数组,对象(除了空对象,因为之后赋值的时候赋对象值都不会报错),不能写正则(可以写RegExp,但是不能写一个具体的正则),也可以写类型的名字(比如RegExp,Array<srting>)

type Name = string; // 类型别名 
type Easing = "ease-in" | "ease-out" | "ease-in-out"; // 字符串字面量类型

字符串字面量类型vs联合类型

字符串字面量类型作用:用来约束取值只能是几个字符串中的一个

看起来和联合类型也很像,但是联合类型约束的是取值可以为多种类型中的一种,字符串字面量类型只能是字符串类型里的某些字符串中的一种

let myFavorite: number | string; //联合类型 
let animalName: "dog" | "cat"; //字符串字面量类型

实际应用

  1. 字符串字面量类型可以区分函数重载,如下所示:
function createElement(tagName: "img"): HTMLImageElement; //但是tagName只能是string类型的
function createElement(tagName: "input"):HTMLInputElement; 
function createElement(tagName: string): HTMLInputElement{
    //... 
}

2.字符串字面量类型用于定义组件的某个参数,只能从几个固定的字符串里面取。比如button组件大大小只能取值 'default'、'large'、'small'

枚举

类似c语言中的枚举,用于归纳一些范围确定的类型,把它们放到一起。比如说星期,颜色这样的(为一组数值赋予美好的名字)。

屏幕快照 2019-12-11 下午8.55.09.png

枚举项的两种类型:常数项和计算项(像加减乘除这种操作并不是计算项),不能给常数枚举项赋值为NAN或者无穷大,但是实际上写了并不会报错...

虽然枚举成员会在定义的时候被自动赋值,但是也可以手动给枚举项赋值,注意以下几点情况:

  • 如果给枚举项赋的值是数字,未被手动赋值的项会被自动赋值为上一个枚举项的值递增+1,最好不要让未手动赋值枚举项的值与手动赋值的重复,虽然Typescript不会报错,但值到枚举名的映射会被枚举名覆盖。
  • 可以给枚举项手动赋以非数字的值,但需要使用类型断言让tsc无视类型检查。
  • 不带初始化器的枚举项不能放在计算项的后面,会报错“枚举成员必须具有初始化表达”。

枚举的意义:避免魔法数字,给难以记忆的id一个别名。建议在元组项里面写的是一些详细的描述,而不是简单的单词。把鼠标放在上面的时候就会显示详细的描述信息。(例子,自己试下)

常数枚举

  • 用const enum定义的枚举类型是常数枚举,注意区分常数枚举和常数项的概念。
  • 常数枚举和普通枚举的区别:常数枚举在编译阶段会被删除,而且不能包含计算成员项(编译时会报错)。在编译阶段会删除的意思是执行tsc命令之后,常数枚举的定义这一行会完全消失。

外部枚举

  • 用declare enum定义的枚举类型是外部枚举,编译结果也会被删除,编译之后不会生成对象。只是对已存在对象的描述。
  • 可以同时使用declare和const
  • 外部枚举的意义:在引入第三方库的时候,想给里面的某些对象写一些描述信息,但又不希望编译器为此产生任何代码,用外部枚举就能很适用于这个问题。 参考链接:stackoverflow.com/questions/2…

Typescript中类的用法

可以用三种访问修饰符修饰属性和方法:public(可以在任何地方被访问)、private(不能在声明 它的类的外部访问它)、protected(与private类似,不同的是protected成员可以在派生类中访问)。在TypeScript中,成员都默认为public

抽象类

用关键字abstract定义的类

抽象类不允许被实例化

抽象类中的抽象方法必须在子类中被实现,这样的意义是什么?

类与接口

接口可以用来定义类需要具有的特性,用implements表示类实现了这个特性,

interface Alarm { 
    //被提取出来的报警器特性     
    alert(); 
} 
interface Light { 
    //灯的特性     
    lightOn();     
    lightOff(); 
} 
class Door { 
}  
class SecurityDoor extends Door implements Alarm { 
    //防盗门继承门这个类,并且实现了报警器这个特性     
    alert() {         
        console.log('SecurityDoor alert');     
    } 
}  
class Car implements Alarm, light {
    //报警器这个特性可以被不同的类实现,一个类也当然可以实现多种特性   
    alert() {         
        console.log('Car alert');     
    } 
}

继承关系:接口可以继承接口和类,类也可以继承类,但是类不能继承接口(只能实现)。

屏幕快照 2019-09-08 下午9.49.52.png

泛型

概念:在定义函数、接口、类时,不指定具体的类型,在实现的时候再制定具体类型

应用:不确定输入输出的类型,但是又希望输出的类型是根据输入的类型出来的。

function swap<T, U>(tuple: [T, U]): [U, T] { 
    // U和T是类型变量,用于捕获传入的类型(只捕获类型,不捕获值) 
    return [tuple[1], tuple[0]]; 
}  
swap<number, string>([7, 'seven']); // 此处的<number, string>可以省略,函数可以根据实现时传入的参数,类型推论出T和U的类型。

泛型接口,泛型类

可以把泛型用在接口上,然后用这个泛型来定义函数的形状(在用这个接口的时候,就要定义这个接口泛型的类型)

泛型类和泛型接口用法差不多,需要注意的是:类分为静态部分(static)和实例部分,泛型类指的是实例部分的类型,静态部分不能使用泛型类型。

interface CreateArrayFunc<T> {
    //<T>可以拿下来,写在输入参数大括号的前面     
    (length: number, value: T): Array<T>; 
}  
let createArray: CreateArrayFunc<any>;
//在用这个接口时,就要指定泛型是any类型的。 
createArray = function<T>(length: number, value: T): Array<T> {     
    //...
} 
createArray(3, 'x'); 

泛型约束

概念:在操作泛型变量的时候,由于不确定泛型变量的类型,所以不能贸然使用一些属性和方法,所以可以用泛型约束规定函数的输入必须包含要用到的某种属性。

用法:比如说可以定义一个接口来让函数入参必须继承这个接口来约束参数,也可以参数之间互相约束,比如说:

interface Lengthwise {     
    length: number;
} 
function loggingIdentity<T extends Lengthwise>(arg: T): T {} //用接口来约束
function copyFields<T extends U, U>(target: T, source: U): T {}//参数之间互相约束

在泛型里使用类类型

使用工厂模式创建对象的时候,需要引用构造函数的类类型。

class C {   
    make: 'haha'
} 
function create<T>(C: {new(): T; }): T {   return new C(); } 
let createSome = create(C);

声明合并

概念:定义了名字相同的函数、接口、类,他们会合并成一个类型。

要注意接口声明合并的时候:名字相同的属性,如果类型不同,接口在合并的时候会报错。

interface Alarm {    
    price: number;
} 
interface Alarm {     
    price: string;  // 前面已经有一个price属性了,是number类型的,这里类型不一样就会报错了。     
    weight: number; 
}

函数合并的优先级类似于函数重载。

命名空间可以和其他类型合并,但是类不可以。

声明合并应该是隐式进行的吧,毕竟在编译之后,函数、接口、类它们类型的定义就消失了。

工程引用

github.com/microsoft/T…

关于工程引用,ts自己的库的实现就是一个最好的例子,可以直接看这个。