Typescript学习笔记

时间:2023-01-29 21:38:02

什么是 TypeScript

TypeScript 是 JavaScript 的类型的超集,它可以编译成纯 JavaScript。

安装 TypeScript

命令行工具安装:

npm install -g typescript

编译一个 TypeScript 文件:

tsc hello.ts

原始数据类型/ 任意值

为每一个变量提前申明该类型,则该变量取值只能为申明的类型,否则报错

如果一个变量可以任意取值,可以通过any 申明该变量为任意值

原始数据类型包括:布尔值(boolean)、数值(number)、字符串(string)、nullundefined、Symbol(ES6)共六种

申明为number类型的变量,后面可以取值类型为 number  、null、undefined,会默认进行隐式转换

例子:

let isDone: boolean = false;        //申明该值为 boolean 类型

let decLiteral: number = 6;

let notANumber: number = NaN;       // 进行隐式转换

let infinityNumber: number = Infinity;  // 进行隐式转换

let num: number = undefined;   // 进行隐式转换

let myName: string = 'Xcat Liu';

let u: undefined = undefined;

let n: null = null;

let anytest: any = 123;   //申明该值为任意类型
anytest = true;
anytest = '任意值' // 内置对象类型申明 let b: Boolean = new Boolean(1); // 申明为 Boolean 类型 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
});
 
提前申明变量的类型也可以用在functiuon函数传参,用于规定该参数的类型

在 TypeScirpt 中,可以用 void 表示没有任何返回值的函数

例子:
function sayHello(person: string) {     // 在函数出入的参数后面申明类型
return 'Hello, ' + person;
} function test(): void { //void 表示该函数没有返回值
alert('Hello Wrold');
}
 

联合类型

联合类型表示取值可以为多种类型中的一种。使用 | 分隔每个类型。

let test: string | number;    //申明 test变量的类型为string 或者 number其中一种
test = 'seven'; //test的取值可以为其中一种类型
test = 7;

当不确定一个使用了联合类型的变量是哪种类型时,只能访问此联合类型的所有类型里共有的属性或方法

function getString(something: string | number) {     //申明传入的参数为string或者number类型
return something.toString(); // 只能调用string和number共有的方法
}

错误例子:

function getLength(something: string | number) {
return something.length; // number类型不存在length
}

对象的类型——接口 (interfaces)

即提前定义一个对象的类型规则,用于申明一个对象时进行类型匹配

interface Person {     //使用interface定义一个对象的类型,取名Person    首字母一般大写
name: string; //申明该对象每个值的取值类型
age: number;
} let xcatliu: Person = { // 申明一个xcatliu对象,使用Person规则进行匹配验证
name: 'Xcat Liu',
age: 25,
};

使用Interfaces来定义对象的类型,定义了类型的对象,定义的变量不允许比类型少,也不能多,结构必须一模一样

错误例子:

interface Person {
name: string;
age: number;
} let xcatliu: Person = { //不能比定义的Person类型的变量少
name: 'Xcat Liu',
}; let xcatliu: Person = {
name: 'Xcat Liu',
age: 25,
website: 'http://xcatliu.com', /不能比定义的Person类型的变量多
};

如果我们希望不要完全匹配一个 类型,可以使用可选属性: 可选属性的含义是该属性可以不存在。使用 ?      但是仍然不允许添加未定义的属性

interface Person {
name: string;
age?: number; //在属性后面加上? 表示该属性可有可无
} let xcatliu: Person = {
name: 'Xcat Liu',
};

有时候我们希望一个接口允许有任意的属性,即可以任意添加属性个数,使用 任意属性  [propName: string]

一旦定义了任意属性,那么确定属性和可选属性都必须是它的子属性    任意属性的取值类型为 any ,否则会跟可选属性冲突

interface Person {
name: string;
[propName: string]: any; // 表示可以任意添加属性个数 ,添加的属性类型为 any
} let xcatliu: Person = {
name: 'Xcat Liu',
website: 'http://xcatliu.com', //任意添加的属性
websit2: 'http://xcatliu.com', //任意添加的属性
};

对象中的一些字段只能在创建的时候被赋值,那么可以用 readonly 定义只读属性,在刚刚创建对象的时候赋值,后面不允许改变该属性值。也不能在创建的时候未定义值

readonly 定义在属性的前面,用空格区分

interface Person {
readonly id: number; //使用 readonly 定义只读属性
name: string;
} let xcatliu: Person = {
id: 89757,
name: 'Xcat Liu',
}; xcatliu.id = 9527; //报错 不能改变该属性值

数组的类型

数组的类型用于定义申明一个数组时的格式,申明格式类型的数组不能使用其他类型的方法

对象中的接口定义类型方法也同样适用于数组

let testArr: number[] = [1,2,5,4,8]     //申明一个数组的类型为 number

let fibonacci: string[] = ["1","2"];    //申明一个数组的类型为 string

let list: any[] = ['Xcat Liu', 25, { website: 'http://xcatliu.com' }];   // 申明一个数组的类型为任意类型

let fibona: Array<number> = [ 1, 2, 3, 5];   //也可以使用数组泛型Array<elemType> 来表示数组

interface NumberArray {      //使用接口定义一个数组的类型,表示该数组取值必须为 string 类型
[index: number]: string;
}
let fi: NumberArray = ["a","1"]; interface NumberArr { //使用接口定义一个数组的类型,表示该数组取值必须为 number 类型
[index: number]: number;
}
let fi2: NumberArr = [1, 1, 2, 3, 5];

类数组不是数组类型,比如 arguments  ,不能使用数组的类型定义方式

常见的类数组都有自己的接口定义,如 IArgumentsNodeListHTMLCollection 等

function sum() {
let args: IArguments = arguments; // arguments是类数组类型,使用 IArguments
}

函数的类型

定义函数的类型,对函数传入的参数和返回值做一定的限制

输入多余的(或者少于要求的)参数,是不被允许的,

function sum(x: number, y: number): number {     //申明一个函数sum  限制其传入的参数为 number类型,返回的参数为 number类型
return x + y;
} sum(1, 2);

如果是一个函数表达式,,那么需要对左右两边进行类型的限制

//申明了一个 mySum函数,申明其类型为number
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y;
};

函数的类型定义同样可以使用对象的接口

interface SearchFunc {      //使用接口申明一个类型,名称为 SearchFunc
(source: string, subString: string): boolean; //申明该函数的传入值都为 string 返回值为 boolean
} let mySearch: SearchFunc; //申明一个函数,使用 SearchFunc 类型规则
mySearch = function(source: string, subString: string) {
return source.search(subString) !== -1;
}

函数的类型也可以定义可选的传入参数, 在参数后面使用 ?

可选参数必须接在必需参数后面

function buildName(firstName: string, lastName?: string) {    // lastName 为可选参数
if (lastName) {
return firstName + ' ' + lastName;
} else {
return firstName;
}
}
let xcatliu = buildName('Xcat', 'Liu');
let xcat = buildName('Xcat');

TypeScript 会将添加了默认值的参数识别为可选参数     使用默认值的可选参数不限制位置

function buildName(firstName: string, lastName: string = 'Liu') {     // lastName 为可选参数
return firstName + ' ' + lastName;
}
let xcatliu = buildName('Xcat', 'Liu');
let xcat = buildName('Xcat');
function reverse(x: number | string): number | string {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''));
} else if (typeof x === 'string') {
return x.split('').reverse().join('');
}
}

ES6 中,可以使用 ...rest 的方式获取函数中的剩余参数,items 是一个数组。可以用数组的类型来定义它:

function push(array: any[], ...items: any[]) {     //...items表示剩余参数,是一个数组,可以申明为任意类型的数组
items.forEach(function(item) {
array.push(item);
});
} let a = [];
push(a, 1, 2, 3);

类型断言   <类型>值     值 as 类型

类型断言可以用来绕过编译器的类型推断,手动指定一个值的类型

TypeScript在使用联合类型时,默认不能引用不确定类型的方法,只能引用共有的方法,某些时刻,我们需要使用类型断言,即申明此时的属性为某个类型

类型断言不是类型转换,断言成一个联合类型中不存在的类型是不允许的   断言的类型必须是联合类型中的某一个

function getLength(something: string | number): number {
if ((<string>something).length) { // 默认不能使用 length属性,使用类型断言 <string> 将此时的something申明为 string类型
return (<string>something).length;
} else {
return something.toString().length;
}
}

类型别名

类型别名用来给一个类型起个新名字。使用 type

type Name = string;        // 使用 type 将string类型起名为 Name
type NameResolver = () => string; //另一种写法
type NameOrResolver = Name | NameResolver; //使用该别名
function getName(n: NameOrResolver): Name {
if (typeof n === 'string') {
return n;
}
else {
return n();
}
}

字符串字面量类型

字符串字面量类型用来约束取值只能是某几个字符串中的一个。同样使用 type

type test = 'click'|'scroll'|'mousemove'       // 使用 type 规定test变量为三个值其中一个
function handleEvent(ele: Element, event: test) { //使用test类型,传入的值为规定值的其中一个
// do something
}
handleEvent(document.getElementById('hello'), 'scroll');  

元组

数组合并了相同类型的对象,而元组(Tuple)合并了不同类型的对象。

简单理解,在数组类型中,要么规定数组中的每个值都为某种类型,要么都为任意类型。使用元组,可以依次给每个值指定类型

let xcatliu: [string, number] = ['Xcat Liu', 25];

我们也可以在定义数组类型后,通过索引依次赋值,也可以只赋值其中一项

当直接对元组类型的变量进行初始化或者赋值的时候,需要提供所有元组类型中指定的项。

let xcatliu: [string, number];    //先定义每个值的类型
xcatliu[0] = 'Xcat Liu'; // 通过索引赋值
xcatliu[1] = 25; xcatliu[0].slice(1); //可以通过索引调用对应类型的方法
xcatliu[1].toFixed(2);
let xcatliu: [string, number] = ['Xcat Liu'];   //报错,需要全部赋值

当我们去访问数组未定义的下标或者对其进行赋值时,它的类型为已存在元素的类型组成的联合类型,能够调用的方法为联合类型共有的方法

let xcatliu: [string, number];

xcatliu = ['Xcat Liu', 25, 'http://xcatliu.com/'];    // 第三个值的类型为 string|number

xcatliu.push(true);     // 报错   true 是 boolean 类型

console.log(xcatliu[2].slice(1));   // 报错   slice不是 string|number 共有的方法

TypeScript 中类的用法    public private 和 protected

  • public 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public 的
  • private 修饰的属性或方法是私有的,不能在声明它的类的外部访问
  • protected 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的
class Animal {
public name; //表示公共的,在任何地方都可以被访问
private age; //表示私有的,不允许被访问,也不能继承
protected height; //表示受保护的,只能通过继承访问
public constructor(name) {
this.name = name;
}
} var a = new Animal('Jack');
console.log(a.name);
console.log(a.age); //报错,不允许被访问
console.log(a.height); // 报错,只能通过继承访问

abstract 用于定义抽象类和其中的抽象方法。

抽象类是不允许被实例化的,即不能通过new去实例化,只能被继承

抽象类中的抽象方法必须被子类实现,即使用abstract 定义为抽象方法,那么必须在子类实现

abstract class Animal {
public name;
public constructor(name) {
this.name = name;
}
public abstract sayHi(); //抽象方法
} class Cat extends Animal {
public sayHi() { // 继承 Animal类后实现定义的抽象方法
console.log(`Meow, My name is ${this.name}`);
}
} let cat = new Cat('Tom');

类实现接口  implements

不同类之间可以有一些共有的特性,这时候就可以把特性提取成接口(interfaces),用 implements 关键字来实现。

interface Alarm {    //使用 interface 定义了一个接口,里面是一个 alert 方法
alert();
} class Door {
} class SecurityDoor extends Door implements Alarm { // SecurityDoor继承了 Door类并且引用了 Alarm 接口的方法
alert() {
console.log('SecurityDoor alert');
}
} class Car implements Alarm { // Car类 引用了Alarm 接口的方法
alert() {
console.log('Car alert');
}
}

一个类可以同时引用多个接口

interface Alarm {
alert();
} interface Light {
lightOn();
lightOff();
} class Car implements Alarm, Light {
alert() {
console.log('Car alert');
}
lightOn() {
console.log('Car light on');
}
lightOff() {
console.log('Car light off');
}
}

接口与接口之间也可以相互继承:

interface Alarm {
alert();
} interface LightableAlarm extends Alarm {
lightOn();
lightOff();
}