认识TS
- JavaScript没有加入类型检测这一机制
- 2014年,Microsoft微软 推出了TypeScript1.0版本
- TypeScript 是拥有 类型 的 JavaScript超集
- 错误提示越早越好
- 编写代码(IDE)>>> 代码编译(TS)>>> 代码运行 (浏览器)
- TypeScript最终会被编译成JavaScript代码,所以你并不需要担心它的兼容性问题,在编译时也可以不借助于Babel这样的工具
- 明确限制 数据类型 和 个数
TypeScript的编译环境
全局安装
- 前提保证有node环境
npm install typescript -g
- 查看版本
tsc --version
编译
tsc编译
//进入目录
tsc index.ts
//浏览器或者Node环境下运行JavaScript代码
node index.js
webpack
配置本地的TypeScript编译环境和开启一个本地服务,可以直接运行在浏览器上
ts-node 库
// 安装 ts-node
npm install ts-node -g
//另外ts-node需要依赖 tslib 和 @types/node 两个包
npm install tslib @types/node -g
ts-node index.ts
Ts变量的声明
var/let/const 标识符: 数据类型 = 赋值;
- 不推荐用var ,没有块级作用域
- 首次赋值时的 数据类型 不可再变
类型推论
- 第一次声明一个标识符时,如果直接赋值(省略数据类型),会根据所赋值的类型 推论 出标识符的类型注解 (鼠标悬浮显示),这个过程就 是 类型推论
- let 类型推论, 推论出的是 通用类型
- const类型推论,推论出的是 字面量类型
注意
- ts环境不管 多少个编译都是全局的
- 需要给每个ts文件都添加一个
export {}
,防止标识符(变量)冲突
Ts中的数据类型
number 数值型
- TypeScript和JavaScript一样,不区分整数类型(int)和浮点型(double)
- 双精度 64 位浮点值
- 支持二进制、八进制、十六进制
num = 100; //十进制
num = 0b110; //二进制
num = 0o555; //八进制
num = 0xf23; //十六进制
boolean 布尔型
- true 和 false
- 表达式
string 字符串类型
string
s 是小写- 使用单引号(’)或双引号(")来表示字符串类型
- 注意 JAVA 等强类型语言 用的是 双引号 (")
- 模板字符串
undefined 未定义类型
- 未定义的值
let u:undefined = undefined
null 空类型
- 已定义且值为空
let n:null = null
Array 数组类型
元素类型 []
let arr: number[] = [1, 2, 3];
const arr2: string[] = ["aaa","bbb","ccc"];
let arr: ( number | string | number[] )[] = [1, '4', [1, 2, 4]]
- 数组泛型
let list: Array<number> = [1, 2, 3];
let list: Array<string> = ["aaa","bbb","ccc"];
Object 对象类型
object
表示非原始类型,也就是除number
,string
,boolean
,symbol
,null
或undefined
之外的类型- json对象,函数,数组均可以
let obj: object = {
name: 'tom'
}
//object是通用类型,找不到里边的属性
console.log(obj.name) //报错
//但实际不会这么用
let obj={}=[]
- 严格限制对象结构,对象的每一个属性,都需要数据类型约束
let loginParams: {
userName: string,
userPass: string
};
loginParams = {
userName: "张三",
userPass: "1234"
};
let loginParams: {
userName: string,
userPass: string
} = {
userName: "张三",
userPass: 1234, //报错
age: 12 //报错
}
- 如果某一个属性是可有可无的,可以设置一个
?
let user: { name: string, age: number, gender?: string } = {
name: '张三',
age: 20
}
Symbol 类型
函数 类型
- 限制函数的参数类型 及 返回值,返回值没限制 会推论
function sum(num1: number, num2: number) {
return num1 + num2
}
等同于
function sum(num1: number, num2: number):number {
return num1 + num2
}
const res = sum(123, 321)
- (上下文类型)大多数 匿名函数 会根据上下文 推论 类型,不需要添加 类型注解
const names: string[] = ["abc", "cba", "nba"]
//function匿名函数 会根据上下文 推论 类型
names.forEach(function(item, index, arr) {
console.log(item, index, arr)
})
- 有限个参数无法确定是否会存在
?
function fn( a:number, b?:string ){ }
any 任意类型
- 无法确定一个变量的类型,并且之后可能会发生一些变化,使用 any
- any类型的变量进行任何事情都是合法的,包括获取不存在的属性、方法
- 可以给一个any类型的变量赋值任何的值
- 相当于关闭了类型检查
显式:let a:any = "aaa"
隐式: let a;
unknown 未知类型
- unknown类型的值上做任何事情都是不合法的,必须进行类型校验(类型缩小)
let a:unknown
let b:string
if(typeof a ==='string'){
b=a
}
never 从不会出现的值
function parseLyric() {
return [] //这个数组里边永远不会有内容, 所以类型为 never[]
}
- 封装工具可能会用到,更加严谨
- 其他人添加 类型,那么never 就会报错
// 其他时候在扩展工具的时候, 对于一些没有处理的case, 可以直接报错
function handleMessage(message: string | number | boolean) {
switch (typeof message) {
case "string":
console.log(message.length)
break
case "number":
console.log(message)
break
case "boolean":
console.log(Number(message))
break
default:
const check: never = message //如果上边有一个没有判断的类型,这里就会报错
}
}
void 类型
- 表示为空,空也是存在的,这就是和never的区别
- void通常用来指定一个函数是没有返回值的
- 指定了 void类型,可以
return undefined
(Ts编译器允许罢了)
function sum(num1: number, num2: number): void {
console.log(num1 + num2)
return undefined
// return 123 错误的做法
}
- 应用场景: 用来指定函数类型的返回值是void
// 1.定义要求传入的函数的类型
type ExecFnType = (...args: any[]) => void
// 2.定义一个函数, 并且接收的参数也是一个函数, 而且这个函数的类型必须是ExecFnType
function delayExecFn(fn: ExecFnType) {
setTimeout(() => {
fn("why", 18)
}, 1000);
}
// 3.执行上面函数, 并且传入一个匿名函数
delayExecFn((name, age) => {
console.log(name, age)
})
- 基于上下文类型推论的函数中的返回值如果是void类型, 并不会强制函数一定不能返回内容
const names = ["abc", "cba", "nba"]
//没有明确指定为 viod类型,所以可以 return
names.forEach((item: string, index: number, arr: string[]) => {
console.log(item)
return 123
})
tuple 元组类型
- 表示一个已知元素数量和类型的数组,各元素的类型不必相同
- 例:保存我的个人信息: zhangsan 男 18
/ 1.使用数组类型不合适: 数组中最好存放相同的数据类型, 获取值之后不能明确的知道对应的数据类型
const info1: any[] = ["why", "男", 18]
const value = info1[2]
console.log(value) //类型为any
/2. 使用对象类型(最多)不合适: 会添加多余的key值
const info2 = {
name: "why",
sex:"男" ,
age: 18
}
/ 3.使用元组类型,可以存放不同的数据类型, 取出来的item也是有明确的类型
const info3: [string, string, number] = ["why", "男", 18]
const value2 = info3[2] //类型为number
- 在函数中 使用元组类型 ( 函数的多个返回值 )
//指定多个返回值的类型
function useState(initialState: number): [number, (newValue: number) => void] {
let stateValue = initialState
function setValue(newValue: number) {
stateValue = newValue
}
return [stateValue, setValue]
}
const [count, setCount] = useState(10)
//如果不指定返回值类型的话,这个函数是 any类型 ,不严谨
setCount(100)
enum 枚举类型
- 定义数值集合
- 默认情况下,从
0
开始为元素编号。 你也可以手动的指定成员的数值
enum Color {Yellow, Red = 1, Green= 5, Blue}
let a: Color = Color[0]; //Yellow
let b: Color = Color[1]; //Red
let c: Color = Color[6]; //Blue
联合类型
- 满足多个类型中一个即可
- 使用
|
符号
let appTypeId:string|number=1;
appTypeId="1";
appTypeId=1;
特殊操作
开挂操作,不会报错
let appPlatformId:number=1;
// @ts-ignore
appPlatformId="1";
交叉类型
- 需要满足多个类型的条件
- 使用
&
符号
// 交叉类型: 两种(多种)类型要同时满足
type NewType = number & string // 没有意义 never类型
interface IKun {
name: string
age: number
}
interface ICoder {
name: string
coding: () => void
}
type InfoType = IKun & ICoder
const info: InfoType = {
name: "why",
age: 18,
coding: function() {
console.log("coding")
}
}
字面量类型
//将多个字面量类型联合起来 |
type Direction = "left" | "right" | "up" | "down"
const d1: Direction = "left"
// 例子: 封装请求方法,只能有post和get方法
type MethodType = "get" | "post"
function request(url: string, method: MethodType) {}
request("http://codercba.com/api/aaa", "post")
// TS细节
const info = {
url: "xxxx",
method: "post"
}
// 下面的做法是错误: info.method获取的是string类型
request(info.url, info.method)
// 解决方案一: info.method进行类型断言
request(info.url, info.method as "post")
// 解决方案二: 直接让info对象类型是一个字面量类型
const info2: { url: string, method: "post" } = {
url: "xxxx",
method: "post"
}
// 解决方案三: as const
const info2 = {
url: "xxxx",
method: "post"
} as const
request(info2.url, info2.method)
类型收窄(缩小)
- Type Narrowing 类型收窄 类型缩小
- 缩小比声明时更小的类型
- typeof padding === "number 可以称之为 类型保护(type guards)
- 常见的类型保护有
- typeof
function printID(id: number | string) {
if (typeof id === "string") {
console.log(id.length, id.split(" "))
} else {
console.log(id)
}
}
- 平等缩小(比如===, !==, ==, != )
type Direction = "left" | "right" | "up" | "down"
function switchDirection(direction: Direction) {
if (direction === "left") {
console.log("左:", "角色向左移动")
} else if (direction === "right") {
console.log("右:", "角色向右移动")
} else if (direction === "up") {
console.log("上:", "角色向上移动")
} else if (direction === "down") {
console.log("下:", "角色向下移动")
}
}
- instanceof,检查一个值是否是另一个值的“实例”
//instanceof: 传入一个日期, 打印日期
function printDate(date: string | Date) {
if (date instanceof Date) {
console.log(date.getTime())
} else {
console.log(date)
}
//或者 注意Date 判断是Object
// if (typeof date === "Object") {
// console.log(date.getTime())
// } else {
// console.log(date)
// }
}
- in操作符,指定的属性在指定的对象或其原型链中,则返回true
别名
type 类型别名
- 给 所有 数据类型起一个别名,方便复用
type MyNumber = number
const age: MyNumber = 18
type IDType = number | string
function printID(id: IDType) {
console.log(id)
}
type PointType = { x: number, y: number, z?: number }
function printCoordinate(point: PointType) {
console.log(point.x, point.y, point.z)
}
interface 接口声明
- 编译后Js文件中不会体现
- 只能声明 对象
// 接口: interface
interface PointType2 {
x: number
y: number
z?: number
}
function printCoordinate(point: PointType2) {}
- interface支持继承的,允许接口继承多个接口
interface IPerson {
name: string
age: number
}
interface IKun extends IPerson {
kouhao: string
}
const ikun1: IKun = {
kouhao: "你干嘛, 哎呦",
name: "kobe",
age: 30
}
- interface可以被类实现
class Person implements IPerson {
}
区别
- type类型使用范围更广,接口类型只能用来声明对象
- 在声明对象时,type不允许两个相同名称的别名;interface可以多次声明,最终取合集
type PointType1 = { //报错
x: number
y: number
}
type PointType1 = { //报错
z?: number
}
interface PointType2 {
x: number
y: number
}
interface PointType2 {
z: number
}
const point: PointType2 = {
x: 100,
y: 200,
z: 300
}
- interface支持继承的
- interface可以被类实现
类型断言as
-
允许变量从一种类型更改为另一种类型。
-
例:类不确定绑定在哪,不确定是否有src 和alt 属性,会报错
-
使用类型断言as
const imgEl = document.querySelector(".img") as HTMLImageElement
imgEl.src = "xxx"
imgEl.alt = "yyy"
- TypeScript只允许类型断言转换为 更具体 或者 不太具体 的类型版本,此规则可防止不可能的强制转换
const age3 = age as any
//当你很确定就是字符串类型时
const age4 = age3 as string
console.log(age4.split(" "))
非空类型断言
- 表示可以确定某个标识符是有值的,跳过ts在编译阶段对它的检测
- 符号
!
interface IPerson {
name: string
age: number
friend?: {
name: string
}
}
const info: IPerson = {
name: "why",
age: 18
}
// 访问属性: 可选链: ?.
console.log(info.friend?.name) //undefined
info.friend?.name = "kobe" //报错
// 解决方案一: 类型缩小
if (info.friend) {
info.friend.name = "kobe"
}
// 解决方案二: 非空类型断言(有点危险, 只有确保friend一定有值的情况, 才能使用)
info.friend!.name = "james"