TS 常用类型

我们经常说TypeScript是JavaScript的一个超级

 

TypeScript 常用类型

  • TypeScript 是 JS 的超集,TS 提供了 JS 的所有功能,并且额外的增加了:类型系统
    • 所有的 JS 代码都是 TS 代码
    • JS 有类型(比如,number/string 等),但是 JS 不会检查变量的类型是否发生变化,而 TS 会检查
  • TypeScript 类型系统的主要优势:可以显示标记出代码中的意外行为,从而降低了发生错误的可能性
  1. 类型注解
  1. 常用基础类型

类型注解

语法:

声明了类型后TypeScript就会进行类型检测,声明的类型可以称之为类型注解

var/let/const 标识符: 数据类型 = 赋值;

示例代码:

let age: number = 18

  • 说明:代码中的 : number 就是类型注解
  • 作用:为变量添加类型约束。比如,上述代码中,约定变量 age 的类型为 number 类型
  • 解释:约定了什么类型,就只能给变量赋值该类型的值,否则,就会报错
  • 错误演示:
// 错误代码:
// 错误原因:将 string 类型的值赋值给了 number 类型的变量,类型不一致
let age: number = '18'

常用基础类型

可以将 TS 中的常用基础类型细分为两类:1 JS 已有类型 2 TS 新增类型

  1. JS 已有类型
    • 原始类型:number/string/boolean/null/undefined/symbol
    • 对象类型:object(包括,数组、对象、函数等对象)
  1. TS 新增类型
    • 联合类型、自定义类型(类型别名)、接口、元组、字面量类型、枚举、void、any 等
  • 注意:
    1. 原始类型在 TS 和 JS 中写法一致
    2. 对象类型在 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: Array<string> = ['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]
  • 解释:
    1. 元组类型可以确切地标记出有多少个元素,以及每个元素的类型
    2. 该示例中,元素有两个元素,每个元素的类型都是 number

那么tuple(元祖)和数组区别:

  • 数组中通常建议存放相同类型的元素,不同类型的元素是不推荐放在数组中
  • 元组中每个元素都有自己特性的类型,根据索引值获取到的值可以确定对应的类型

应用场景:

tuple(元祖)通常可以作为返回的值,在使用的时候会非常的方便;

function useState<T>(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)
  • 解释:
    1. 使用 enum 关键字定义枚举
    2. 约定枚举名称以大写字母开头
    3. 枚举中的多个值之间通过 ,(逗号)分隔
    4. 定义好枚举后,直接使用枚举名称作为类型注解

数字枚举
  • 问题:我们把枚举成员作为了函数的实参,它的值是什么呢?
  • 解释:通过将鼠标移入 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'] = 'UP'Direction['Down'] = 'DOWN'Direction['Left'] = 'LEFT'Direction['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 类型的情况
    1. 声明变量不提供类型也不提供默认值
    2. 函数参数不加类型
  • 注意:因为不推荐使用 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]
  • 解释:
    1. 使用 type 关键字来创建自定义类型
    2. 类型别名(比如,此处的 CustomArray)可以是任意合法的变量名称
    3. 推荐使用大写字母开头
    4. 创建类型别名后,直接使用该类型别名作为变量的类型注解即可

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中要求,实参的个数必须跟形参的个数相同
  • 为函数指定类型的两种方式:
    1. 单独指定参数、返回值的类型
    2. 同时指定参数、返回值的类型

  1. 单独指定参数、返回值的类型:
// 函数声明
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
}

  1. 同时指定参数、返回值的类型:
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

  • 解释:
    1. 使用 {} 来描述对象结构
    2. 属性采用属性名: 类型的形式
    3. 方法采用方法名(): 返回值类型的形式

如果你需要在对象中任意给值,可以使用[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 种常见场景:
    1. 声明变量并初始化时
    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 类型推论机制,可以得到答案:
    1. 变量 str1 的类型为:string
    2. 变量 str2 的类型为:'Hello TS'
  • 解释:
  1. str1 是一个变量(let),它的值可以是任意字符串,所以类型为:string
  1. 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' | 'right'function 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 {}
  • 解释:
    1. 使用 as 关键字实现类型断言
    2. 关键字 as 后面的类型是一个更加具体的类型(HTMLAnchorElement 是 HTMLElement 的子类型)
    3. 通过类型断言,aLink 的类型变得更加具体,这样就可以访问 a 标签特有的属性或方法了
  • 另一种语法,使用 <> 语法,这种语法形式不常用知道即可:

// 该语法,知道即可:在react的jsx中使用会报错
const aLink = <HTMLAnchorElement>document.getElementById('link')et value: any = '张三';
let arr = (<string>value).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) {}
  • 解释:
    1. 使用 typeof 操作符来获取变量 p 的类型,结果与第一种(对象字面量形式的类型)相同
    2. typeof 出现在类型注解的位置(参数名称的冒号后面)所处的环境就在类型上下文(区别于 JS 代码)
    3. 注意: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' | 'center'function 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定义的是别名,别名是不能重复的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/53854.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《JavaEE进阶》----14.<SpringMVC配置文件实践之【验证码项目】>

本篇博客介绍的是Google的开源项目Kaptcha来实现的验证码。 这种是最简单的验证码。 也是很常见的一种验证码。可以去看项目结果展示。就可以明白这个项目了。 前言&#xff1a; 随着安全性的要求越来越高、很多项目都使用了验证码。如今验证码的形式也是有许许多多、更复杂的图…

基于SpringBoot的古城墙景区管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSpringBootVueMySQL的古城墙景区管理系…

2024数学建模国赛官方评阅标准发布!

​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑…

C++类与对象(下)--最后的收尾

内部类 • 如果⼀个类定义在另⼀个类的内部&#xff0c;这个内部类就叫做内部类。内部类是⼀个独⽴的类&#xff0c;跟定义在 全局相⽐&#xff0c;他只是受外部类类域限制和访问限定符限制&#xff0c;所以外部类定义的对象中不包含内部类。 #include<iostream> using…

安装FTP服务器教程

一。安装vsftpd yum install vsftpd 二。修改配置文件&#xff0c;匿名账户具有访问&#xff0c;上传和创建目录的权限 vim /etc/vsftpd/vsftpd.conf &#xff08;红色进行设置放开YES&#xff09; local_enable&#xff1a;本地登陆控制&#xff0c;no表示禁止&#xff0c;ye…

3. 轴指令(omron 机器自动化控制器)——>MC_MoveAbsolute

机器自动化控制器——第三章 轴指令 4 MC_MoveAbsolute变量▶输入变量▶输入输出变量▶输入输出变量 功能说明▶指令详情▶时序图▶重启运动指令▶多重启动运动指令▶异常 示例程序1▶参数设定▶动作示例▶梯形图▶结构文本(ST) 示例程序2▶参数设定▶动作示例▶梯形图▶结构文…

RDMA应用场景及效果

GPU Direct 参考&#xff1a;网络架构如何支持超万卡的大规模 AI 训练&#xff1f;| AICon_芯片与网络_InfoQ精选文章 GPU 网络的情况已经发生了很大变化。每个 GPU 都有自己的内部互联&#xff0c;例如 NVIDIA 的 A100 或 H800&#xff0c;它们内部的 NVLink 互联可以达到 6…

单个 java 虚拟机 生产者消费者

一、通过 java.lang.Object#wait()&#xff0c;java.lang.Object#notify&#xff0c;java.lang.Object#notifyAll来实现 生产者&#xff0c;消费者 public abstract class Goods {protected String type;protected String goodName;protected int number;public abstract …

【Authing身份云-注册安全分析报告-无验证方式导致安全隐患】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

香港打工人√三天通过微软mos认证

在繁忙的香港&#xff0c;时间就是金钱&#xff0c;效率就是生命。作为一名香港的打工人&#xff0c;在这座竞争激烈的城市中&#xff0c;不断提升自我是保持竞争力的关键。最近&#xff0c;我完成了一项挑战&#xff1a;在短短三天内通过微软MOS认证大师。以下是我备考的经验分…

微调大模型:提高其代码修复能力的尝试

目录 一、作品背景&#xff1a; 二、作品目标&#xff1a; 三、作品技术方案&#xff1a; (1)标记化 (2)量化 (3) LoRA&#xff08;低秩自适应&#xff09;配置 (4)训练配置 (6)模型保存 四、作品效果&#xff1a; 一、作品背景&#xff1a; 随着大型模型技术的日益成…

SOMEIP_ETS_107: SD_Consider_Entries_Order

测试目的&#xff1a; 验证DUT在接收到包含两个条目的消息时&#xff0c;能够按照正确的顺序处理&#xff1a;首先是带有TTL 0的SubscribeEventgroup以删除订阅&#xff0c;然后是常规的SubscribeEventgroup以重新订阅。 描述 本测试用例旨在确保DUT能够正确处理订阅消息的顺…

Frida0C - Module相关API

亲爱的读者你们好啊&#xff0c;今天主要分享一下 frida 相关的学习文档&#xff0c;见文章最后一节。 Module var module Process.findModuleByAddress(Module.findBaseAddress("libc.so")) module.enumerateSymbols()enumerateSymbols 返回该 so 的符号表。 还…

Java项目: 基于SpringBoot+mybatis+maven医院管理系统(含源码+数据库+任务书+开题报告+毕业论文)

一、项目简介 本项目是一套基于SpringBootmybatismaven医院管理系统 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 该系统功能完善、界面美观、操作简单、…

ffmpeg安装测试(支持cuda支持SRT)

文章目录 背景安装ffmpeg直接下载可执行文件选择版本选择对应系统版本下载测试Linux下安装 查看支持协议以及编码格式 常见错误缺少 libmvec.so.1LD_LIBRARY_PATH 错误 GPU加速测试SRT服务器搭建下载srs5.0源码解压安装配置启动 SRT推流测试SRT播放测试 背景 在音视频开发测试中…

视频监控管理平台LntonAIServer视频智能分析抖动检测算法应用场景

在视频监控系统中&#xff0c;视频画面的稳定性对于确保监控效果至关重要。抖动现象是指视频画面中存在不稳定或频繁晃动的情况&#xff0c;这可能会影响视频的清晰度和可读性。LntonAIServer通过引入抖动检测功能&#xff0c;帮助用户及时发现并解决视频流中的抖动问题&#x…

【Python】从基础到进阶(七):深入理解Python中的异常处理与调试技巧

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 一、引言二、异常处理概述1. 什么是异常&#xff1f;2. 异常的捕获与处理 三、常见的异常类型四、自定义异常五、调试与日志记录1. 使用assert进行调试2. 使用日志记录 六、案例&#xff1a;文件操作与异常处理1. 需求分析2…

最新kubernetes的安装填坑之旅(新手篇)

Kubernetes&#xff08;常简称为 K8s&#xff09;是一个开源的容器编排平台&#xff0c;用于自动化部署、扩展和管理容器化应用程序&#xff0c;lz也不知道哪根脑经秀逗了&#xff0c;竟然妄挑战学习一下&#xff0c;结果折戟沉沙&#xff0c;被折腾的欲仙欲死&#xff0c;不过…

写的一致性问题之双删模式

文章目录 1、双删模式1.1、同步双删1.2、异步双删1.3、延时双删1.4、定时双删 在事务提交前后删除两次redis&#xff0c;会有性能问题 企业开发常用&#xff1a;延时双删、异步双删 1、双删模式 1.1、同步双删 实现思路&#xff1a;AOP 1.2、异步双删 在事务提交之后异步删除r…

redis群集的三种模式

目录 一、redis群集有三种模式 二、redis主从复制 2.1 概念 2.2 主从复制的作用 2.3 主从复制流程 三、搭建redis主从复制 四、redis哨兵模式 4.1 概念 4.2 哨兵模式原理: 4.3 哨兵模式的作用&#xff1a; 4.4 故障转移机制&#xff1a; 4.5 主节点的选举&#xff…