配置
安装tsc工具进行编译 npm i typescript -g
查看版本号:tsc -v
编译ts代码-需要使用tsc编译之后才能运行,TS为JS的衍生,浏览器不能直接识别TS语法:tsc xxx.ts
运行ts代码:node xxx.js
或者直接运行ts代码——ts-node xxx.ts
解除2584报错:
tsc --init
在生成的配置文件中的lib中,添加['DOM','ESNext']
一、基础类型
- srting
- number
- Boolean
- undefined
- null
- any:可以定义任何类型,但是相当于关闭了类型判断,所以不建议使用
//类型注解
let str: string = '123'
let num: number = 123
let bol: boolean = false
let und: undefined = undefined
let nul: null = null
//尽量避免在ts中使用any 使用any就是关闭类型检查
let a: any = true
二、类型推断与void类型
- 直接定义且赋值,ts会自动判断赋值的类型
- void类型,返回没有返回值的类型
- never类型,表示永远也无法达到的类型(比如报错
- unknown未知的类型,数据来源于外部(后端接口)
//类型推断
let str1 = 'haha' //自动推断出来了str1是字符串类型
//void类型 返回没有返回值的类型
function fn(a: string): void {
console.log(a)
}
//never类型 表示永远无法达到的类型
function err(): never {
throw Error('wrong')
}
//unknown 未知的类型 数据来源于外部(后端接口)
let res: unknown
res = {}
export { }
三、对象,数组,函数
- Object/{}表示的是基类,object表示的是普通对象
- 定义数组时可以给数组指定类型arr:string arr:number
- 或者使用Array<boolean>来指定数组的内容类型
- 数组也可以使用any类型来兼容数组中不同类型的数组元素(不推荐)
- 方法函数在写的时候需要给每一个传入参数和return结果规定类型
let obj: object = { a: 1, b: 2 };
let arr: string[] = ["1", "2", "3"];
let arr1: number[] = [1, 2, 3];
let arr2: Array<boolean> = [false, true]
let arr3: any[] = [1, '2', true]
function fn(a: string, b: string): number {
return Number(a) + Number(b)
}
fn('1', '2')
export { }
四、字面量
- 数字,字符串,布尔值类型
- 只能给字面量类型赋值字面量这个值,不能使用其他值
- 定义一个eat函数,他只能接收参数food,food值只能是rice noodle中的一个
- 联合类型function eat(food:'rice'|'noodle'),只要是其中一个即可
- 定义一个联合定义,可以使用type类型别名
type x = string | number
function add(a:x,b:x):number {
return Number(a) + Number(b)
}
add(2,'3')
// 联合类型food
type f = 'rice'|'noodle'
// 方法eat
function eat(food:f) {
console.log(food)
}
eat('rice') // 只能传入'rice'|'noodle'其中之一的值
- 类型别名常用来定义对象leixing
type obj = {
a:number;
b:string
}
let p:obj = {
a:666,
b:'777'
}
- 在ts中,如果需要换一种类型,都可以使用类型别名这种形式
五、枚举类型
很多时候,希望将一些字面量的联合类型列举起来,可以枚举的数据类型
- 定义一个eat方法,可以吃的东西——苹果,香蕉,桃子。枚举类型——联合列举字面量类型
enum Fruit = {
APPLE = 'apple',
BANANA = 'banana',
PEACH = 'peach'
}
function eat(food:Fruit) {
console.log(food)
}
eat(Fruit.APPLE)
- 枚举类型enum一般使用大写开头
- 枚举一般分为字符串枚举和数字枚举
- 枚举中有一个APPLE属性,它的值为'apple'
- 给eat传入的实参必须满足三中类型之一
- 代码语言中,经常会使用数字来表达状态。xhr.readyState监听接口是否正确返回数据(0=>4)
enum State {
// 不赋值的话,默认赋值从零开始依次递增
BEGIN,
READY,
GET,
HANDLE,
RESPONSE
}
if(xhr.state = State.BEGIN) {
'请求未发送'
}else if(xhr.state = State.READY) {
'请求准备发送'
}...
六、接口
接口,接口描述对象或者函数
- 定义Animal,类型上有字符串name,数字age,联合类型 男|女 gender
- 接口有专用关键字interface
- 使用接口进行对对象类型的描述
interface Animal {
name:string;
age:number;
gender:'男'|'女'
}
let a1:Animal = {
name:'peiqi',
age:3,
gender:'女'
}
a1.gender
- 定义对象类型时分隔符为分号
- 与类型别名的区别
-
- 类型别名有=赋值,接口没有
- 接口可以扩展,再interface一次Animal,再给它增加一组key,value,并不是覆盖原有类型,而是在原有类型基础上增加一个类型
interface Animal {
height:number
}
- 由于接口的可扩展性,所以通常使用接口来描述对象
-
- 类型别名想要扩展需要使用交叉类型(和& 联合类型为或|)
- 定义两个类型别名,在第三个类型别名中交叉两个类型别名1&2,即可扩展,但很麻烦
- 接口可以继承
-
- 如果定义一个新的接口Dog,它可以继承自Animal,使用extends进行继承,dog就会拥有所有Animal的属性
interface Dog extends Animal {
bark():void; /* 定义bark方法,没有返回值 */
}
let a3:Dog = {
name:'peiqi',
age:3,
gender:'女',
height:100,
bark() {
console.log('wangwang')
}
}
// 调用a3,a3.之后就会有a3的属性弹出
-
- 类型别名无法继承
- 实际应用中,对象是通过构造函数(类)来生成的,并不是直接通过字面量形式生成。类可以实现接口
-
- 类是如何实现接口的:使用implements进行类对于接口的实现
class MyDog implements Dog {
name: string
age:number
gender:'男'|'女'
height:number
constructor(
_name:string;
_age:number;
_gender:'男'|'女',
_height:number,
){
this.name = _name
this.age = _age
this.gender = _gender
this.height = _height
}
bark(): void{
console.log(this.name)
}
}
let a4:MyDog = new MyDog('xiaohei',4,'女',40)
a4.bark()
-
- 在类中定义——如果添加具体属性,需要先声明一下(ts中添加任何属性都必须声明。继承可以不用重新声明,但是class MyDog implements Dog这种必须要重新声明)
- 类中定义成员:普通属性应该定义在Constructor上,方法应该定义在原型身上
- 前面的age是定义的对象身上的属性,后面一个是传入的参数
- constructor后面进行的是传入的参数,是用来描述实参的形参
- this后面的表示要给成员添加什么样的属性。
- 方法是定义在原型上的,不用写this
- 前后定义的类型必须一致,否则无法这样赋值。
- 接口的索引访问:如果想要某一个索引不写值也可以运行,使用?,?表示这个索引可以有也可以没有
- Phone中有两个必须的索引,onSale是可选的索引,props是可以添加的自定义索引
interface Phone {
title:string;
price:number;
onSale?:boolean // ?表示这个索引可以有也可以没有
[props: string] : any // 可以添加任意多的索引,索引类型可以自己定义
}
let p1:Phone = {
title:'iphone15',
price: 5999
}
let p2:Phone = {
title:'iphone15',
price: 5999,
onSale: false
}
let p3:Phone = {
title:'iphone15',
price: 5999,
weight: 400
}
- 索引访问类型:可以访问索引类型。
// 11-索引访问类型
type s = Phone['price'] /* s为number */
// 12-访问Phone中的索引
type k = keyof Phone
let k1:k = 'onSale'
let k2:k = 'price'
// 13-接口描述函数
interface Fn {
(a:number):number /* 参数为数字,返回值也是数字。接口描述函数用冒号链接 */
}
let fn:Fn = (a:number):number => {
return a
}
fn(123)
- 收集到的phone这个接口上所有的索引值字面量组成的联合类型,只能访问Phone中的索引
- 接口描述函数:定义接口,实现接口,调用接口
七、元组
ts中元组可以理解为固定了长度和类型的数组(长度为3,类型分别为字符串,数字,布尔)
可变元组:如果只需要最后三个元素为[string, number, boolean]
// 1
let turple:[string, number, boolean] = ['haah', 666, true]
// 2
let t2:[...any[], string, number, boolean] = [{}, 188, 'eee', 'haah', 666, true]
八、类型断言
类型注解,类型推断前面写了,类型断言:
let b: number | string = '123'
let len:number = (b as string).length
b既可以是字符串类型也可以是数字类型,如果想访问b的length类型——如果b为number类型,则length不存在,只有b为string类型时,才可以访问b身上的length——当一个变量可能存在多个类型时,可以使用断言来缩小它的类型。将b断言为字符串类型,即可访问length
九、泛型
ref定义的变量上肯定有一个value属性,value应该是什么类型呢?需要一个什么都可以的类型,但绝对不能是any
interface Ref<T> {
value: T // 定义Ref接口和定义了任意一个类型T,其实写什么都行
}
let r1:Ref<string> = {
value:'haha'
}
let r2:Ref<number> = {
value:321
}
// fn传入的参数和返回的参数是一个类型
function fn<K>(a:K):K {
return a
}
fn('haha')
fn(123)
定义Ref接口和定义了任意一个类型T,其实写什么都行。这样定义的类型可以传入任意类型
在使用接口的时候,需要定义需要传入的类型,将泛型具体化。相当于泛型是一个形式参数,泛型是用来传递类型的。在具体使用的时候需要传入实际参数(具体的类型)
如果在定义方法的时候定义它需要传入和返回同一种类型的参数,则在实际使用的时候传入什么样的参数,则传入和返回的都是这个参数的类型
练习
实现一个类ArrayList,这个类具有数组的添加元素(add)和访问数组(get)的功能。利用泛型来实现这样一个类
class ArrayList<T> {
arr:T[] // 定义一个泛型空数组
index: number = 0 // 表示数组元素的个数,不表示索引
constructor(){
this.arr = [] // 开始时置空数组
}
// 添加元素
add(element:T){
this.arr[this.index++] = element // 先执行再index递增,添加了元素就增加长度
}
// 访问元素
get(index:number){
return this.arr[index] ? this.arr[index] : undefined
}
}
let arr1 = new ArrayList<number>()
arr1.add(1)
console.log(arr1.arr) // [1]
arr1.add(2)
console.log(arr1.arr) // [1,2]
console.log(arr1.arr[1]) // 2
console.log(arr1.arr[2]) // undefined
泛型和泛型约束
写一个方法,可以访问对象身上的属性
function getProperty<T extends object,K extends keyof T>(obj:T,key:K){
return obj[key]
}
getProperty({a:1,b:2},'a') // key只能是a或者b,
obj表示整个对象,obj的T extends object约束T为普通对象类型。key的K约束为K extends keyof T,在给obj给了内容后,key只能是obj的key