专业摄影网站,移动论坛网站模板,制作网站需要多少费用,企业网站设计价格我们经常说TypeScript是JavaScript的一个超级 TypeScript 常用类型 
TypeScript 是 JS 的超集#xff0c;TS 提供了 JS 的所有功能#xff0c;并且额外的增加了#xff1a;类型系统 所有的 JS 代码都是 TS 代码 JS 有类型#xff08;比如#xff0c;number/string 等…我们经常说TypeScript是JavaScript的一个超级 TypeScript 常用类型 
TypeScript 是 JS 的超集TS 提供了 JS 的所有功能并且额外的增加了类型系统 所有的 JS 代码都是 TS 代码 JS 有类型比如number/string 等但是 JS 不会检查变量的类型是否发生变化而 TS 会检查 
TypeScript 类型系统的主要优势可以显示标记出代码中的意外行为从而降低了发生错误的可能性 
类型注解 
常用基础类型 类型注解 
语法 
声明了类型后TypeScript就会进行类型检测声明的类型可以称之为类型注解 var/let/const 标识符: 数据类型  赋值; 示例代码: 
let age: number  18 说明代码中的 : number 就是类型注解 
作用为变量添加类型约束。比如上述代码中约定变量 age 的类型为 number 类型 
解释约定了什么类型就只能给变量赋值该类型的值否则就会报错 
错误演示 
// 错误代码
// 错误原因将 string 类型的值赋值给了 number 类型的变量类型不一致
let age: number  18 常用基础类型 
可以将 TS 中的常用基础类型细分为两类1 JS 已有类型 2 TS 新增类型 
JS 已有类型 原始类型number/string/boolean/null/undefined/symbol 对象类型object包括数组、对象、函数等对象 
TS 新增类型 联合类型、自定义类型(类型别名)、接口、元组、字面量类型、枚举、void、any 等 
注意 原始类型在 TS 和 JS 中写法一致对象类型在 TS 中更加细化每个具体的对象比如数组、对象、函数都有自己的类型语法 原始/基本类型 
原始类型number/string/boolean/null/undefined/symbol 
特点简单这些类型完全按照 JS 中类型的名称来书写 
//数字
let age: number  18// 字符串
let myName: string  老师
const name: string  zs
const age: number  20
const info  my name is ${name}, age is ${age}
console.log(info); // my name is zs, age is 20//布尔
let isLoading: boolean  false
let flag: boolean  true
flag  false
flag  20  30 // flase//在 JavaScript 中undefined 和 null 是两个基本数据类型。
// 在TypeScript中它们各自的类型也是undefined和null也就意味着它们既是实际的值也是自己的类型
// null
let n: null  null// undefined
let u: undefined  undefined// symbol
const title1  Symbol(title)
const title2  Symbol(title)
let info  {[title1]: 123,[title2]: 456
} 注意 
TypeScript也是支持二进制、八进制、十六进制的表示 
let num1: number  100 // 十进制默认
let num2: number  0b111 // 二进制
let num3: number  0o456 // 八进制
let num4: number  0xf23 // 十六进制 数组类型 
数组类型的两种写法 推荐使用 number[] 写法 
// 写法一原始写法
let numbers: number[]  [1, 3, 5]// 写法二泛型写法
let strings: Arraystring  [a, b, c]//写法三 通过any方式定义数组中存放任意的值
let arr: any[]  [1,2,true]// 数组和对象结合要满足数组里面是对象的格式
let json: {username: string, age: number}[][{username: zs, age: 14}] 
注意事项在真实的开发中数组一般存放相同的类型不要存放不同的类型 元组类型 
元组类型tuple属于数组类型中一种元组类型规定了数组的长度、数组中数据类型的顺序 
一旦规定了元组类型那么数组里面的数据类型必须按照规定的顺序排列并且长度也必须按照元组中的长度取存放 
场景在地图中使用经纬度坐标来标记位置信息 
可以使用数组来记录坐标那么该数组中只有两个元素并且这两个元素都是数值类型 number[] 
let position: number[]  [116.2317, 39.5427] 
使用 number[] 的缺点不严谨因为该类型的数组中可以出现任意多个数字 
更好的方式元组 Tuple 
元组类型是另一种类型的数组它确切地知道包含多少个元素以及特定索引对应的类型 
let position: [number, number]  [39.5427, 116.2317] 
解释 元组类型可以确切地标记出有多少个元素以及每个元素的类型该示例中元素有两个元素每个元素的类型都是 number 那么tuple元祖和数组区别 
数组中通常建议存放相同类型的元素不同类型的元素是不推荐放在数组中 
元组中每个元素都有自己特性的类型根据索引值获取到的值可以确定对应的类型 应用场景 
tuple元祖通常可以作为返回的值在使用的时候会非常的方便 
function useStateT(state: T) :[T, (newState: T)  void] {let currentState  stateconst changeState  (newState: T)  {currentState  newState}return [currentState, changeState]
}
const [counter, setCounter]  useState(10) 枚举类型 
枚举的功能类似于字面量类型联合类型组合的功能也可以表示一组明确的可选值 
枚举定义一组命名常量。它描述一个值该值可以是这些命名常量中的一个 
enum类型是对JavaScript的标准数据类型的补充比如支付状态0失败1成功2超时 
// 创建枚举
enum Direction { Up, Down, Left, Right }// 使用枚举类型
function changeDirection(direction: Direction) {console.log(direction)
}// 调用函数时需要应该传入枚举 Direction 成员的任意一个
// 类似于 JS 中的对象直接通过 点.语法 访问枚举的成员
changeDirection(Direction.Up) 
解释 使用 enum 关键字定义枚举约定枚举名称以大写字母开头枚举中的多个值之间通过 ,逗号分隔定义好枚举后直接使用枚举名称作为类型注解 数字枚举 
问题我们把枚举成员作为了函数的实参它的值是什么呢? 
解释通过将鼠标移入 Direction.Up可以看到枚举成员 Up 的值为 0 
注意枚举成员是有值的默认为从 0 开始自增的数值 
我们把枚举成员的值为数字的枚举称为数字枚举 
当然也可以给枚举中的成员初始化值 
// Down - 11、Left - 12、Right - 13
enum Direction { Up  10, Down, Left, Right }enum Direction { Up  2, Down  4, Left  8, Right  16 } 字符串枚举 
字符串枚举枚举成员的值是字符串 
注意字符串枚举没有自增长行为因此字符串枚举的每个成员必须有初始值 
enum Direction {Up  UP,Down  DOWN,Left  LEFT,Right  RIGHT
} 使用枚举解决固定的数据 
enum fullyear {spring  0,summer  1,autom  2,winter  3
}console.log(fullyear.spring);
console.log(fullyear[0]); 
一旦用枚举定义了数据那么这个数据是不能被修改的也不能增加额外的属性。 
enum fullyear2 {spring  0,summer  1,autom  2,winter  3
}console.log(fullyear2.autom);
console.log(fullyear2[2]); // 报错不存在2 
如果enum中的值是字符串那么只能通过左侧的属性进行访问不能通过右侧的值去访问。 什么数据需要用到枚举 
固定的数据可以用枚举去写。一年四季一年12个月性别一周七天 枚举实现原理 
枚举是 TS 为数不多的非 JavaScript 类型级扩展(不仅仅是类型)的特性之一 
因为其他类型仅仅被当做类型而枚举不仅用作类型还提供值(枚举成员都是有值的) 
也就是说其他的类型会在编译为 JS 代码时自动移除。但是枚举类型会被编译为 JS 代码 
enum Direction {Up  UP,Down  DOWN,Left  LEFT,Right  RIGHT
}// 会被编译为以下 JS 代码
var Direction;(function (Direction) {Direction[Up]  UPDirection[Down]  DOWNDirection[Left]  LEFTDirection[Right]  RIGHT
})(Direction || Direction  {}) 
说明枚举与前面讲到的字面量类型联合类型组合的功能类似都用来表示一组明确的可选值列表 
一般情况下推荐使用字面量类型联合类型组合的方式因为相比枚举这种方式更加直观、简洁、高效 any 类型 
在某些情况下我们确实无法确定一个变量的类型并且可能它会发生一些变化这个时候我们可以使用any类型 any类型有点像一种讨巧的TypeScript手段 
我们可以对any类型的变量进行任何的操作包括获取不存在的属性、方法 
我们给一个any类型的变量赋值任何的值比如数字、字符串的值 let a: any  123
a  14
a  true
a  null
a  undefined
const arr: any[]  [1, 2, 1.8, true] 如果对于某些情况的处理过于繁琐不希望添加规定的类型注解或者在引入一些第三方库时缺失了类型注解这个时候我们可以使用any 
包括在Vue源码中也会使用到any来进行某些类型的适配 
unknown是TypeScript中比较特殊的一种类型它用于描述类型不确定的变量 
unknown类型只能赋值给any和unknown 
any类型可以赋值给任意类型 原则:不推荐使用 any! 这会让 TypeScript 变为 “AnyScript”(失去 TS 类型保护的优势) 
因为当值的类型为 any 时可以对该值进行任意操作并且不会有代码提示 
let obj: any  { x: 0 }obj.bar  100
obj()
const n: number  obj 
解释:以上操作都不会有任何类型错误提示即使可能存在错误 
尽可能的避免使用 any 类型除非临时使用 any 来“避免”书写很长、很复杂的类型 
其他隐式具有 any 类型的情况 声明变量不提供类型也不提供默认值函数参数不加类型 
注意因为不推荐使用 any所以这两种情况下都应该提供类型 
在项目开发中尽量少用any类型 unknown 类型 
未知类型 
要想使用该类型进行相关操作时必须要进行类型校验 示例 1 
let foo: unknown  aaa
foo  123
// 类型校验--类型缩小
if(typeof foo  string){....   
} 示例2 
// unknown 类型
let a: unknown  hello;
a  123
console.log(a); // 123
// any 是不进行检测了但是unknown使用的时候TS默认会进行检测
// 使用类型断言告诉a就是一个数组不需要进行检测了
(a as []).map((){}) unknown 类型和  any 类型的区别在于 
unknown 做任何事情都是不合法的必须要通过类型校验才可以进行其他操作any 做任何事情都是合法的无需校验 联合类型 
TypeScript的类型系统允许我们使用多种运算符从现有类型中构建新类型 
联合类型是由两个或者多个其他类型组成的类型类型之间进行或的操作 
表示可以是这些类型中的任何一个值 
联合类型中的每一个类型被称之为联合成员 需求数组中既有 number 类型又有 string 类型这个数组的类型应该如何写? let arr: (number | string)[]  [1, a, 3, b] 
解释|竖线在 TS 中叫做联合类型即由两个或多个其他类型组成的类型表示可以是这些类型中的任意一种 
注意这是 TS 中联合类型的语法只有一根竖线不要与 JS 中的或|| 或混淆了 注意 
使用联合类型的时候一定要非常的小心 例如传入给一个联合类型的值是非常简单的 
只要保证是联合类型中的某一个类型的值即可但是我们拿到这个值之后我们应该如何使用它呢因为它可能是任何一种类型。比如我们拿到的值可能是string或者number我们就不能对其调用string上的一些方法 那么我们怎么处理这样的问题呢 
我们需要使用缩小narrow联合 
TypeScript可以根据我们缩小的代码结构推断出更加具体的类型 
function printId(id: number | string) {if(typeof id  string){// 确定为string类型console.log(id为, id.toUpperCase());} else {// 确定为number类型console.log(id);}
} 交叉类型 
类型之间进行与的操作 
交叉类似表示需要满足多个类型的条件 
交叉类型使用  符号 
type MyType  number  string 
表达的含义是number和string要同时满足 
但是有同时满足是一个number又是一个string的值吗其实是没有的所以MyType其实是一个never类型 交叉类型的应用 
在开发中我们进行交叉时通常是对对象类型进行交叉的 
interface Colorful {color: string
}interface IRun {running: ()  void
}type NewType  Colorful  IRunconst obj: NewType  {color: red,running: function() {}
} 类型别名 
类型别名自定义类型为任意类型起别名 
使用场景当同一类型复杂被多次使用时可以通过类型别名简化该类型的使用 
type CustomArray  (number | string)[]let arr1: CustomArray  [1, a, 3, b]
let arr2: CustomArray  [x, y, 6, 7] 
解释: 使用 type 关键字来创建自定义类型类型别名比如此处的 CustomArray可以是任意合法的变量名称推荐使用大写字母开头创建类型别名后直接使用该类型别名作为变量的类型注解即可 undefined和null 
// 0女士   1男士  2其他
let gender: number | undefined | null;gender  1;
gender  undefined;
gender  null; 如果你需要将数据清空这个时候直接赋值undefined或者是null会数据类型不匹配的错误你可以给变量定义多个数据类型。 undefined和null区别 
undefined指未定义 
null值为空 
null  undefined // true
null  undefined // falsetypeof null  object;
typeof undefined  undefined; never类型 
了解 
never类型值永远不存在值的类型。比如一个函数 
如果一个函数中是一个死循环或者抛出一个异常那么这个函数会返回东西吗 
不会那么写void类型或者其他类型作为返回值类型都不合适我们就可以使用never类型 例如never类型指哪些总是抛出错误或异常的函数的返回值类型或者值的类型。 
let fn: never;fn  (()  {throw new Error(错误);
})(); 应用场景 
开发中很少实际去定义never类型某些情况下会自动进行类型推导出never 
开发框架工具的时候可能会用到never类型 
封装一些类型工具的时候可以使用never类型 函数类型 
函数的类型实际上指的是函数参数和返回值的类型 
TS中要求实参的个数必须跟形参的个数相同 
为函数指定类型的两种方式 单独指定参数、返回值的类型同时指定参数、返回值的类型 单独指定参数、返回值的类型 
// 函数声明
function add(num1: number, num2: number): number {return num1  num2
}// 复杂类型约束
function show2(user: {name: string, age: number}) {
}
show2({name: 张三, age: 20});function show3(students: {id: number, name: string}[]) {
}
show3([{id: 1, name: lisi}, {id: 2, name: wangwu}]);// 箭头函数
const add  (num1: number, num2: number): number  {return num1  num2
} 同时指定参数、返回值的类型: 
type AddFn  (num1: number, num2: number)  numberconst add: AddFn  (num1, num2)  {return num1  num2
} 
解释当函数作为表达式时可以通过类似箭头函数形式的语法来为函数添加类型 
注意这种形式只适用于函数表达式 void 类型 
如果函数没有返回值那么函数返回值类型为void 
function greet(name: string): void {console.log(Hello, name)
} 
注意 我们可以将null和undefined赋值给void类型也就是函数可以返回null或者undefined 如果一个函数没有返回值此时在 TS 的类型中应该使用 void 类型 // 如果什么都不写此时add 函数的返回值类型为 void
const add  ()  {}// 这种写法是明确指定函数返回值类型为 void与上面不指定返回值类型相同
const add  (): void  {}const add  (): void  {return;
}let add  function(): void {return undefined;
}// 如果指定 返回值类型为 undefined此时函数体中必须显示的 return undefined 才可以
const add  (): undefined  {// 此处返回的 undefined 是 JS 中的一个值return undefined
}// 如果指定 返回值类型为 null此时函数体中必须显示的 return null 才可以
let add  function(): null {return null
}// 1.
type ExecFnType  (...args: any[])  void
// 2.
function  delayExechFn(fn: ExecFnType) {setTimeout(()  {fn(zs, 20)}, 1000)
}
// 3.
delayExechFn((name, age)   {...
}) 函数可选参数 
使用函数实现某个功能时参数可以传也可以不传。这种情况下在给函数参数指定类型时就用到可选参数了 
比如数组的 slice 方法可以 slice() 也可以 slice(1) 还可以 slice(1, 3) 
function mySlice(start?: number, end?: number): void {console.log(起始索引, start, 结束索引, end)
} 
可选参数在可传可不传的参数名称后面添加 ?问号 
注意可选参数只能出现在参数列表的最后也就是说可选参数后面不能再出现必选参数 对象可选参数 
也是通过?:标记为可选参数 
function show5(user: {name: string, age?: number}) {
}
show5({age: 20, name: 张三}); 跳过可选参数 
使用undefined跳过可选参数 
function show4(name: string, gender: number, age?: number, address?: string) {
}
show4(张三, 0, undefined,红旗河沟); 默认值 
函数参数默认值和es6中写法一样 
function show6(name: string  张三) {
}
show6(); 
注意一旦给了默认值就不能显示的给可选?: 剩余参数 
和es6中剩余参数基本类似但是应该生命剩余参数的数据类型。 
function show7(name: string, ...args: any[]) {}show7(张三, 1, a, true); 传入对象参数 
type Point  {x: number, y: number,z?: number
}function printPoint(point: Point) {console.log(point.x);console.log(point.y);
}printPoint({x: 524.25, y: 78.456, z: 324.586}) 函数作为参数 
function foo() {}type FooFnType  ()  voidfunction bar(fn: FooFnType){fn()
}bar(foo) 示例 
function calc(n1: number, n2: number, fn: (num1: number, num2: number)  number){console.log(fn(n1, n2));return fn(n1, n2)
}calc(20, 30, function(a1, a2) {return a1  a2
})calc(20, 30, function(a1, a2) {return a1 - a2
})calc(20, 30, function(a1, a2) {return a1 * a2
})calc(20, 30, function(a1, a2) {return a1 / a2
})export {} 函数重载 
函数重载或方法重载有以下几个优势 优势1 结构分明 
让代码可读性可维护性提升许多而且代码更漂亮。 优势2 各司其职自动提示方法和属性每个重载签名函数完成各自功能输出取值时不用强制转换就能出现自动提示从而提高开发效率】 优势3 更利于功能扩展 
在TypeScript中如果我们编写了一个add函数希望可以对字符串和数字类型进行相加应该如何编写呢 我们可能会这样来编写但是其实是错误的 那么这个代码应该如何去编写呢 
在TypeScript中我们可以去编写不同的重载签名overload signatures来表示函数可以以不同的方式进行调用 
一般是编写两个或者以上的重载签名再去编写一个通用的函数以及实现 示例 
type MessageType  image | audio | string;//微信消息类型type Message  {id: number;type: MessageType;sendmessage: string;
};let messages: Message[]  [{id: 1, type: image, sendmessage: 你好啊,今晚咱们一起去三里屯吧,},{id: 2, type: audio, sendmessage: 朝辞白帝彩云间千里江陵一日还},{id: 3, type: audio, sendmessage: 你好张无忌},{id: 4, type: image, sendmessage: 刘老根苦练舞台绝技},{id: 5, type: image, sendmessage: 今晚王牌对王牌节目咋样?}]//用方法重载 
//第一个根据数字id来查询单个消息的重载签名
function getMessage(value: number): Message//第二个根据消息类型来查询消息数组的重载签名
// readRecordCount控制显示的条数
function getMessage(value: MessageType, readRecordCount: number): Message[]function getMessage(value: any, value2: any  1) {if (typeof value  number) {return messages.find((msg)  { return 6  msg.id })//undefined} else {return messages.filter((msg)  value  msg.type).splice(0, value2)}
}getMessage(image, 2).forEach((msg)  {console.log(msg);
})export { } 示例 
// 函数重载函数名相同、参数不同的几个函数
function add(num1: number, num2:number): numberfunction add(num1: string, num2: string): stringfunction add(num1: any, num2: any): any {return num1  num2
}const result  add(20, 30)
console.log(result);const result1  add(20, 30)
console.log(result1); 联合类型和重载 
我们现在有一个需求定义一个函数可以传入字符串或者数组获取它们的长度 这里有两种实现方案 
方案一使用联合类型来实现 
方案二实现函数重载来实现 
// 联合类型
// function getLength(a: string | any[]) {
//   return a.length
// }// 重载
function getLength(a: string): number
function getLength(a: any[]): number
function getLength(a: any) {return a.length
} 
在开发中我们选择使用哪一种呢 
在可能的情况下尽量选择使用联合类型来实现 可调用注解 
可以针对函数重载进行类型注解 
// type A  ()  void // 等价于如下// 类型注解
type A  {(n: number, m: number): any(n: string, m: string): any
}function foo(n: number, n1: number): any;
function foo(n: string, n1: string): any;
function foo(n: number|string, m: number|string){}let a: A  foo type A  {(n: number): numberusername?: string
}let foo: A  (n)  {return n}
foo.username  zs 匿名函数 
匿名函数是否需要添加类型注解最好不要添加注解因为它自身会进行上下文推导 
const names: string[]   [a, b]names.forEach(function(item, index, arr){...
}) 对象类型 
在ts中object类型是主要是[]、{}、function三种类型的复核类型。 
let obj: object  {};let obj2: object  [];let obj3: object  function() {}; 
object是复核类型不要随意用因为你不知道这个数据应该传递数组还是对象还是函数一旦传递错误会报语法错误。 JS 中的对象是由属性和方法构成的而 TS 对象的类型就是在描述对象的结构有什么类型的属性和方法 
对象类型的写法: 
// 空对象
let person: {}  {}// 有属性的对象
let person: { name: string }  {name: 同学
}// 既有属性又有方法的对象
// 在一行代码中指定对象的多个属性类型时使用 ;分号来分隔
let person: { name: string; sayHi(): void }  {name: jack,sayHi() {}
}// 对象中如果有多个类型可以换行写
// 通过换行来分隔多个属性类型可以去掉 ;
let person: {name: stringsayHi(): void
}  {name: jack,sayHi() {}
}type Obj  {username: string}
let obj  {} as Obj 解释: 使用 {} 来描述对象结构属性采用属性名: 类型的形式方法采用方法名(): 返回值类型的形式 如果你需要在对象中任意给值可以使用[k: string]: any let obj4: { name: string, age: number, gender: number, [k: string]: any }  {name: 张三,age: 20,gender: 0,address: 红旗河沟
}; [k: string]: any冒号左侧[k: string]代表对象的键只能是字符串类型右侧的any代表值是任意类型 使用类型别名 
注意直接使用 {} 形式为对象添加类型会降低代码的可读性不好辨识类型和值 
推荐使用类型别名为对象添加类型 
// 创建类型别名
type Person  {name: string,age: number,sayHi(): void,// 索引签名[k: string]: any
}// 使用类型别名作为对象的类型
let person:Person  {name: zs,age: 20,sayHi() {},height: 160
} 带有参数的方法类型 
如果方法有参数就在方法名后面的小括号中指定参数类型 
type Person  {greet(name: string): void
}let person: Person  {greet(name) {console.log(name)}
} 箭头函数形式的方法类型 
方法的类型也可以使用箭头函数形式 
type Person  {greet: (name: string)  void
}let person: Person  {greet(name) {console.log(name)}
} 对象可选属性 
对象的属性或方法也可以是可选的此时就用到可选属性了 
比如我们在使用 axios({ ... }) 时如果发送 GET 请求method 属性就可以省略 
可选属性的语法与函数可选参数的语法一致都使用 ? 来表示 
type Config  {url: stringmethod?: string
}function myAxios(config: Config) {console.log(config)
} 类型推论/导 
在 TS 中某些没有明确指出类型的地方TS 的类型推论机制会帮助提供类型 
换句话说由于类型推论的存在这些地方类型注解可以省略不写 
发生类型推论的 2 种常见场景: 声明变量并初始化时决定函数返回值时 
// 变量 age 的类型被自动推断为number
let age  18// 函数返回值的类型被自动推断为number
function add(num1: number, num2: number) {return num1  num2
} 
推荐能省略类型注解的地方就省略偷懒充分利用TS类型推论的能力提升开发效率 
技巧如果不知道类型可以通过鼠标放在变量名称上利用 VSCode 的提示来查看类型 
推荐在 VSCode 中写代码的时候多看方法、属性的类型养成写代码看类型的习惯 console.log()
document.createElement() 字面量类型 
let  进行类型推导推导出来的通用类型 
const  进行类型推导推导出来的字面量类型可作为一个类型使用常用于联合类型中 
思考以下代码两个变量的类型分别是什么? 
let str1  Hello TS
const str2  Hello TS 
通过 TS 类型推论机制可以得到答案 变量 str1 的类型为string变量 str2 的类型为Hello TS 
解释: 
str1 是一个变量(let)它的值可以是任意字符串所以类型为:string 
str2 是一个常量(const)它的值不能变化只能是 Hello TS所以它的类型为:Hello TS 
注意此处的 Hello TS就是一个字面量类型也就是说某个特定的字符串也可以作为 TS 中的类型 
任意的 JS 字面量比如对象、数字等都可以作为类型使用 字面量{ name: jack } [] 18 20 abc false function() {} type A  liner | swing
let a: A  liner 使用模式和场景 
使用模式字面量类型配合联合类型一起使用 
使用场景用来表示一组明确的可选值列表 
比如在贪吃蛇游戏中游戏的方向的可选值只能是上、下、左、右中的任意一个 
// 使用自定义类型:
type Direction  up | down | left | rightfunction changeDirection(direction: Direction) {console.log(direction)
}// 调用函数时会有类型提示
changeDirection(up) 解释参数 direction 的值只能是 up/down/left/right 中的任意一个 
优势相比于 string 类型使用字面量类型更加精确、严谨 字面量推理 
// 方式1
// type Method  GET | POST
// type Request  {
//   url: string,
//   method: Method
// }
// const options: Request  {
//   url: https://www.baidu.com,
//   method: POST
// }
// function request (url: string, method: Method) {}
// request(options.url, options.method)// 方式2
// type Method  GET | POST
// const options  {
//   url: https://www.baidu.com,
//   method: POST
// }
// function request (url: string, method: Method) {}
// request(options.url, options.method as Method)// 方式3
type Method  GET | POST
const options  {url: https://www.baidu.com,method: POST
} as const
function request (url: string, method: Method) {}
request(options.url, options.method as Method)export {} keyof 关键字 
interface A {username: string,age: number
}// 此时想将 username 和 age 单独提取出来作为一个联合类型
// keyof A -- username | age
let a: keyof A  age
let b: keyof A  username let obj: {username: zs,age: 20
}
let a: keyof typeof obj  age 类型断言 基本用法 
有时候你会比 TS 更加明确一个值的类型此时可以使用类型断言来指定更具体的类型。 比如 
const aLink  document.getElementById(link) 
注意该方法返回值的类型是 HTMLElement该类型只包含所有标签公共的属性或方法不包含 a 标签特有的 href 等属性 
因此这个类型太宽泛(不具体)无法操作 href 等 a 标签特有的属性或方法 
解决方式这种情况下就需要使用类型断言指定更加具体的类型 
使用类型断言 const aLink  document.getElementById(link) as HTMLAnchorElementlet value: any  张三;
let arr  (value as string).split()class Person {}
class Student extends Person {studying(){}
}
function sayHello(p: Person) {(p as Student).studying()
}
const stu  new Student()
console.log(stu); // Student {} 
解释 使用 as 关键字实现类型断言关键字 as 后面的类型是一个更加具体的类型HTMLAnchorElement 是 HTMLElement 的子类型通过类型断言aLink 的类型变得更加具体这样就可以访问 a 标签特有的属性或方法了 
另一种语法使用  语法这种语法形式不常用知道即可: // 该语法知道即可在react的jsx中使用会报错
const aLink  HTMLAnchorElementdocument.getElementById(link)et value: any  张三;
let arr  (stringvalue).split() 两种形式是等价的。 至于使用哪个大多数情况下是凭个人喜好然而当你在TypeScript里使用JSX时只有 as语法断言是被允许的。 
注意在tsx文件中只能使用as语法断言。 技巧在浏览器控制台通过 __proto__ 获取 DOM 元素的类型* TypeScript只允许类型断言转换为 更具体 或者 不太具体 的类型版本此规则可防止不可能的强制转换 特殊 
了解 
这样做容易造成代码的混乱 
const message: string  123
// const num: number  (message as any) as number
const num: number  (message as unknown) as number 非空断言 
标识符! 
当我们编写下面的代码时在执行ts的编译阶段会报错 
这是因为传入的message有可能是为undefined的这个时候是不能执行方法的 但是我们确定传入的参数是有值的这个时候我们可以使用非空类型断言 
非空断言使用的是 ! 表示可以确定某个标识符是有值的跳过ts在编译阶段对它的检测 typeof 
众所周知JS 中提供了 typeof 操作符用来在 JS 中获取数据的类型 
console.log(typeof Hello world) // ? 
实际上TS 也提供了 typeof 操作符可以在类型上下文中引用变量或属性的类型类型查询 
使用场景:根据已有变量的值获取该值的类型来简化类型书写 
let p  { x: 1, y: 2 }
function formatPoint(point: { x: number; y: number }) {}
formatPoint(p)function formatPoint(point: typeof p) {} 
解释: 使用 typeof 操作符来获取变量 p 的类型结果与第一种对象字面量形式的类型相同typeof 出现在类型注解的位置参数名称的冒号后面所处的环境就在类型上下文(区别于 JS 代码)注意typeof 只能用来查询变量或属性的类型无法查询其他形式的类型比如函数调用的类型 可选类型 
其实上可选类型可以看做是 类型 和 undefined 的联合类型 
function print(message?: string) {console.log(message);
}print() // undefined
print(21)
print(undefined) // undefined// 报错Argument of type null is not assignable to parameter of type string | undefined
// print(null) 类型缩小/保护 
类型缩小的英文是 Type Narrowing 
我们可以通过类似于 typeof padding  number 的判断语句来改变TypeScript的执行路径 
在给定的执行路径中我们可以缩小比声明时更小的类型这个过程称之为 缩小 
而我们编写的 typeof padding  number 可以称之为 类型保护type guards 常见的类型保护有如下几种 
typeof 
平等缩小比如、! 
instanceof 
in 
字面量类型 
...... typeof 
TypeScript 中检查返回的值typeof是一种类型保护因为 TypeScript 对如何typeof操作不同的值进行编码 
type ID  number | stringfunction printId(id: ID) {if(typeof id  string){console.log(1);}else {console.log(2);}
} 平等缩小 
我们可以使用Switch或者相等的一些运算符来表达相等性比如, !, , and !  
type Direction  left | right | centerfunction turnDirection(direction: Direction) {switch(direction) {case left:console.log(left);break;case right:console.log(right);break;case center:console.log(center);break;default:console.log(default);}
} instanceof 
JavaScript 有一个运算符来检查一个值是否是另一个值的“实例” 
function printValue(date: Date | string) {if(date instanceof Date){console.log(date.toLocaleDateString());}else {console.log(date);}
} in 
Javascript 有一个运算符用于确定对象是否具有带名称的属性in运算符 
如果指定的属性在指定的对象或其原型链中则in 运算符返回true 
type Fish  {swim: ()  void}
type Dog  {run: ()  void}function move(animal: Fish | Dog) {if(swim in animal){animal.swim()} else {animal.run()}
} 字面量 
function foo(n: username | 123) {if(n  username){n.length}
} 自定义保护 
is 是类型谓词它可以做到类型保护 
function isString(n: any): n is string {return typeof n  string
}function foo(n: string | number) {if(isString(n)){}
} interface VS type 
interface和type都可以用来定义对象类型 
如果是定义非对象类型通常推荐使用type比如Direction、Alignment、一些Function 如果是定义对象类型那么他们是有区别的 
interface 可以重复的对某个接口来定义属性和方法 
而type定义的是别名别名是不能重复的