TypeScript学习备忘——基础篇

173 阅读9分钟

原始数据类型

包括哪些

原始数据类型包括:布尔型(boolean),字符串(string),数字(number),undefined,null,Symbol

注意boolean和Boolean区别

如果像下面这样写,会在编译的时候报错:不能将类型“Boolean”分配给类型“boolean”。“boolean”是基元,但“Boolean”是包装器对象。

let isDone:boolean = new Boolean(1);

这是因为Boolean类型是与布尔值对应的引用类型,new Boolean()返回的是Boolean对象,建议不要使用Boolean对象

console.log(typeof new Boolean(1)); // object 
console.log(new Boolean(1)) // [Boolean: true]

void,null,undefined

  • void是js中没有的概念,声明一个函数,表示函数没有返回值,声明一个变量,就只能给这个变量赋值为undefined或者null 
  • null,undefined是所有类型的子类型,这两种类型的变量可以赋值给其他类型的变量,而void类型的变量就不行。

任意值any

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

tips:

  • 访问任意值any类型的变量上的不存在的属性,也不会报错,console.log出来是undefined
  • 如果变量在声明的时候没有赋值没有指定类型,那么它就会被认为是any类型。
  • 类型推论

给变量声明的时候没有指定类型,但是赋了一个值给它,会按类型推论给它推断一个指定类型

let something; 
something = 'seven'; 
something = 7; 
//--------写以上这段不会报错,是因为它和下面这段的区别在于something在声明的时候没有赋值,所以something的类型是any  

let myFavoriteNumber = 'seven';
myFavoriteNumber = 7; 
//---------写这一段会报错,Type 'number' is not assignable to type 'string'.因为myFavoriteNumber被类型推断为字符串

联合类型

访问联合类型的属性和方法

如果不确定联合类型的变量到底是哪个类型,只能访问此联合类型的所有类型里共有的属性或方法。如果能根据类型推论推断出一个类型(被赋值的时候),就只能访问这个类型的属性和方法

对象的类型–接口

  • 接口的名称一般首字母大写
  • 编译器只会检查那些必需的属性是否存在,并且其类型是否匹配。 然而,有些时候TypeScript却并不会这么宽松。如下所示:
interface SquareConfig {   
    color: string;
}  
function createSquare(config: SquareConfig) { 
    alert(config); 
} 
let myObj = { 
    color: 'black',
    height: 100
};  
createSquare(myObj); // 不报错,先把要传入的值赋给一个对象,再传入这个对象 
createSquare({ color: 'black', height: 100 }); // 会报错,对象文字可以只指定已知属性,并且“height”不在类型“SquareConfig”中。
createSquare({ color: 'black', height: 100 } as SquareConfig); // 使用类型断言可以绕过检查(不推荐)
  • 可选属性表示这个属性在变量对象里可以不存在,在接口的属性名字后面打个问号?就可以了
  • 任意属性(可索引的类型) 表示这个接口允许有任意多个属性,而且其他的属性类型必须是任意属性类型的类型子集。
  • 只读属性表示这个属性只能在对象创建的时候被赋值,(把它作为变量使用可以用const,把它作为属性使用使用readonly比较好)
interface Person {     
    name: string;     
    age?: number;  //可选属性,会报错,因为age不是string类型的子集
    [propName: string]: string;  // 任意属性,后面跟着的第一个string也可以改成number,TypeScript支持两种索引签名:字符串和数字 
    readonly id: string; //只读属性
}
  • 接口之间可以相互继承,接口还可以继承类(声明了类中存在的成员,但没有提供具体实现),接口继承类这个demo有些没看懂。。

数组的类型

数组的定义方式

let myArray1: number[] = [1, 2, 3]; //最简单的定义方式: 类型[] 被定义了类型之后,数组里面就只能放这种类型的数组项了  
let ro: ReadonlyArray<number> = a; // 可以通过ReadonlyArray创建只读数组,确保数组创建后不能被修改 
ro[0] = 12; // error!  
let myArray2: Array<number> =[1, 2, 3]; //数组泛型: Array<数组项类型>  
interface NumberArray { 	
    [index: number]: number; // 第一个number表示index需要是number类型,第二个number表示value也需要是number类型 
} 
let myArray3: NumberArray = [1, 2, 3]; //用接口表示数组

类数组

像arguments这种不是不是数组,但是类似数组的,要赋值给一个数组变量的时候,这个数组变量不能用普通的数组的方式来描述,而应该用接口。

事实上,常用的一些类数组都有早已有了自己的接口定义,不用我们自己去写,比如:IArguments,NodeList,HTMLCollection

any数组

把数组类型设为any:

let list any[] = ['us',7,{website: 'www.baidu.com'}]; //这样数组又像传统的js数组一样,可以在里面放任意类型的数组项

扩展

泛型、内置对象

元组

特点:元组的元素可以是不同的类型,但是赋值时元素数量和类型必须与声明匹配。

let person :[string, number]; //可以先定义,再赋值 
person[0] = 'TOM';
let Animal :[string, number] = ['dahuang']; //这种情况会报错,因为当直接对元组类型的变量赋值的时候,需要提供所有元组类型中指定的项 
person.push('Mary'); 
person.push(25);
person.push(true)//会报错,不能往person数组里放元组里没有的数据类型boolean,文档里管这种做法叫添加越界元素

和普通数组对比: 给数组声明加上联合类型,数组元素也可以是不同的类型,但数组没有对元素每一项的位置和数量严格要求

const arr1: Array<number> = [1, 2];
const arr2: string[] = ['hello', 'world'];
const arr3: (string|number)[] = [1, '33']; 

应用: 元组多数时候可用于多维数组的类型检测,如下所示:

let Tom: [string,number][] = []; //定义一个由string,number组成的数组  
Tom.push(['tom',1]) // 不会报错 
Tom.push(['tom',1,true]) // 会报错:类型“[string, number, boolean]”的参数不能赋给类型“[string, number]”的参数。

函数的类型

函数类型包括两个部分:参数部分和返回值类型,如果没有返回值的话,返回值类型要写void,不能为空。

函数声明与函数表达式

// 函数声明 
function sum(x: number, y: number): number{ 
    //输入参数的类型用括号包起来,返回类型可以不写,会自己推断出来
    return x + y;
}  
// 函数表达式
let sum2:(x:number, y: number) => number = function(x: number, y: number): number {
    //要注意这个"=>"这个符号和es6里面不一样,在ts里这个符号左边是输入类型,右边是输出类型 	
    return x + y;
}

函数参数

  • 函数参数不能多传或少传,除非像定义可选属性那样用问号(?)把参数设为可选参数,那这个参数可传可不传
  • 要注意:可选参数后面不允许再出现必需参数了,就是说必需参数在前面,可选参数放后面。
  • 如果给函数参数设置默认值,那这个参数会被识别成可选参数,也就是说这个参数可以传值进来也可以不传
  • rest参数的用法是表示剩余参数,可以把它类型设置成any数组,而且要注意rest参数只能是最后一个参数,这一点和es6里面相同
  • 函数的参数类型可以是联合类型

函数重载

js里是没有函数重载的,在ts里允许重复定义同名函数多次(入参个数或类型不同,与返回值无关)。

ts函数重载意义?

能够让我们知道传入不同的参数得到不同的结果,强制和记录这些约束

function padding(all: number);
function padding(topAndBottom: number, leftAndRight: number); 
function padding(top: number, right: number, bottom: number, left: number); // 上面这一堆是定义,下面这部分是实现 function 
padding(a: number, b?: number, c?: number, d?: number) {  
    if (b === undefined && c === undefined && d === undefined) { 
        b = c = d = a;
    } else if (c === undefined && d === undefined) {    
        c = a;
        d = b;
    }   
    return {     
        top: a,     
        right: b,     
        bottom: c,     
        left: d   
    }; 
}  
padding(1); // Okay: all 
padding(1, 1); // Okay: topAndBottom, leftAndRight 
padding(1, 1, 1, 1);// Okay: top, right, bottom, left  
padding(1, 1, 1); // Error: Not a part of the available overloads 
//上面这句编译的时候会报错,因为没有定义传三个参数的这种情况。  

类型断言

类型断言不等于类型转换:因为转换通常意味着某种运行时的支持。但是,类型断言纯粹是一个编译时语法,同时,它也是一种为编译器提供关于如何分析代码的方法。

何时使用

用联合类型来描述变量时,如果不清楚变量到底是哪种类型,就只能访问联合类型里面那些类型所共有的属性和方法。但我们有时在还不确定变量类型的时候,就想访问其中一种类型的属性或方法,就可以使用类型断言,表示我很清楚我现在在干什么。

通常用于一些js和ts临界的场景。

如何使用

在要断言的变量前面加上<类型>或用as语法,在react里面只能用as写法(这么设计是因为尖括号会和react里面的语法冲突),所以多数时候建议用as。

不允许断言成联合类型中不存在的断言,断言类型和变量是父子类的关系。

function getLength(something string | number): number{ 	
    if ((<string>something).length) { 
    //将something断言成string类型,就可以使用string类型的length属性了 		
    return (<string>something).length;// 每次使用length属性都要用断言 	
    } else { 		
        return <boolean>something;//断言一个联合类型中不存在的boolean类型是会报错的
    }
}

非空断言:(参考链接Typescript类型断言的使用场景和强制类型转换

屏幕快照 2019-09-19 下午8.16.01.png

双重断言:某些api设计不合理的时候可能这么用,正确的设计是什么样的?

声明文件

什么是声明语句:定义一些我们要使用的变量类型的语句,他们会被用于编译时的检查,在编译结果中被删除

把声明语句放在一个单独的文件中,就是声明文件,声明文件的后缀名是:.d.ts

如果是将声明文件直接存放于当前项目中,则建议和其他源码一起放到 src 目录下,推荐直接把d.ts文件放在ts文件相同的目录下,这样vscode也能识别。

当通过模块导入的方式使用第三方库的时候,推荐使用@types统一管理第三库的声明文件,它的使用方式也很简单,直接用npm安装对应的声明模块。可以用这个搜索需要的声明文件:microsoft.github.io/TypeSearch/

如何写自己的声明文件,可参考:zhongsp.gitbooks.io/typescript-…

内置对象

什么是内置对象:在TypeScript核心库的定义文件中已经定义好了的类型,不用我们自己去写接口。比如ECMAScript提供的Boolean,Error,Date等内置对象,DOM和BOM中提供的Document、HTMLElement等内置对象

let b: Boolean = new Boolean(1); //不需要我们自己去定义Boolean接口,TypeScript帮我们写好了,直接拿来用就可以 
let e: Error = new Error('Error occurred');
let d: Date = new Date();
let r: RegExp = /[a-z]/;

注意:TypeScript 核心库的定义中不包含 Node.js 部分