编译TypeScript
浏览器不能直接运行ts代码,需要编译为js再交由浏览器解析执行
1.命令行编译
第一步:全局安装
npm i typescript -g
第二步:使用命令编译目标文件
tsc demo.ts
2.自动化编译
第一步:初始化ts编译控制文件
tsc --init
/*
1.在跟目录下会生成一个tsconfig.json配置文件,其中包含多种编译配置
2.默认编译的js版本是es7
*/
第二步:监测目录中.ts文件的变化
tsc --watch或tsc -w
类型声明
使用:来对变量或函数形参,进行类型声明
let a: string //变量a只能存储字符串
let b: number //存储数值
let c: boolean //存储布尔值
function demo(x:number,y:number):number{//x,y,返回值都必须是数字
return x+y
}
Ts中的数据类型
1.包含所有的js类型(string,number,boolean,null,undefined,bigint,symbol,object)
2.六个新类型(any,unknown,never,void,tuple,enum)
3.两个用于自定义类型的方式(type,interface)
注意点:
在js中的这些内置构造函数:Number,String,Boolean,用于创建对应的包装对象,在日常开发时很少使用,在ts中也是一样的,所以在ts中进行类型声明时,通常都是用小写的number,string,boolean
Ts中常用类型及语法
1.any:任意类型,一旦将变量类型限制为any,意味着放弃了对该变量的类型检查
// 明确的表示a的类型是 any —— 【显式的any】
let a: any
// 以下对a的赋值,均无警告
a = 100
a = '你好'
a = false
// 没有明确的表示b的类型是any,但TS主动推断出来b是any —— 隐式的any
let b
//以下对b的赋值,均无警告
b = 100
b = '你好'
b = false
/* 注意点:any类型的变量,可以赋值给任意类型的变量 */
let c:any
c = 9
let x: string
x = c // 无警告
2.unknown:未知类型,适用于:期初不确定数据的具体类型,要后期才能确定
1.unknown可以理解为一个类型安全的any
let a: unknown
//一下对a的赋值,均符合规范
a = 100
a = false
a = '你好'
//设置x的类型为string
let x:string
x = a//警告:不能将类型unknown分配给类型string
2.unknown会强制开发者在使用之前进行类型检查,从而提供更强的类型安全性
let x:string
let a:unknown
a = 'hello'
//把变量a的值付给变量x
x = a //警告:a的类型是unknown
/*解决方式*/
//第一种:加类型判断
if(typeof a ==='string'){
x = a
console.log(x)
}
//第二种方式:加断言
x = a as string
x = <string>a
3.读取any类型数据的任何属性都不会报错,而unknown正好与之相反
let str1:string
str1 = 'hello'
str1.toUpperCase()//无警告
let set2:any
set2= 'hello'
set2.toUpperCase()//无警告
let set3:unknown
set3= 'hello'
set3.toUpperCase()//警告:str3的类型为未知
//使用断言强制制定str3的类型为string
<string>set3.toUpperCase()//无警告
3.never:任何值都不是,即:不能有值,例如undefined,null,‘’,0都不行
1.几乎不用never去直接限制变量,因为没有意义
//指定a的类型为never,那就意味着a以后不能存任何的数据了
let a:never
//以下对a的所有赋值都会有警告
a = 1
a = true
a = undefined
a = null
2.never一般是ts主动推断出来的
//指定a的类型为string
let a:string
a = 'hello'
if(typeof a === 'string'){
console.log(a.toUpperCase())
}else{
console.log(a)//ts会推断出此处的a是nerve,因为没有任何一个值符合此处的逻辑
}
3.never也可用于限制函数的返回
//限制throwError函数不需要有任何返回值,任何只都不行,包括undefined,null
function throwError(str:string):never{
throw new Error('程序异常退出:'+str)
}
4.void:函数不返回任何值,调用者也不应该依赖其返回值进行任何操作
1.void通常用于函数返回值声明
function logMessage(msg:string):void{
console.log(msg)
}
logMessage('你好')
/*
注意:编码者没有编写return指定函数返回值,所以logMessage函数是没有显式返回值的,但会有一个隐式返回值undefined,虽然函数返回类型为void,但也是可以接受undefined的
*/
2.以下写法均符合规范
function logMessage(msg:string):void{
console.log(msg)
}
function logMessage(msg:string):void{
console.log(msg)
return
}
function logMessage(msg:string):void{
console.log(msg)
return undefined
}
3.限制函数返回值时,是不是undefined和void就没有区别呢?-----有区别.因为 返回值类型为void的函数,调用者不应该依赖其返回值进行任何操作
function logMessage(msg:string):void{
console.log(msg)
}
let result = logMessage('你好')
if(result){//这里会报错,无法测试void类型的表达式的真实性
console.log('logMessage有返回值')
}
5.object:时间开发中用的相对较少,因为范围太大
1.object(小写):所有非原始类型,可存储:对象.函数,数组等,由于限制的范围比较宽泛,在实际开发中使用的相对较少
let a:object //a的值可以是任何非原始类型,包括:对象,函数,数组等
//以下代码,是将非原始类型赋给a,所以均符合要求
a = {}
a = {name:'zs'}
a = [1,2,3,4,5]
a = function(){}
a = new String('123')
class Person{}
a = new Person()
//以下代码,是将原始类型赋给a,有警告
a = 1 // 警告:不能将类型“number”分配给类型“object”
a = true // 警告:不能将类型“boolean”分配给类型“object”
a = '你好' // 警告:不能将类型“string”分配给类型“object”
a = null // 警告:不能将类型“null”分配给类型“object”
a = undefined // 警告:不能将类型“undefined”分配给类型“object”
2.Object(大写):所有可以调用Object方法的类型(除了undefined和null的任何值)由于限制的范围实在太大,所以实际开发中使用频率极低
let b:Object //b的值必须是Object的实例对象(除去undefined和null的任何值)
// 以下代码,均无警告,因为给a赋的值,都是Object的实例对象
b = {}
b = {name:'张三'}
b = [1,3,5,7,9]
b = function(){}
b = new String('123')
class Person {}
b = new Person()
b = 1 // 1不是Object的实例对象,但其包装对象是Object的实例
b = true // truue不是Object的实例对象,但其包装对象是Object的实例
b = '你好' // “你好”不是Object的实例对象,但其包装对象是Object的实例
// 以下代码均有警告
b = null // 警告:不能将类型“null”分配给类型“Object”
b = undefined // 警告:不能将类型“undefined”分配给类型“Object”
3.声明对象类型
1.实际开发中,限制一般对象,通常使用以下形式
//限制person1对象必须有name属性,age为可选属性
let person1:{name:string,age?:number}
//含义同上,也能用分号做分隔
let person2:{name:string;age?:number}
//含义同上,能用换行做分隔
let person3:{
name:string
age?:number
}
//如下赋值均可以
person1 = {name:'李四',age:18}
person2 = {name:'张三'}
//如下赋值不合法,因为person3的类型限制中,没有对gender属性的说明
person3 = {name:'王五',gender:'男'}
2.索引签名:允许定义对象可以具有任意数量的属性,这些属性的键和类型是可变的,常用于:描述类型不确定的属性
//限制person对象必须有name属性,可选age属性但值必须是数字,同时可以有任意数量,任意类型的其他属性
let person:{
name:sting
age?:number
[key:sting]:any //索引签名,完全可以不用key这个单词,换成其他的也可以
}
//合法赋值
person = {
name:'张三',
age:18,
gender:'男'
}
4.声明函数类型
let count:(a:number,b:number) => number
count = function(x,y){
return x+y
}
/*
ts中的=>在函数类型声明时表示函数类型,描述其参数类型和返回类型
js中的=>是一种定义函数的语法,是具体的函数实现
*/
5.声明数组类型
let arr1:string[]
let arr2:Array<string>
arr1 = ['a','b','c']
arr2 = ['a','b']
//上述代码中的Array<string>属于泛型
6.tuple:(元祖)是一种特殊的数组类型,可以存储固定数量的元素,并且每个元素的类型是已知的且可以不同.元祖用于精确描述已组值的类型, ?表示可选元素
//第一个元素必须是string类型,第二个元素必须是number类型
let arr1:[string,number]
//第一个元素必须是number类型,第二个元素是可选地,如果存在,必须是boolean类型
let arr2:[string,boolean?]
//第一个元素必须是string类型,后面的元素可以是任意数量的string类型
let arr3:[number,...string[]]
// 可以赋值
arr1 = ['hello',123]
arr2 = [100,false]
arr2 = [200]
arr3 = [100,'hello','world']
arr3 = [100]
// 不可以赋值,arr1声明时是两个元素,赋值的是三个
arr1 = ['hello',123,false]
7.enum:(枚举)可以定义一组命名常量,他能增强代码的可读性
如下代码的功能是:根据调用walk时传入的不同参数,执行不同的逻辑,存在的问题是调用walk时传参时没有任何提示,编码者很容易写错字符串内容;并且用于判断逻辑的up、down、left、right是连续且相关的一组值,那此时就特别适合使用 枚举(enum)。
function walk(str:string) {
if (str === 'up') {
console.log("向【上】走");
} else if (str === 'down') {
console.log("向【下】走");
} else if (str === 'left') {
console.log("向【左】走");
} else if (str === 'right') {
console.log("向【右】走");
} else {
console.log("未知方向");
}
}
walk('up')
walk('down')
walk('left')
walk('right')
1.数字枚举:最常见的枚举类型,其成员的值会自动递增,其数字枚举还具备反向映射的特点,在下面代码的打印中,不难发现:可以通过值来获取对应的枚举成员名称 。
enum Direction{
Up,
Down,
Left,
Right
}
console.log(Direction) // 打印Direction会看到如下内容
/*
{
0:'Up',
1:'Down',
2:'Left',
3:'Right',
Up:0,
Down:1,
Left:2,
Right:3
}
*/
// 反向映射
console.log(Direction.Up)
console.log(Direction[0])
// 此行代码报错,枚举中的属性是只读的
Direction.Up = 'shang'
也可以指定枚举成员的初始值,其后的成员值会自动递增
enum Direction {
Up = 6,
Down,
Left,
Right
}
console.log(Direction.Up); // 输出: 6
console.log(Direction.Down); // 输出: 7
使用数字枚举完成刚才walk函数中的逻辑,此时我们发现: 代码更加直观易读,而且类型安全,同时也更易于维护。
enum Direction {
Up,
Down,
Left,
Right,
}
function walk(n: Direction) {
if (n === Direction.Up) {
console.log("向【上】走");
} else if (n === Direction.Down) {
console.log("向【下】走");
} else if (n === Direction.Left) {
console.log("向【左】走");
} else if (n === Direction.Right) {
console.log("向【右】走");
} else {
console.log("未知方向");
}
}
walk(Direction.Up)
walk(Direction.Down)
2.字符串枚举:枚举成员的值是字符串
enum Direction {
Up = "up",
Down = "down",
Left = "left",
Right = "right"
}
let dir: Direction = Direction.Up;
console.log(dir); // 输出: "up"
3.常量枚举
8.type:可以为任意类型创建别名,让代码更简洁,可读性更强,同时能更方便地进行类型复用和扩展
1.基本用法:类型别名使用type关键字定义,type后跟类型名称,例如下面代码中num是类型别名
type num = number;
let price:num
price = 100
2.联合类型:是一种高级类型,他表示一个值可以是几种不同类型之一
type Status = number | string
type Gender = '男'|'女'
function printStatus(status:Status){
console.log(status)
}
function logGender(str:Gender){
console.log(str)
}
printStatus(404)
printStatus('200')
printStatus('501');
logGender('男')
logGender('女')
3.交叉类型:允许将多个类型合并为一个类型.合并后的类型将拥有所有被合并类型的成员.交叉类型通常用于对象类型
//面积
type Area = {
height:number;//高
width:number;//宽
}
//地址
type Address = {
num:number;//楼号
cell:number;//单元号
room:string;//房间号
}
//定义类型House,且House是Area和Address组成的交叉类型
type House = Area&Address
const house:House={
height: 180,
width: 75,
num: 6,
cell: 3,
room: '702'
}