Ts支持所有的js代码,并且为js提供了类型提示,使得在编写js代码时就可以获得良好的代码提示,使得js代码更加的安全。提升了可读性和可维护性。最终会由编译器编译为Js代码供浏览器使用。
- 适用于周期较长的大型项目 适合团队开发 编写通用的代码库
- 试用 :www.typescriptlang.org/zh/play
- 文档 :www.tslang.cn/docs/handbo…
- TS 相对于 Js 进行了类型限制,使得对某种类型的数据只允许做该类型允许的操作,实现了类型安全 。
- 在运行时做类型检查叫做动态类型检查,写代码时比较灵活,但是只会在运行时报错,例如:JavaScript
- 如果在代码编译时做类型检查,叫做 静态类型检查,写代码时需要对变量的声明和相应操作,但是会在编译时报错
优势:(1)提供静态类型检查,在写代码阶段就可发现错误。
(2)良好的代码提示,更容易的写代码,减少bug。
安装: npm install -g typescript
核心: 定义 任何东西都要 注明类型 , 调用 任何东西的时候都要 检查类型
ts环境
yarn add global typescript 全局安装ts
tsc -v 查看版本 tsc 文件名 编译文件(最终会编译成js)
在开发环境下,目前的脚手架程序基本上都可以很容易的创建ts环境
当在 TypeScript 项目中使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能。
针对多数第三方库,社区已经帮我们定义好了它们的声明文件,我们可以直接下载下来使用。一般推荐使用 @types 统一管理第三方库的声明文件,@types 的使用方式很简单,直接用 npm 或 yarn 安装对应的声明模块即可。
一、类型注解
- 语法:给变量添加类型 在变量后面用
:
来声明类型
使用 type
来声明类型,一般首字母需要大写(类型别名)
在变量后可以使用 ?
表示可选,非必传
返回值类型声明为 void
的函数可以 没有返回值
可以使用as
来进行类型断言(as 可以理解为强制类型转换,来告诉ts 一个指定的类型,但是只在编译阶段生效)
二、类型简介
1. 简单类型
TypeScript支持所有js基础类型 number string boolean symbol undefinde null
(小写)
********同时支持JavaScript的所有复合类型 Array class
let count:string = '100';
2. 复杂类型
(1)数组
使用 类型[]
或者 泛型 的方式来定义 特点是内部值固定为一种类型,如果需要多种类型请使用元组,
还可以使用 number[][]
来定义二维数组。三维数组同理。
type A = Array<string>
let userList : A = ['one','two'] // 泛型写法
let userList:string[] = ['one','two']; //string[] 表示字符串数组
let userList:Array<string> = ['one','two']; // Array<elemType>泛型 规定数组内部值的类型
(2)元组
可以让同一个数组中同时存在多种类型,特点是适合指定长度的数组,因为要指定数组中每一项的类型
“ 内部元素的个数和类型都固定的数组 ”
let info:[number,string]; // 指定数组中每一项的类型,赋值必须严格执行
info=[100,'name'];// 数组中的类型和顺序都有要求
(3)函数
函数的类型声明主要针对参数和返回值,一旦声明完毕,参数和返回值的类型必须按照定义来传递。
type 声明的函数参数类型,在调用的时候参数可以少
函数参数如果有默认值,会被ts理解为可选参数
函数的可选参数也有可能会有类型保护的问题:如果用户不传值,就有可能是undefined,无法使用其属性
解决方法:加入if
判断是否传值,只有为真时,才取值
// 指定参数的类型和声明变量十分相似
function Add(a:number,b:number):number{
return a+b;
}
//括号内部为参数类型,外部为返回值类型
type x = (a:number,b:number)=> number // type关键字相当于为后面的类型声明起别名,void可以表示无返回值
const Add:x = (a,b)=>{
return a + b;
}
// ? 表示可选参数,可选参数必须写到必填参数后面
function add (a:number,b?:string)
(4)interface
接口声明对象
interface关键字,声明一个可以继承 ****的对象(object,Function,构造器)类型,
注意:首字母大写,有些习惯接口名称首字母加I
前缀,变量的值应该和接口规定保持一致
- readonly ****关键字可以在 interface 中声明一个只读属性,不可以二次赋值
- ?
****表示可选属性
- 其他 interface 声明的类型可以使用 ****extends
继承 ,变量需要满足子接口和父接口声明的类型
- 类型可以嵌套声明
interface Idemo{
redonly name:string; //只读
herght?:number; // 可选属性
}
interface Ison extends Idemo{
height:number //继承Idemo的属性
}
interface NumberArray {
[index:number]:number; //如果索引是number,值也为number 定义数组类型,不常用
[index:string]:any; // 定义任意类型
}
interface func{
(a:string,b:string):string; //接口定义函数
}
interface sayHello{
(name:string):string;
}
interface PersonConstructor {
new (name: string, age: number): IPerson;
}
注意:TypeScript 支持直接使用字面量作为类型,例如:123,'字符串',{obj : ' 对象 '}
(5)Class
interface Options {
name:string;
}
interface animal { // 定义类
option:Option, // 定义类的参数
eat():void //定义类的内部方法
}
class Dog implements animal {
let option:Opiton
constructor(options:Opitons){
this.optionS = options
}
eat():vodi{
consosole.log(
cosnole.log(this.options,'吃了骨头')
)
}
}
new Dog ({
name:'旺财"
})
修饰class:
readonly
只读 , 修饰属性或索引签名,不可修改。private
修饰方法,只能在内部使用,子类也不可以调用。protected
****修饰方法,给继承的子类和父类自身内部使用public
****默认为 public 可以在任意地方使用
(6)object
可以使用object声明 引用类型,包括 object , Array , Function ,一般用来做类型约束
let a : object = [] // 可以用来声明 Array 等引用类型
let b : {} // 相当月 new Object , 可以声明所有类型 , 一般不用
3. TS 特殊类型(js中没有的)
- void 含义:空类型,可以使用 as 断言指定类型 只能将 undefinde 和 null 赋值给 void。
- unknown 含义:未知类型,在使用时可以使用 as 断言指定类型,任何类型都可以赋值给unknown
但是unknow不可以赋值给其他类型。(可以理解为unknow是所有类型的联合)
- never 含义:永远不存在的值,一般不用于声明,在类型判断时使用,或者函数抛出异常的时候,返回值为never。
never 可以赋值给任意类型,但是所有类型都不可以给never 赋值
- any 含义:任意类型,可以赋值给任何类型(never除外),也可以接受任何类型的赋值
使用时候可以使用 as 断言指定类型
- Enum 含义:枚举是一系列值的复合,enum的值只接受字符串string 和 数字number,可以用来做权限管理等等。
enum A {
name:'小明',//name 枚举值,‘小明’ 真实值
age:'18',
}
const example = A.age;
4. type and interface 对比
- type “别名 ” 可以声明任何类型(基本类型和Object),
- interface “接口 ” 只能声明对象(Array,Function,object,Date,Map,正则等),interface是类型声明
- type声明后的值不可以重新赋值,扩展性较差。interface可以扩展。
- interface 对类型中属性冲突的地方会直接报错,type 会转为never 不能使用
- 注意:对外Api尽量使用interface方便扩展,对内api使用type ,防止代码分散。
- 对于定义的属性,可以使用
?
来表示为可选属性, 可以使用readonly
来指定为只读属性,通过只读属性构造的变量,其属性不可以改变。
interface A {
[k:string]:number
// 声明索引类型和值类型,index是随机的,但是一般是采用含义相近的,例如 i,k,key等
}
interface A2 extends Array<string>{
name:string,
}
interface A3 {
(a:number,b:number):number
}
type A1 = Array<string> & {
name:string
}
interface Itype {
name? : string, // 可选属性
readonly age : number, // 只读属性
}
// 1. 声明两个同名的interface,ts会自动将其属性合并,X会同时拥有name和age
interface X {
name:string
}
interface x {
age:number
}
const a:x={
name:'小明',
age:17
}
// 扩展全局的interface String
declare global {
interface String{
foo(x:string):void
}
}
三、高级类型
1. 联合类型
使用 |
****来声明联合类型,表示值为多种类型中的一种,typescript类型可以根据赋值来类型推断。
联合类型的类型保护:由于值可能是多种类型中的一个,所以只能使用所有类型的共有属性
为了避免这种情况 可以通过 in
或者 as 断言
来缩小联合类型范围,使代码更加安全
let a: number | string ; // 表示 a 的类型是 number 和 string 中的一种
2. 交叉类型
使用 &
****来声明联合类型,表示值需要同时满足几种类型,可以配合interface等使用
let a: number & string ; // 表示 a 的类型是 number 和 string 中的一种
3. 映射类型
type Demo<T> = {
[Key in keyof T]?: T[Key]
}
//实现 pick<T,K>,从类型T中选出类型K,构造成一个新的类型
type R = MyPick<{a:1,b:2,c:3},"a">; // 从第一个对象中,找出key为a的类型
type MyPick<T,K extends keyof T>={
[p in k]:T[p]
}
4. 泛型
- 泛型是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性
- 为了类型复用,泛型即为通用的类型,ts的类型体操就是对泛型进行逻辑处理。
<>
内部的内容表示参数的类型,function foo<a:string参数类型>(a):string 返回值类型 {}
function Add<T>(a:T,b:T):T{
return a+b;
} // 使用t来表示类型待定,等传值时候再指定类型
let c = Add<number>(2,5);// 使用时传值
type a = string[];
//等价于
type a = Array<string>
四 类型运算
类型运算就是对传入的类型参数进行运算后返回新的类型。
1. 条件判断
extends ? :
ts 类型的条件判断的语法是 条件 ? 分支1 : 分支2
type value = T extends U ? true : false // 如果T是U的子类,则返回true,反之为false
2. 推导
infer
提取类型的一部分,通过模式匹配的方式。
type Str<T extends string> = T extends `${infer Key}=${infer Value}`
? {
[k in Key]: Value
}
: never
3. 利用js方法进行类型收窄
- typeof 语法
typeof 变量名
返回值为类型的字符串格式,例如'number'
缺点:返回值仅支持常用类型,例如array,function都会返回 'object'
const f1 = (a:number | string)=>{
if(typeof a==='number'){
....
}else{
....
}
}
- instanceof 语法:
a instanceof Date
****如果a 是日期的实例就返回true
缺点:不支持简单类型(instanceof基于原型),仅支持判断Object,any,或者泛型,不支持ts的独有类型
const f1 = (a:Date|Date[]){
if (a instanceof Date){
...
}else{
...
}
}
- in 判断
const f1 = (a : Person | Animal)=>{
if ('name' in a){
...
}
}
- Array.isArray() 判断
const f1 = (a:Array|function){
if(Array.isArray(a)){
...
}else{
...
}
}
4. 利用 ts方法进行类型收窄
- 可辨别联合类型
定义:ts 允许用户给多个类型添加同一字段从而判断联合类型的实际类型
这个字段的类型只能为基本类型,多个类型必须有同一个字段,不同类型的同名字段的类型尽量不要有交集
type A = {kind:'string',value:'number'}
type B = {kind:'number',value:'string'}
const f1 =(a:A|B){
if(a.kind ==='string'){
A类型
}else{
B类型
}
}
- is 类型谓词
type A = {
name : string
age : number
}
type B ={
gender: string
job : string
}
const f1 (example : A | B){
// 利用函数判断example是否是A类型
if(isA(example)){
}
}
// 声明一个专门用来判断A类型或者B类型的函数,返回值为boolean
function isA ( x : A | B ) : x is A{
return 'name' in x && 'age' in x
}
- as断言
直接使用as强制指定类型,进行 类型收窄。
5、 类型遍历 in keyof
索引名称 in keyof 类型
索引查询
type Person = {
name: string
age: number
}
type KeyofPersion = keyof Person // 'name' | 'age'
k in keyof Person
T[Key]
是取索引类型某个索引的值,叫做索引访问。类似于js对象属性的访问方式,T 并没有实际意义,需要根据传递的值判断。in
是用于遍历联合类型的运算符。类似于 js 。
6、映射类型详解
- 映射值
type MapType<T> = {
[Key in keyof T]: [T[Key], T[Key], T[Key]]
// [Key in keyof T] 取出 key 的名字 即 a 和 b
// T[Key] 取出 key 对应的值 即 1 和 2
}
type res = MapType<{a: 1, b: 2}>;
- 映射类型
因为索引类型(对象、class 等)可以用 string、number 和 symbol 作为 key,这里 keyof T 取出的索引就是 string | number | symbol 的联合类型,和 string 取交叉部分就只剩下 string 了。就像前面所说,交叉类型会把同一类型做合并,不同类型舍弃。
type MapType<T> = {
[
Key in keyof T
as `${Key & string}${Key & string}${Key & string}`
]: [T[Key], T[Key], T[Key]]
}
// Key in keyof T 取出 key 的名字
// as `${Key & string}${Key & string}${Key & string}` 将key 的名字 转化为 重复三次的字符串