0.Ts基础
0.0 【如何初始化 tsconfig.json文件】
通过在命令行终端输入
tsc --init
可以快速初始化 一个名叫tsconfig.json
的Ts
配置文件0.1【如何监听 ts 文件的变化自动生成对应的 js 文件】
通过在命令行终端输入
tsc --watch
可以监听当前.ts
的改变,自动生成向对应的.js
文件0.2【ts 中的基本类型】
string
、number
、boolean
、Array
、any
0.4【ts 中的类型注释】
let myAge: number = 20 // :number 便是变量 myAge 的类型注释
let myName = 'Youwei' // 也可不用显示的指定其变量的类型注释,TypeSCript 会根据值类型自动推断
0.5【ts 中函数的类型注释】
``typescript function getName(name: string): string{ console.log(
my name is ${name}`) return name }
// 在上面函数中,括号内参数可以也可以书写类型注释,括号后的 :string 表示该函数返回值的类型注释
<a name="szr7s"></a>
#### 0.6【上下文类型】
```typescript
const food = ['肉夹馍','凉皮','冰峰']
food.forEach((item)=>{
item.toUpperCase()
})
// 观察上面函数,可以发现,函数的参数 item 并没有指定类型注释,但依然可以使用字符串才有的方法
// 这是因为 TypeSCript 会自动根据 food 参数的类型与 forEach 函数的上下文类型中,自动推断出参数 item 的类型
0.7【对象参数类型】
function printCoord(PT: {X: number, Y: number}) {
console.log(`坐标X的值为:${PT.X}`);
console.log(`坐标Y的值为:${PT.Y}`);
}
printCoord({X:300, Y:400})
//可以将函数的参数的类型注释指定为一个对象传入
0.8【可选参数】
function getPersonName(person: { name: string, age?: number }) {
console.log(`姓名为:${person.name}`);
console.log(`年龄为:${person.age}`);
}
getPersonName({name: '柳白猿'})
// 上面函数的参数 age?: number 表示该参数是一个可传可不传的参数
// 当我们调用 getPersonName 函数时不传入 age 参数也不会触发 TypeScript 的类型检查
0.9【联合类型】
联合类型可以让我们变量或参数类型变得宽泛,但如果需要使用该变量或者参数时,就一定要让其满足多种类型的具备的方法或属性
// id:number | string 表示联合类型,即此时 id 的类型可以为 number类型 或者 string类型
let id: number | string = '001x'
id = '100' // 正确
id = 100 // 正确
id = {uid: 100} // 错误
// --------------------------------------------------------------------------------//
function getName(name: string[] | string){
// 当我们给 name 参数直接使用 数组的方法时,会报错。因为 name 不一定是数组类型,有可能只是一个 string 类型
// name.join() 直接使用会报错
// 此时我们可以使用 if 语句进行判断,来达到目的
if(Array.isArray(name)){
return name.join('浩克')
}else{
return name.toUpperCase()
}
}
getName(['mark42', '奇异博士', 'Tony'])
getName('绯红女巫')
getName(20) // 报错
// 函数 getName 方法接收一个 name 参数,而 name 参数的类型可以是一个 string类型的数组,或者 string
0.10【类型别名】
使用 type
关键字来定义类型别名
// 定义变量 ponit 为对象类型
type point = {
X: number,
Y: number
}
function getPoint(point: point){
console.log(`当前X位置为${point.X}`);
console.log(`当前Y位置为${point.Y}`);
}
getPoint({X: 500, Y: 100}) // 正确
getPoint(Y:100) // 报错
// 定义变量 ID 的类型为 number类型的数组或 string类型
type ID = number[] | string
function getId(id: ID) {
return id.slice(0,2)
}
getId([100,200,300]) // 正确
getId('100') // 正确
getId({id: '100'}) // 报错
// 定义变量 returnType 的类型为 string 或 number类型
type returnType = string | number
type value = number | string
function setSumVal(value): returnType {
return value
}
setSumVal(20) // 正确
setSumVal('1000') // 正确
setSumVal([100,200,'300']) // 报错
0.11【接口 interface】
使用 interface
关键字来定义接口
interface IPonit{
X: number,
Y: number
}
function getPoint(point: point){
console.log(`当前X位置为${point.X}`);
console.log(`当前Y位置为${point.Y}`);
}
getPoint({X: 500, Y: 100})
0.12【接口与类型别名的区别】
- 接口使用
interface
关键字定义,类型别名使用type
关键字定义,两者都是定义类型注释 - 类型别名
type
使用&
来扩展新的属性,interface
接口则通过extends
继承老接口属性,来扩展新属性 同一个接口可以支持重复定义,并添加新的属性,但是同一个类型别名不支持重复定义
0.12.1【接口的扩展】
接口的扩展可以使用
extends
关键字,让新接口在不修改老接口的情况下,添加新的属性 ```typescript interface IPerson{ name: string, age: number }
// 此时的 ISkill 接口中除了有 skill 属性还有,继承自 IPerson 接口的 name 属性与 age 属性 interface ISkill extends IPerson{ skill: string }
// 此时的变量 Tony 的类型必须同时满足 IPerson 与 ISkill 两个接口的定义 const Tony: ISkill = { name: ‘Tony’, age: 30, skill: ‘钞能力’ } // 在上面的栗子中可以看到, ISkill 接口通过 extends 继承了 IPerson 接口,并且定义了自己的新属性 // 在使用 ISkill 作为变量 Tony 的类型注释时就必须要满足 IPerson 与 ISkill 两种接口的定义
<a name="ze8XM"></a>
##### 0.12.2【类型别名的扩展】
- 类型别名通过 `&` 来基于旧的类型别名,扩展新的属性
```typescript
type IAnimal = {
name: string
age: number
}
// 此时的 IPanda 中不仅有 hobby,还有扩展自 IAnimal 的 name 与 age 属性
type IPanda = IAnimal & {
hobby: string
}
// 变量 pandaInfo 必须同时满足 类型别名 IAnimal 与 IPanda 中定义的属性
const pandaInfo: IPanda = {
name: '大宝',
age: 3,
hobby: '睡觉'
}
// 上面的栗子,IPanda 通过 & 基于 IAnimal 扩展了自己新的属性 hobby
// 在使用时,变量 pandaInfo 使用了类型注解 IPanda,就需要同时满足 IAnimal 与 IPanda 的定义
0.12.3【重复定义】
interface IPerson{
name: string
}
interface IPerson{
age: number
}
interface IPerson{
hobby: string
}
const person: IPerson = {
name: 'Tony',
age: 30,
hobby: 'honey'
}
// 以上的接口 IPerson 被重复定义多次,并不会报错
// 并且在使用 IPerson 作为类型注解时,需要满足接口的所有定义
// -----------------------------------------------------------------------------//
type IAnimal = {
name: string
}
type IAnimal = {
age: string
}
type IAnimal = {
skill: string
}
类型别名不支持重复定义,会报错:标识符“IAnimal”重复
0.13【断言】
- 使用
as
可以更具体的指定某一属性或变量的类型,类型断言由编译器删除,不会影响代码运行时的行为,所以也没有与as
相关的运行时检查 ```typescript const canvas1 = document.getElementById(‘myCanvas’) as HTMLCanvasElement
const canvas2 =
// 通过上面的栗子,获取了id 名为 myCanvas 的元素,但 TypeScript 只会返回某种类型的 HTMLElement // 但是并不知道具体是哪一种类型的 HTMLElement 元素,这时候可以使用 as 语法,指定一个明确的类型
<a name="r4H8S"></a>
#### 0.14【文字类型】
- 可以使用 文本 或者 数字 作为变量或参数类型
<a name="H70YG"></a>
##### 0.14.1【字符串文字类型】
```typescript
let personName: 'Tony' = 'Tony'
personName = 'zs' // 报错:不能将类型“"zs"”分配给类型“"Tony"”
// 上面的栗子中,将字符串 'Tony' 设置为变量 personName 的类型注解,当赋值不是 字符串的 'Tony' 是便会报错
//----------------------------------------------------------------------------------------//
// 一般用配合联合类型使用,文字联合类型,达到一个更具体的概念
function getLocation(name: string, location: 'left' | 'top' | 'right'){
console.log(`名称是${name}, 位置在${location}`);
}
getLocation('Tony', 'right') // 正确
getLocation('Hulk', 'top') // 正确
getLocation('zs', '左上角') // 报错:类型“"bottom"”的参数不能赋给类型“"left" | "top" | "right"”的参数
// getLocation 函数的第二个参数 location 此时只允许传入 'left' | 'top' | 'right',这几种字符串
// 相当于限制死了 location 参数的值
0.14.2【数值文字类型】
// 数值文字类型与字符串文字类型类似
function compare(a: string, b: string): 1 | 0 | -1{
// return a === b ? 0 : a > b ? 1 : 2 报错:不能将类型“2”分配给类型“0 | 1 | -1”
return a === b ? 0 : a > b ? 1 : -1
}
// 在上面的栗子中,已经规定了函数的返回值必须是 1 | 0 | -1
// 如果函数 compare 的返回值不是 1 | 0 | -1 其中的一种,便会报错
0.14.3【文字类型与其他类型的结合】
interface IOption {
width: number
}
function setValue(value: IOption | 'auto'){
return value
}
setValue({width: 200})
setValue('auto')
setValue(30) // 报错:类型“30”的参数不能赋给类型“IOption | "auto"”的参数
0.14.4【布尔文字类型】
let flag1: true = true
let flag2: false = false
flag1 = false // 报错:不能将类型“false”分配给类型“true”
flag2 = true // 报错:不能将类型“true”分配给类型“false”
// 上面的栗子:相当于对 flag1 和 flag2 设置了一个固定的布尔值类型注释,重新赋值会报错
0.14.5【文字推理】
let obj = {
value: 0
}
obj.value = 0
obj.value = '0' // 报错:不能将类型“string”分配给类型“number”
// TypeScript 基于上下文,帮我们推断出了 value 的类型为 number,
// 当赋值数字类型的 0 时可以成功,赋值为字符串类型 '0' 即报错
// -----------------------------------------------------------------------------------//
const request = {
url: 'http:xxxxxx.com',
method: 'GET'
}
function getDataList(url: string, method: 'GET'| 'POST'){
return `url是:${url}, 请求方法是${method}`
}
getDataList(request.url, request.method) // 报错:类型“string”的参数不能赋给类型“"GET" | "POST"”的参数
// 上面函数栗子:参数 method 的类型只能是 'GET'| 'POST',其实传入的值是对的
// 但是 TypeScript 帮我们自动推断 request.method 的值为 string 类型,导致类型判断失败报错
// 解决方法:
// 1. getDataList(request.url, request.method as 'GET') // 正确,使用类型断言解决
// 2. const request = {
// url: 'http:xxxxxx.com',
// method: 'GET'
// } as const 给整个 request 对象,断言为 const 将类型固定
0.15【null和undefined类型】
let Y: undefined = undefined
let X: null = null
function getName(x? number | null){
console.log(x!.toFixed()) // 使用非空断言
}
// 在上述的栗子中,对变量 x 使用 !,表示非空断言,即 x 的值一定存在,将 null 与 undefined 排除在外
0.16【枚举类型】
- 枚举类型通过
enum
关键字来定义,后面跟一个枚举的名字与类型定义 - 默认情况下,枚举成员是基于 0 开始递增,当显式的设置了类型类型的值,则会根据值,返回相应的结果 ```typescript enum Food { noodles , Hamburger, rice, }
Food.noodles // 0 Food.Hamburger // 1 Food.rice // 2 // ————————————————————————————————— enum Fruits { apple = 1, // 显式的设置了枚举类型的值 banana, strawberry, grape, }
Fruits.apple // 1 Fruits.banana // 2 Fruits.strawberry // 3 ….
<a name="Zns8q"></a>
#### 0.17【类型缩小】
- `TypeScript` 类型收窄就是从宽类型转换成窄类型的过程,类型收窄常用于处理联合类型变量的场景。
<a name="USweb"></a>
##### 0.17.1【typeof 类型守卫】
- `typeof`可以获取 `Ts` 代码在执行时,有关值最基本的类型信息
```typescript
function printAll(strs: string | string[] | null) {
if (Array.isArray(strs) ) {
// strs 为数组时 TODO
for (const value of strs){}
} else if (typeof strs === 'string') {
// strs 为字符串时 TODO
} else {
// 为 null 时 TODO
}
}
// 在上面栗子中,首先通过 Array.isArray() 方法判断 strs 是否为一个数组 strs 为数组时命中
// 接下来通过 typeof 判断 strs 的值是否为字符串,当 strs 为字符串命中
// 若以上都未命中,则 strs 的值为 null
0.17.2【真值缩小】
通过 条件、&&、||、if语句、布尔否定( ! ) 组成一个布尔表达式
function multiplyAll(values: number[] | undefined, factor: number){
if(!values){
return values
}else{
return values.map(() => {
return x * factor
})
}
}
multiplyAll([8.9],2)
// 通过 ! 取反 values 变量,组成一个布尔表达式,若逻辑命中做一些事情,未命中进行其他操作
0.17.3【等值缩小】
===
、!==
、==
、!=
来处理等值检查,实现类型缩小function example(x: string | number, y: string | boolean){
if( x === y){
x.toUpperCase()
y.toUpperCase()
}else{
// TODO...
}
}
// 上面的栗子中,由于函数参数 x 和 y 共同存有类型注释 string 所以 if 会成立
0.17.4【in操作符缩小 】
使用
in
运算符,用来确定某一对象是否有某个名称的属性,以此缩小潜在类型的范围
type Fish = { swim: () => void }
type Bird = { fly: () => void }
function move(animal: Fish | Bird) {
if ('swim' in animal) {
return animal.swim()
}
return animal.fly()
}
// 在上面的栗子中:animal 的类型注释是 Fish 与 Bird 的联合类型
// 因此在 if 语句的判断中 'swim' 是存在于 animal 中的
0.17.5【分配缩小】
let x = Math.random() < 0.5 ? 10 : 'hello world'
// 现在变量 x 现在相当于拥有 number | string 的联合类型,let x :number | string
let x = 1000 // 正确 满足 let x :number 类型
let x = 'Yooooo' // 正确 满足 let x :string 类型
let x = [] // 报错
let x = {} // 报错
0.18【never类型】
never
类型表示不应该存在的状态never
类型可以分配给每个类型,但是除never
本身以外,没有任何类型可以分配给never
类型let x: never
x = 'hellow', // 报错:不能将类型“string”分配给类型“never”
x = 20 // 报错:不能将类型“number”分配给类型“never”
0.19【函数】
0.19.1【函数类型表达式】
fn: (a:string) => void
// 上面的栗子就是一段函数表达式类型
// fn 函数后面有点类似箭头函数的样子的便是 fn 函数的类型
0.19.2【函数重载】
函数重载或方法重载,是使用相同名称和不同参数、数量或类型创建多个方法的一种能力
// 为 add 函数提供了多个函数类型定义,从⽽实现函数的重载
type Combinable = string | number
function add(a: number, b: number): number;
function add(a: string, b: string): string;
function add(a: string, b: number): string;
function add(a: Combinable, b: Combinable) {
// type Combinable = string | number;
if (typeof a === 'string' || typeof b === 'string') {
return a.toString() + b.toString();
}
return a + b;
}