类型注解
变量后面约定类型的语法,就是类型注解
作用: 约定类型,明确提示
// 类型注解let age:number = 18;
age = 19;
// age = "hello"; //不能将类型“string”分配给类型“number”
- :number 就是类型注解,为变量提供类型约束
- 约定了什么类型,就只能给该变量赋值什么类型的值,否则报错
- 并且,约定类型之后,代码的提示也会非常的清晰
原始类型
TS常用类型:
- js已有类型
- 简单类型:number , string , boolean , null ,undefined
- 复杂类型 : 对象Object , 数组 Array ,函数Function
- Ts新增类型
- 联合类型 自定义类型(类型别名) , 接口 , 元组, 字面量类型 , 枚举 ,void , any , 泛型
数组类型
// 数组类型
// 第一种写法【推荐】
let numbers:number[] = [1,2,3];
// 第二种写法
let strings:Array<string> = ["a","b","c"];
联合类型
将多个类型合并为一个类型
类型和类型之间使用 | 连接,代表类型可以是他们当中的其中一种,这种类型叫联合类型
// 数组中有number 和string 如何写?
let arr:(number | string |boolean)[] = [1,"hello",4,"456",true];// 给定时器加类型let timer:number | null = null;
timer= setInterval(()=>{},1000)
类型别名
使用类型别名语法给类型重新起名字
当同一类型被多次使用时,可以通过类型别名,简化该类型的使用
// 类型别名
// let arr1:(number|string|boolean)[] =[1,2,"hello"];
// 类型别名 type 类型别名 = 具体类型type CustomArr = (number | string | boolean)[];
let arr1:CustomArr = [1,2,"hello",true];
- type 类型别名 = 具体类型 基本语法
- 定义类型别名,遵循大驼峰命名规范,类似于变量
- 使用类型别名,与类型注解的写法一样即可
函数类型
给函数指定类型,就是给 参数和返回值 指定类型
两种写法:
- 在函数基础上 分别指定 参数和返回值类型
- 使用类型别名 同时指定 参数和返回值的类型
// - 在函数基础上 **分别指定** 参数和返回值类型// 函数声明
function add(num1:number,num2:number):number{return num1 + num2
}
console.log(add(1,2));// 箭头函数
const add1 = (num1:number,num2:number):number=>{return num1 + num2;
}
console.log(add1(3,4))// - 使用类型别名 **同时指定** 参数和返回值的类型 //只适用于函数表达式
type AddFn = (num1:number ,num2:number) =>numberconst add2:AddFn =(num1,num2)=>{return num1 + num2;
}console.log(add2(7,8));
void类型
void函数返回值类型
如果函数没有返回值,定义函数类型时返回值类型为void
如果函数没有返回值,且没有定义函数返回值类型的时候,默认是void
// void类型
// 如果函数没有返回值,定义函数类型时返回值类型为void
const say = ():void=>{console.log("hi");
}
say()// 如果函数没有返回值,且没有定义函数返回值类型的时候,默认是void
const say1 = ()=>{console.log("hhi");
}say1()
- 在js中如果没有返回值,默认返回的是undefined
- 但是void 和undefined 在TypeScript中并不是一回事
- 如果指定返回值类型是undefined 那返回值必须是undefined
// 如果指定返回值类型是undefined 那返回值必须是undefined
const add4 = ():undefined=>{return undefined;
}
使用 ? 将参数标记为可选
如果函数的参数,可以传也可以不传,这种情况就可以使用 可选参数 语法,参数后加 ? 即可
const fn = (n?:number)=>{console.log(123)
}
fn();
fn(3);
案例
// 模拟slice函数, 定义函数参数类型
// 注意:必须参数不能位于可选参数后面
const mySlice = (start:number,end?:number) =>{console.log("起始index"+start+'结束index'+end)
}mySlice(2)
mySlice(4,5)
对象类型
TS的对象类型,其实就是描述对象中的属性 方法的类型,因为对象是由属性和方法组成的
- 声明对象结构 {}
- 属性怎么写类型 属性名: 类型
- 方法怎么写类型 方法名():返回值类型
// 空对象
let person:{} = {}// 有属性的对象
let person1:{name:string} = {name:"zs"
}// 有属性和方法,一行书写多个属性 ; 分割
let person2:{name:string;sayHi():void} = {name:"jack",sayHi(){}
}// 换行写 可以省略 ; 符号
let person3 :{name:stringsayHi():void
} = {name:"zs",sayHi(){}
}
扩展
对象类型中,函数使用箭头函数类型,属性设置可选,使用类型别名
-
函数使用箭头函数类型
let person5:{name:stringsayHi:()=>void } = {name:"jack",sayHi(){} }
-
对象属性设置可选
// axiois({url,method}) 如果是get 请求method 可以省略 const axios =(config:{url:string;method?:string})=>{}
-
使用类型别名
// { } 会降低代码可阅读性,建议对象使用类型别名type Config = {url:string;method?:string }const axios1 =(config:Config) =>{}
接口interface
使用interface声明对象类型
接口声明是命名对象类型的另一种方式
- interface 后面是接口名称,和类型别名的意思一样
- 指定接口名称作为变量的类型使用
- 接口的每一行只能由一个属性或方法,每一行的分号可以省略
// 接口 interface声明对象类型interface Person{name:string;age:number;sayHi:()=>void
}
// 使用类型
let person8:Person = {name:"zs",age:12,sayHi(){}
}
interface继承
使用extends实现接口继承,达到类型复用
语法:interface 接口A extends 接口B { }
继承后接口A拥有接口B的所有属性和函数的类型声明
// interface 继承
// 有两个接口,有相同的属性或函数,如何提高代码复用?
// interface Point2D {
// x:number;
// y:number;
// }
// interface Point3D {
// x:number;
// y:number;
// z:number;
// }
// 继承 相同的属性或方法可以抽离出来,使用extends实现继承复用
interface Point2D {x:number;y:number;
}
// 继承Point2D
interface Point3D extends Point2D {z:number;
}
let p:Point3D ={x:12,y:23,z:56
}
type交叉类型
交叉类型实现接口的继承效果
使用 & 可以合并连接的对象类型 叫做交叉类型
// type交叉类型
// 使用type来定义Point2D 和Point3D
type Point2D = {x:number;y:number;
}// 使用交叉类型 来实现接口继承的功能
type Point3D =Point2D & {z:number;
}let o:Point3D= {x:1,y:2,z:3
}
interface vs type
- 类型别名和接口非常相似,在很多情况下,可以在他们之间自由选择
- 接口的几乎所有特性都以类型的形式可用,关键的区别在于不能重新打开类型以添加新属性,而接口总是可扩展的
interface | type |
---|---|
支持:对象类型 | 支持:对象类型 其他类型 |
复用: 可以继承 | 复用: 交叉类型 |
不同点
- type不可以重复定义
- interface重复定义会合并
type Point2D = {x:number;y:number;
}
// 标识符“Point2D”重复
type Point2D = {age:number;
}//-------------------------------------
interface Point2D {x:number;y:number;
}
//类型会合并 注意:属性类型和方法类型不能重复定义
interface Point2D {age:number
}
// 继承Point2D
interface Point3D extends Point2D {z:number;
}
let p:Point3D ={x:12,y:23,z:56,age:78
}
类型推断
- TS中存在类型推断机制,在没有指定类型的情况下,TS也会给变量提供类型
开发项目时,能省略类型注解的地方就省略, 充分利用TS的类型推断能力,提高开发效率
// 发生类型推断的几个场景// 变量a的类型被自动推断为 number
let a = 18;// 函数的返回值类型被自动推断为 number
const add3 = (num1:number,num2:number) =>{return num1 + num2
}
字面量类型
js字面量如: 18 ‘jack’ [‘a’] {age:10} 等等
使用js字面量作为变量类型,这种类型就是字面量类型
// 'lucy' 是字面量类型
let n:'lucy' = 'lucy';
// 18 是字面量类型
let n1:18 = 18;
// 报错 不能将类型“19”分配给类型“18”
// n1 = 19;let str1 = "hello"; //str1的类型是string
const str2 = "hello"; //str2的类型是 "hello" str2是const声明的 值只能是 "hello"
字面量类型应用
// 性别只能是男 和 女 不会出现其他值
// let gender = '男';
// gender = '女'
// 字面量类型 配合联合类型使用, 表示 一组明确的可选的值
type Gender = '男' | '女';
let gender:Gender = "男";
gender = "女"type Direction = 'up' | 'down' | 'left' | 'right';// direction的只能是 up ,down ,left ,right 中的任意一个
// 优点:使用字面量类型更加精确,严谨
function changeDirection(direction:Direction){console.log(direction)
}changeDirection('left');
any类型
any类型的作用是逃避TS的类型检查
显示any: 当变量的类型指定为any的时候,不会有任何错误,也不会有代码提示,TS会忽略类型检查
隐式any:声明变量不给类型或初始值,函数参数不给类型或初始值
any的使用越多,程序可能出现的漏洞越多,因此【不推荐】使用any类型,尽量避免使用
// 显式any
let obj:any = {age:18}
obj.bar = 100
// 不会报错 将来可能出现错误
obj = 23;// 隐式any
let aa ;
const fn5 = (n4) =>{}
类型断言
- 使用关键字 实现类型断言
- 关键字as后面的类型是一个更加具体的类型(HTMLAnchorElement 是 HTMLElement的子类型)
有时候你会比ts更加明确一个值的类型,此时,可以使用类型断言来指定更具体的类型
// alink的类型 HTMlElement,该类型只包含a元素特有的属性或方法
HTMLElement 使用这个类型 太宽泛 没包含a元素特有的属性或方法 href
const alink = document.getElementById('link')
<a id="link"></a>
我们很明确alink 获取到的就是一个a元素,通过类型断言,给它指定一个更具体的类型
const alink = document.getElementById('link') as HTMLAnchorElement
const img= document.getElementById('img') as HTMLImageElement
泛型
在ts中,泛型是一种创建可复用代码组件的工具,这种组件不只能被一种类型使用,而是能被多种类型复用。类似于参数;
泛型是一种用于 增强类型(types)、接口(interfaces)、函数类型等能力的非常可靠的手段
泛型别名中使用泛型
泛型: 定义类型别名后 加上 <类型参数> 就是泛型语法,使用时传入具体的类型即可
是一个变量,可以随意命名,建议遵循大驼峰命名法
和类型别名配合,在类型别名后加上泛型语法,然后类型别名内就可以使用这个类型参数
泛型可以提高类型的复用性和灵活性
type User = {name:string;age:number
}type Goods = {id:number;goodsName:string;
}type Data<T> = {msg:string;code:number;data:T
}// 使用类型
type UserData = Data<User>
type GoodsData = Data<Goods>
泛型接口
- 在接口名称的后面加上<类型参数>,那么这个接口就编程了泛型接口,接口中所有成员都可以使用类型参数
interface IdFn<T> {id:()=>T;ids:()=>T[]
}const idObj:IdFn<number> = {id(){return 1},ids(){ return [1,2,3]}
}
内置的泛型接口
// 内置的泛型接口
const arr7 = ["a","b"]
// Ts有类型推断能力 其实可以看作 const arr7:Array<number> = [1,2,3]
const arr8:Array<number> = [1,2,3]
// 通过Ctrl + 鼠标左键 去查看内置的泛型接口
arr7.push("7")
arr7.forEach(item=>(console.log(item)))
泛型函数
- 函数名称后面加上,T是类型参数
- 当我们调用函数的时候,传入具体的类型,T捕获这个类型,函数任意位置都可以使用该类型
- 好处
- 让函数可以支持不同类型(复用),保证类型是安全的
- 调用函数,传入的数据可以推断出你想要的类型,就可以省略泛型
// 泛型函数
function getId<T>(id:T):T{return id
}let id1 = getId<number>(1)
// TS会进行类型推断 参数的类型作为泛型的类型
let id2 =getId("2")let id3 = getId({name:"zs"})