JavaScript是一门动态弱类型语言。对变量的类型非常宽容,没有类型约束。
TS是一个拥有类型系统的JavaScript超集,可以编译成纯JavaScript。
ts主要提供了类型系统和对ES6的支持。
tsconfig.json目录中文件的存在表明该目录是ts项目的根。该tsconfig.json文件指定了根文件和编译项目所需的编译器选项。
有node环境,通过npm安装:
安装:
npm i -g typescript
检查ts版本:
tsc -v
TS命令:
tsc 文件名 (运行文件)
三个要点:
类型检查
TS会在编译代码时,进行严格的静态类型检查。
语言扩展
异步操作、装饰器、接口类型、抽象类。
工具属性
TS可以编译成标准的JavaScript。
重塑“类型思维”
静态类型语言 | 动态类型语言 |
---|---|
对类型极度严格 | 对类型非常宽松 |
立即发现错误 | Bug可能隐藏数月甚至数年 |
运行时性能好 | 运行时性能差 |
自文档话 | 可读性差 |
动态类型语言的支持者认为:
Boolean Number String Array Function Object Symbol undefined null
void any never 元祖 枚举 高级类型
类型注解:
作用:相当于强类型语言中的类型声明
语法:
(变量/函数):type
类型推论
ts会在没有明确的指定类型的时候推测出一个类型,这就是类型推论。
如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成any类型而完全不被类型检查。
number | string 联合类型:取值可以为多种类型中的一种 分隔符 |
let bool: boolean = true
let num: number = 123
let str: string = ‘abc’
let tuple:[number, string] = [1,’1’]
let arr1:number[ ] = [1,2,3]
let fibonacci: number[] = [1, 1, 2, 3, 5];
数组的一些方法的参数会根据数组在定义时约定的类型进行限制:
数组的项中不允许出现其他的类型:以下是错误的。
let fibonacci: number[] = [1, '1', 2, 3, 5];
// Type 'string' is not assignable to type 'number'.
let fibonacci: Array<number> = [1, 1, 2, 3, 5];
let list: any[] = ['xcatliu', 25, { website: 'http://xcatliu.com' }];
泛型是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。数组泛型来定义返回值的类型。
T用来指代任意输入的类型。
多个类型参数,定义泛型的时候,可以一次定义多个类型参数
泛型约束
interface NumberArray {
[index: number]: number;
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5];
NumberArray 表示:只要索引的类型是数字时,那么值的类型必须是数字。
对象 声明类型的对象
let obj:{x:number, y:number} = {x:1,y:2}
在ts中不可以任意修改对象的属性,以上写法就可以修改对象的属性了。
interface Person {
name: string;
age: number;
}
let tom: Person = {
name: 'Tom',
age: 25
};
元组合并了不同类型的对象
越界元素:当访问超出元组长度的元素时,它的类型会被限制为元组中每个类型的联合类型。
可选属性
赋值的时候,变量的形状必须和接口的形状保持一致。
如果不希望完全匹配一个形状,可用可选属性 ?。可选属性的含义是该属性可以不存在。仍然不允许添加未定义的属性。
interface Person {
name: string;
age?: number;
}
let tom: Person = {
name: 'Tom'
};
任意属性
使用 [propName: string] 定义了任意属性取 string 类型的值。
一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集。
interface Person {
name: string;
age?: number;
[propName: string]: any;
}
let tom: Person = {
name: 'Tom',
gender: 'male'
};
一个接口只能定义一个任意属性。如果接口中有多个类型的属性,则可以在任意属性中使用联合类型:
interface Person {
name: string;
age?: number;
[propName: string]: string | number;
}
let tom: Person = {
name: 'Tom',
age: 25,
gender: 'male'
};
只读属性
一些字段只能在创建的时候被赋值,那么可以用readonly定义只读属性。
interface Person {
readonly id: number;
name: string;
age?: number;
[propName: string]: any;
}
symbol 唯一(es6)
let s1:symbol = Symbol( )
let s2 = Symbol( )
都只能赋值为undefined null。undefined类型的变量只能被赋值为undefined,null类型的变量只能被赋值为null。
修改tsconfig.json中的strictNullChecks为false,即可把其他类型赋值为undefined和null,因为在官方文档中,undefined和null是其他类型的子类
没有任何返回值的函数
let noReturn = ( ) => { }
任意类型,变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型。任意类型,即在任意值上访问任何属性和方法都是允许的,即不做类型检查。
永远不会有返回值的类型。never类型表示的是永不存在的值的类型。比如never类型是总会抛出异常或者根本就不会有返回值的函数表达式或箭头函数表达式的返回值的类型。变量也可能是never类型,当它们被永不为真的类型保护所约束时。
never类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是never的子类型或可以赋值给never类型(除never本身以外)即使any也不可以赋值给never
一组具有名字的常量集合 enum
数字枚举、字符串枚举、异构枚举(字符串、数字混用)
枚举类型用于取值被限定在一定范围内的场景:枚举使用enum关键字来定义。枚举成员被赋值从0开始递增的数字,同时也会对枚举值到枚举名进行反向映射。未手动赋值的枚举项与手动赋值的重复了,ts不会察觉到这一点。
枚举项有两种类型:常数项和计算所得项
当满足以下条件,枚举成员被当作是常数:(不具有初始化函数并且之前的枚举成员是常数)
let add = (x:number,y:number) => x + y
let compute: (x:number, y:number) => number
compute = (a,b) => a+b;
函数的类型
函数声明:在JavaScript中,定义函数的方式:函数声明和函数表达式。
// 函数声明(Function Declaration)
function sum(x, y) {
return x + y;
}
// 函数表达式(Function Expression)
let mySum = function (x, y) {
return x + y;
};
在ts的类型定义中,=>用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y;
};
在ts中,输入多余的或者少于要求的参数,是不被允许的
function sum(x: number, y: number): number {
return x + y;
}
用接口定义函数的形状:
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
return source.search(subString) !== -1;
}
采用函数表达式 | 接口定义函数的方式时,对等号左侧进行类型限制,可以保证以后对函数名赋值时保证参数个数、参数类型、返回值类型不变。
声明合并:
如果定义了两个相同名字的函数、接口或类,那么它们会合并成一个类型。
函数的合并:重载(函数名相同,方法不同)
接口的合并:接口中的属性在合并时会简单的合并到一个接口中。(合并的属性的类型必须是唯一的)类的合并与接口的合并规则一致。
可选参数
用?表示可选参数 可选参数后面不允许再出现必需参数。
function buildName(firstName: string, lastName?: string) {
if (lastName) {
return firstName + ' ' + lastName;
} else {
return firstName;
}
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');
参数默认值
ts会将添加了默认值的参数识别为可选参数
剩余参数
items是一个数组。可以用数组的类型来定义它。
function push(array: any[], ...items: any[]) {
items.forEach(function(item) {
array.push(item);
});
}
let a = [];
push(a, 1, 2, 3);
重载
重载允许一个函数接受不同数量或类型的参数时,作出不同的处理。
利用联合类型。重复定义了多次函数reverse,前几次都是函数定义,最后一次是函数实现。
function reverse(x: string): string;
function reverse(x: number): number;
function reverse(x: number | string): any {
if(x === 10) {
console.log(x);
return x;
}else if( x === 'lss') {
console.log(x);
return x;
}
};
reverse('lss');
reverse(10);
类型别名用来给一个类型起一个新名字。
字符串字面量类型用来约束取值只能是某几个字符串中的一个。
类型别名与字符串字面量类型都是使用type进行定义。
可以用来手动指定一个值的类型(两种语法)
值 as 类型 (常用)
<类型> 值
在react中必需使用as
类型断言的用途:
是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
在函数名后添加了,其中T用来指代任意输入的类型,在后面的输入value:T和输出Array中即可使用了。
定义泛型的时候,可以一次定义多个类型参数:
双重断言 as any as Foo
!!!除非迫不得已,否则不要使用双重断言
类型断言VS类型转换
类型断言只会影响ts编译时的类型,类型断言语句在编译结果中会被删除,类型断言不是类型转换,不会真的影响到变量的类型。
类型断言VS类型声明
核心区别:
a断言b,只需要满足a兼容b或者b兼容a即可。
A赋值c,需要满足b兼容a
类型声明比类型断言更加严格。为了增加代码的质量,最好优先使用类型声明,也比类型断言的as语法更加优雅。
类型断言VS泛型
在使用第三方库时,引出它的声明文件,才能获得对应的代码补全、接口提示等功能
声明合并:定义了两个相同名字的函数、接口或类,那么会合并成一个类型;
函数的合并:使用重载定义多个函数类型
接口的合并:接口中的属性在合并时会简单的合并到一个接口中:
相当于
合并的属性的类型必须是唯一的
类的合并与接口的合并规则一致。
js中有很多内置对象,它们可以直接在ts中当作定义好的类型。
内置对象是指根据标准在全局作用域上存在的对象。标准是指ECMAScript和其他环境的标准。
es的内置对象:
boolean、error、date、正则等
let b: Boolean = new Boolean(1);
let e: Error = new Error('Error occurred');
let d: Date = new Date();
let r: RegExp = /[a-z]/;
BOM和DOM
Document、HTMLElement、Event、NodeList等
let body: HTMLElement = document.body;
let allDiv: NodeList = document.querySelectorAll('div');
document.addEventListener('click', function(e: MouseEvent) {
// Do something
});
用TS写node.js
Node不是内置对象的一部分,用ts写node,需要引入第三方声明文件:
npm install @types/node --save-dev
小问题:
元组越界问题:ts版本3.9.3
正常元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。当访问一个已知索引的元素,可以得到正确的类型。当访问一个越界的元素,会使用联合类型替代。
解决:越界的元素,当添加越界的元素时,它的类型会被限制为元组中每个类型的联合类型,不可以是不存在的类型。不可以直接给不存在的索引进行赋值。