typescript 第一天
1. typescript介绍
- JavaScript的超集
- JavaScript为动态语言
- typescript为静态语言
- typescript:JavaScript+类型支持
1.1 动态语言和静态语言
- 静态语言:编译器做类型检查
- 动态语言:执行期做类型检查
1.2 ts的工具包
- 安装命令
npm i -g typescript
- ts转换js命令
tsc ts文件名称
1.3 直接执行ts的工具包
- 安装命令
npm i -g ts-node
- 执行命令
ts-node ts文件名称
2. ts的常用类型
2.1 类型注解
let age: number=18
- :number 为类型注解
- 作用:为变量添加类型约束,给定变量age的值只能是number
2.2 常用类型概述
js已有的类型
原始类型:number/string/boolean/null/undefined/symbol
对象类型:object
TS新增类型
联合类型
自定义类型(类型别名)
接口
元组
字面量
枚举
void
any
2.3 原始类型的类型注解
- 简单数据类型
直接在前面加上:
如 :number
- 数组
number[] :number类型数组
Array :字符串类型数组
- Ts的类型注解
联合类型:(number|string):表示里面类型的任意一种
类型别名:为任意类型起别名:
- type CustomArray=(number|string)
- 函数类型
- 函数参数
- 函数返回值
function add(num1:number,num2:number):number{
return num1+num2
}
- 自己查看,加深记忆
// 基本数据类型
let aage: number =18
let aname: string ='myname'
let isLoading: boolean =false
// 复杂数据类型
// 数组类型 ,第一种写法
let numbers: number[] =[1,2,34,4]
// 数组类型,第二种写法
let arr: Array<number>=[1,3,4]
// 联合类型,数组中的数据即有number也有string
let strings:(number|string)[]=[1,'2222','blue']
// 类型别名
type customArray=(number|string)[]
// 使用类型别名来定义数组
let arr1: customArray=[1,2,'333']
// 等价于
let arr2: (number|string)[]=[1,2,'333']
//函数类型
// 为函数指定参数类型和返回值类型
function add(num1: number,num2: number): number{
return num1+num2
}
// 函数表达式情形
const add1=(num1:number,num2:number): number=>{
return num1+num2
}
console.log(add(1,3));
console.log(add1(1,3));
// 同时指定参数和返回值类型
const add2: (num1:number,num2:number)=>number=(num1,num2)=>{
return num1+num2
}
console.log(add2(1,2));
// void 返回值类型
function greet(name: string): void{
console.log('hello',name);
}
greet('peter')
// 可选参数,加上?
function mySlice(start?: number,end?: number): void{
console.log('start',start,'end',end);
}
mySlice()
mySlice(1)
mySlice(1,3)
// 对象类型
// // 属性类型写在一行中时,使用分号分割
// let person: {name: string;age: number;sayHi(): void}={
// name:'peter',
// age:18,
// sayHi() {
// },
// }
// 属性类型可以分行写,不用分号,函数可以使用给参数和返回值同时声明的类型
let person:{
name: string
age:number
sayHi:()=>void
}={
name:'peter',
age:18,
sayHi() {
},
}
// 对象的可选属性
// 可选属性与函数的可选参数写法一致
function myAxios(config:{url: string;method?: string}): void{
console.log(config);
}
myAxios({
url:'1234'
})
// 接口,类似类型别名
// 接口只可用于对象类型
// 而类型别名可用于所有类型
interface IPerson{
name: string
age: number
sayHi(): void
}
let person1: IPerson={
name:'peter',
age:19,
sayHi() {
},
}
// 接口继承
interface Point2D{
x: number
y: number
}
// Point3D 继承了Point2D
interface Point3D extends Point2D{
z: number
}
let point: Point3D={
x:1,
y:1,
z:1
}
// 元组,特殊的数组
// 明确规定只有两个值
let position: [number,number]=[23,114]
2.4 接口
- 类似类型别名,不过接口用于描述对象对象的类型
- 而类型别名可以描述所有类型
2.5 元组
- 元组类型是另一种类型的数组,它确切的知道包含多少个元素,以及特定索引对应的类型
2.6 类型断言
- 有时候你会比ts更加明确一个值的类型,此时可以使用类型断言指定更具体的类型
const link = document.getElementById('link') as HTMLAnchorElement
console.log(link.href);
- as 关键字实现类型断言
2.7 字面量类型
// 字面量为number
let aname=18
// 字面量为'hello world'
const str: 'hello world'='hello world'
function changeDirection(direction :'up'|'down'|'left'|'right'){
console.log(direction);
}
changeDirection('down')
2.8 枚举类型
// enum虽然为枚举类型,但存储在计算机中的为自增的数字,默认从零自增
// 可以为enum的值赋予初始值
// enum Direction{'up'=1,'down','left','right'}
// function changeDirection(direction: Direction){
// console.log(direction);
// }
// // changeDirection(Direction.up) // 值为0
// // 值现在从1开始自增
// changeDirection(Direction.up) //1
// changeDirection(Direction.down) //2
// changeDirection(Direction.left) //3
// changeDirection(Direction.right) //4
// 2. 设置枚举的值为字符串
enum Direction{up='up',down='down',left='left',right='right'}
function changeDirection(direction: Direction){
console.log(direction);
}
// changeDirection(Direction.up) // 值为0
// 值现在从1开始自增
changeDirection(Direction.up) //up
changeDirection(Direction.down) //down
changeDirection(Direction.left) //left
changeDirection(Direction.right) //right
- 说明
枚举是 TS 为数不多的非 JavaScript 类型级扩展(不仅仅是类型)的特性之一。
因为:其他类型仅仅被当做类型,而枚举不仅用作类型,还提供值(枚举成员都是有值的)。
也就是说,其他的类型会在编译为 JS 代码时自动移除。但是,枚举类型会被编译为 JS 代码!
2.9 typeof 操作符
console.log(typeof "hello world"); // string js的类型
// 当在类型上下文中使用typeof时,解析的是ts的类型
let p: {x:number,y:number}={
x:1,
y:2
}
function formatPoint(point: typeof p){
}
// 等价于
// function formatPoint(point: {x:number,y:number}){
// }
3.0 class类型
class Person{
age:number // 声明number类型
sex='男' // 赋初始值,类型推断
// 只读修饰符,默认值为666,只可以通过构造函数来修改初始值
readonly str:string="666"
// 构造函数
constructor(age:number,sex:string,str: string){
this.age=age
this.sex=sex
this.str=str
}
}
const p=new Person(19,'女','777')
-
class类中的方法类型注解与普通函数相同
-
class类可以extends(继承父类)和 implement(实现接口)
3.1 class中的方法和属性的可见修饰符
- public:公开的,可以被任何地方访问,默认值
- protected:表示受保护的,仅对其声明所在类和子类中(非实例对象)可见
- private:表示私有的,只在当前类可见,对实例对象以及子类也是不可见的
4. 类型兼容性
TS采用的是结构化类型系统,类型检查关注的是值所具有的形状,如果两个对象具有相同的形状,则认为他们属于同一类型
4.1 对象类型的成员之间的兼容性
成员多的可以赋值给成员少的
4.2 接口之间的类型兼容性
类似于class,class于interface之间也可以兼容
- 成员多的可以赋值给成员少的
4.3 函数之间的类型兼容性
- 参数个数
- 参数少的可以赋值给参数多的
- 参数类型
- 相同位置的参数类型要相同(原始类型)或兼容(对象类型)
- 对象类型,将对象拆开,把每个属性看做一个个参数,则参数少的可以赋值给参数多的
- 返回值类型
- 关注返回值本身
- 原始类型:需要相同
- 对象类型,成员多的可以赋值给成员少的
- 关注返回值本身
4.4 交叉类型
- 类似接口继承,用于组合多个类型为一个类型
- 处理同名属性和方法时的结果不同
- 接口继承为报错
- 而交叉类型会联合两种类型
- 处理同名属性和方法时的结果不同
// 交叉类型 类似接口继承,用于将多个类型合并为一个类型
interface Person{
name:string
}
interface Contact{
phone:string
}
type PersonDetail =Person & Contact
let a:PersonDetail={
name:'123',
phone: '666'
}
// 交叉类型处理同名属性的兼容
interface A{
fn:(value: number)=> string
}
interface B{
fn:(value: string)=> string
}
// 交叉,
type C =A&B
let c:C={
// fn相当于
fn(value: number|string){
return ''
}
}
5.泛型
泛型是可以在保证类型安全的前提下,让函数与多种类型一起工作,从而实现复用,常用于:函数,接口,class
5.1 泛型初体验
// 创建泛型函数
function id<Type>(value: Type): Type{
return value
}
// 以number类型调用
const num=id<number>(1)
// 以string类型调用
const str=id<string>('123')
// 简化泛型调用
const num1=id(10)
const str1=id('1234')
// 泛型约束,泛型只能打印值的本身,本身的属性无法获取(不知道何种类型,其拥有的属性也不同)
function id2<Type>(value: Type[]): Type[]{
// 数组一定有length属性,即使不是数组
console.log(value.length);
return value
}
5.2 添加泛型约束
interface Ilength{
length:number
}
// 添加约束,使传入的参数需要满足拥有length这个属性
function id<Type extends Ilength> (value:Type ) :Type{
return value
}
id('1233')
id(['1','2',3])
5.3 多个类型之间的泛型约束
// 添加了两个类型变量,第二个类型变量用逗号分割
// keyof关键字接收一个对象类型,生成其键名称的联合类型
// 这里Key被约束,只能是Type的键中的联合类型
function getProp<Type,Key extends keyof Type>(obj:Type,key:Key){
return obj[key]
}
getProp({name:'jack',age:18},'name')
getProp({name:'jack',age:18},'name')
// 如果是基本类型,可以获取其包装类型的方法
getProp(18,'toFixed')
// 字符串类型,
// 这里的1代表索引,即'abc'[1]
getProp('abc',1)
5.4 泛型接口
// 泛型接口,在接口名称添加尖括号和类型
interface idFunc<Type>{
id:(value:Type)=>Type
ids:()=>Type[]
num:Type
}
let obj:idFunc<number>={
num: 100,
id(value){
return value
},
ids(){
return [1,2,3]
}
}
const arr:number[]=[1,2,3]
arr.forEach((value,index)=>{
})
5.5 泛型类
// class GenericNumber<NumberType>{]
// defaultValue:NumberType
// add:(value:NumberType)=> NumberType
// }
// // 明确指定类型
// const myNum=new GenericNumber<number>()
// myNum.defaultValue=10
// 当存在construct方法时,可以通过传递的参数推断处NumType
class GenericNumber<NumberType>{
defaultValue:NumberType
add:(value:NumberType)=> NumberType
constructor(value:NumberType){
this.defaultValue=value
}
}
// 不需要明确指定类型
const myNum=new GenericNumber(100)
myNum.defaultValue=10
5.6 索引签名类型
- 当无法确定对象中有那些属性是(或者说对象中可以出现任意多个属性),此时需要用到索引签名类型
// 无法确定属性中有多少类型时,可以使用索引签名类型
// 前置知识,js中的键的类型为string
interface AnyObject{
[key:string]:number
}
let anyObj={
a:1,
b:2
}
// 数组本身是一个接口,其键和对象不同,为number类型,即数组的索引
interface MyArray<Type>{
[key:number]:Type
}
const myArr=[1,2,3]
console.log(myArr[0]);
5.7 基于旧类型创建新类型(对象类型)映射类型
// 基于旧类型创建新类型,不能用于接口中
type TypeProps='a'|'b'|'c'
type Type1={a:number,b:number,c:number}
// 使用映射类型
type Type2={[key in TypeProps]:number}
// 根据对象类型创建新类型
type Type3={[key in keyof Type1]:string}