原始数据类型
包括哪些
原始数据类型包括:布尔型(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
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类型断言的使用场景和强制类型转换)
双重断言:某些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 部分