当前位置: 首页 > news >正文

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定义的是别名,别名是不能重复的

相关文章:

TS 常用类型

我们经常说TypeScript是JavaScript的一个超级 TypeScript 常用类型 TypeScript 是 JS 的超集&#xff0c;TS 提供了 JS 的所有功能&#xff0c;并且额外的增加了&#xff1a;类型系统 所有的 JS 代码都是 TS 代码 JS 有类型&#xff08;比如&#xff0c;number/string 等&…...

半导体芯闻--20240913

1、舜宇光学在2024年上半年业绩表现亮眼&#xff0c;营收和净利润同比大幅增长。公司资产规模维持较高水平&#xff0c;短期偿债能力强。研发投入持续增加&#xff0c;特别是在车载模组领域取得显著成绩&#xff0c;与多家主流平台方案厂商深度合作&#xff0c;巩固了其在车载模…...

C盘空间不足如何解决?解决C盘空间不足的7个方法

当计算机的C盘&#xff08;通常作为系统盘&#xff09;空间不足时&#xff0c;会严重影响系统的运行效率和稳定性。针对这一问题&#xff0c;以下7个解决方案&#xff0c;可以帮助我们有效释放C盘空间&#xff0c;提升系统性能。 1.磁盘清理 利用Windows内置的磁盘清理工具…...

比 GPT-4 便宜 187 倍的Mistral 7B (非广告)

Mistral 7B 是一种设计用来快速处理较长文本的人工智能模型。它采用了一些特别的技术来提高速度和效率&#xff0c;比如“分组查询注意力&#xff08;grouped-query attention&#xff09;”和“滑动窗口注意力&#xff08;sliding-window attention&#xff09;”。 这些技术…...

FFmpeg与OpenCV联合开发

本文讲述如何利用FFmpeg SDK与OpenCV 从RTSP流中获取图像&#xff08;OpenCV MAT 对象格式&#xff09;。 一&#xff0c;构造RTSP视频流 因为是在本机实验&#xff0c;所以我自己构造了一个RTSP流。如果你有现成的RTSP流也可以的。 实验用的源视频是黑神话悟空的《云宫讯音》…...

Docker 部署 Redis (图文并茂超详细)

部署 Redis ( Docker ) [Step 1] : 拉取 Redis 镜像, 推荐使用 7 的 Redis 版本 docker pull redis:7.0.12[Step 2] : 创建 Redis 相关目录 ➡️ 启动 Redis 容器 ➡️ 拷贝文件 ➡️ 授权文件夹 ➡️ 删除容器 # 创建 Redis 相关目录 mkdir -p /data/redis/{conf,data,log…...

Docker基础-Docker Compose使用

文章目录 一、什么是Docker Compose?常用命令 Docker Compose安装前提条件Linux 上的安装macOS 上的安装Windows 上的安装注意事项启动 Docker Compose 服务 Docker Compose使用Docker Compose 文件基本结构关键概念示例&#xff1a;更复杂的配置注意事项 参考文献 一、什么是…...

GPT撰写开题报告教程——课题确定及文献调研

撰写开题报告是一项复杂而重要的任务&#xff0c;需要涵盖从主题选择到文献综述、研究方法等多个环节。借助AI&#xff0c;如ChatGPT&#xff0c;可以显著提高这一过程的效率以及内容的质量。本文将详细探讨如何一步步利用ChatGPT撰写开题报告。 一、开题报告内容 一个清晰的…...

SprinBoot+Vue高校就业管理系统的设计与实现

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 application.yml3.5 SpringbootApplication3.5 Vue 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍&#xff1a;CSDN认证博客专家&#xff0c;CSDN平台Java领域优质…...

【人工智能】Transformers之Pipeline(十八):文本生成(text-generation)

目录 一、引言 二、文本生成&#xff08;text-generation&#xff09; 2.1 概述 2.2 家谱树谱——encoder or decoder is a problem 2.3 pipeline参数 2.3.1 pipeline对象实例化参数 2.3.2 pipeline对象使用参数 ​​​​​​​ 2.3.3 pipeline返回参数 ​​​​​​​…...

判断当前用户登录时常是否超过两个小时

有一个签到功能需要用户登录时间超过两个小时才可以签到 1. 存储登录时间戳 login() { // 假设这是登录后的操作 const currentTime new Date().getTime(); // 获取当前时间戳 localStorage.setItem(loginTimestamp, currentTime.toString()); // 存储登录时间戳 // 其他…...

nacos明明配置了远程连接地址却一直连接本地的详细配置解释

大家时间都很珍贵&#xff0c;我直接把方法放这 这个是yml文件&#xff0c;我们配置yml文件的时候&#xff0c;一定要把他的服务发现地址写了 这里是针对bootstrap做出的文件&#xff0c;注意名字&#xff0c;要和我们在yml文件里面的spring名字一样 yml discovery:是发现的意…...

Superset二次开发之源码 run-server.sh 分析

背景&#xff1a; 如果基于docker方式部署Superset项目&#xff0c;Dockerfile文件末尾指向了docker-ci.sh&#xff0c;而docker-ci.sh 脚本又指向了run-server.sh。因此我们重点分析一下run-server脚本 路径 docker\run-server.sh #!/usr/bin/env bashHYPHEN_SYMBOL-gunicorn…...

Java 之四种内部类详解

在 Java 中&#xff0c;除了传统的类定义方式之外&#xff0c;还有一种特殊的类定义方式——内部类。内部类定义在另一个类的内部&#xff0c;可以访问外部类的成员&#xff0c;这使得代码更加灵活和可读性更强。本文将详细讲解 Java 中四种内部类的类型&#xff1a;成员内部类…...

03:手动可变电阻

可变电阻 1、电位器2、变阻器/数字电位器2.1&#xff1a;变阻器2.2&#xff1a;数字电位器 3、电位器锥度4、寄生电感/电容 1、电位器 如上图所示&#xff1a;将可变的电阻作为分压器&#xff0c;那么这种可变的电阻就是电位器。例如&#xff1a;将L1连接负极&#xff0c;L3连接…...

嵌入式Linux电池管理(TODO)

&#xff08;TODO&#xff09; 在树莓派3B上使用电池供电需要考虑多个方面&#xff0c;包括电源管理、硬件连接和软件配置。以下是详细的步骤和建议&#xff1a; 1. 选择合适的电池 树莓派3B需要5V的电源供电&#xff0c;通常电流需求在2.5A左右。常见的电池选择包括&#xff1…...

Python 求亲和数

亲和数&#xff08;Amicable Numbers&#xff09;是指两个不同的正整数&#xff0c;它们的真因数&#xff08;即除去本身的所有因数&#xff09;之和与对方的数相等。 def sum_of_proper_divisors(n):"""计算一个数的真因子之和"""divisors_su…...

【C++】——vector模拟实现和迭代器失效问题

文章目录 模拟实现vector基本成员变量vector的构造与析构vector迭代器vector容量vector元素访问vector修改操作 vector迭代器失效问题什么是迭代器失效1.插入元素导致迭代器失效2.删除元素导致迭代器失效3.重新分配空间导致迭代器失效 如何解决迭代器失效问题 模拟实现 vector…...

USB 3.1 标准 A 型连接器及其引脚分配

USB 3.1 标准 A 型连接器 USB 3.1 标准 A 型连接器被定义为主机连接器。它具有与 USB 2.0 标准 A 型连接器相同的配合接口&#xff0c;但增加了另外两对差分信号和一个接地引脚。 USB 3.1 标准 A 型插座可以接受 USB 3.1 标准 A 型插头或 USB 2.0 标准 A 型插头。类似地&…...

机器学习文献|基于循环细胞因子特征,通过机器学习算法预测NSCLC免疫治疗结局

今天我们一起学习一篇最近发表在Journal for immunotherapy of cancer &#xff08;IF 10.9&#xff09;上的文章&#xff0c;Machine learning for prediction of immunotherapeutic outcome in non-small-cell lung cancer based on circulating cytokine signatures[基于循环…...

Flask RESTful 示例

目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题&#xff1a; 下面创建一个简单的Flask RESTful API示例。首先&#xff0c;我们需要创建环境&#xff0c;安装必要的依赖&#xff0c;然后…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

DAY 47

三、通道注意力 3.1 通道注意力的定义 # 新增&#xff1a;通道注意力模块&#xff08;SE模块&#xff09; class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

(二)原型模式

原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

Nuxt.js 中的路由配置详解

Nuxt.js 通过其内置的路由系统简化了应用的路由配置&#xff0c;使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

selenium学习实战【Python爬虫】

selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...

Python 包管理器 uv 介绍

Python 包管理器 uv 全面介绍 uv 是由 Astral&#xff08;热门工具 Ruff 的开发者&#xff09;推出的下一代高性能 Python 包管理器和构建工具&#xff0c;用 Rust 编写。它旨在解决传统工具&#xff08;如 pip、virtualenv、pip-tools&#xff09;的性能瓶颈&#xff0c;同时…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式

今天是关于AI如何在教学中增强学生的学习体验&#xff0c;我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育&#xff0c;这并非炒作&#xff0c;而是已经发生的巨大变革。教育机构和教育者不能忽视它&#xff0c;试图简单地禁止学生使…...

虚拟电厂发展三大趋势:市场化、技术主导、车网互联

市场化&#xff1a;从政策驱动到多元盈利 政策全面赋能 2025年4月&#xff0c;国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》&#xff0c;首次明确虚拟电厂为“独立市场主体”&#xff0c;提出硬性目标&#xff1a;2027年全国调节能力≥2000万千瓦&#xff0…...

Go语言多线程问题

打印零与奇偶数&#xff08;leetcode 1116&#xff09; 方法1&#xff1a;使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...