TypeScript 学习第三步——基本类型,量大管饱(边🦑边✍️

前言

部门方向老大:主要用ts写,很重要

我:好的,正好我也学了一些(点点)ts

ps:又到了(🦑)时间学习了......

数据类型有哪些?

可以将 TS 中的常用基础类型细分为两类:

  1. js 已有类型
  2. ts 新增类型。

js 已有类型:

  • 原始类型: number、string、boolean、null、undefined、symbol。
  • 引用类型: set、map、正则、obiect(包括数组、对象、函数等对象)。

ts 新增类型:

  • 联合类型
  • 自定义类型(类型别名)
  • 接口
  • 元组
  • 字面量类型
  • 枚举
  • void
  • any 等

示例:

js 复制代码
// ts常见数据类型
// 原始类型
let age: number = 18;
let myName: string = 'mike';
let isBoolean: boolean = true;
let isNull: null = null;
let isUndefined: undefined = undefined;
let isSymbole: symbol = Symbol('sym');
let isBigint: bigint = 10n;

// 引用类型
let arr: number[] = [1, 2, 3]; //或者 let tuple: [string, number] = ['mike', 18];
let isObj: object = { name: 'mike' };
let isFunction: Function = () => {
  console.log('hello');
};

1. 联合类型

来表示俩个或及其以上的类型进行联合使用。

示例:

ts 复制代码
//联合类型
// 添加小括号,表示:arr是数组,它能够出现number和string两种类型
let arr: (number | string)[] = [1, 2, 'mike'];
// 不添加小括号,表示:arr1只能是number或string数组
let arr1: number | string[] = 12;

2. 自定义类型(类型别名)

类型别名可以给一个类型起一个新名字,使代码更具可读性和可维护性。通过类型别名,可以简化复杂类型的使用,并且提高代码的可读性,使用关键字type来创建。

示例:

typescript 复制代码
// 类型别名
type MyArray = (number | string)[];
let arr1: MyArray = [1, 2, 'mike'];
let arr2: MyArray = [1, 'joker', 2];

3. 函数类型

3.1 指定参数、返回值的类型

函数的类型实际上指的是函数参数和返回值的类型,函数指定类型的两种方式:

  • 单独指定参数、返回值的类型
  • 同时指定参数、返回值的类型。

1. 单独指定参数、返回值的类型:

ts 复制代码
// 1.单独指定参数、返回值类型:
function add(a: number, b: number): number {
  return a + b;
}
// 箭头函数形式
const add2 = (a: number, b: number): number => {
  return a + b;
}

2. 同时指定参数、返回值的类型:

ts 复制代码
// 2.同时指定参数、返回值类型:
const add: (a: number, b: number) => string = (a, b) => { return `${a + b}` }
console.log( add(1, 2), typeof add(1, 2) === 'string'); // 打印:3 true

注意:这种形式只适用于函数表达式。

这里可以看到箭头函数里面的参数已经被赋为number类型,这种写法比较绕,有点类似于lambda的匿名函数写法,但本质上是对箭头函数的参数类型更全面的补充,当函数作为表达式时,可以通过类似箭头函数形式的语法来为函数添加类型。

类似于:

ts 复制代码
// 箭头函数形式
// 1.单独指定参数、返回值类型:
const add2 = (a: number, b: number): number => {
  return a + b;
}

// 2.同时指定参数、返回值类型:
const add = (a, b) => {}
          ⬇️                                 ⬇️
const add  : (a: number, b: number) => string   = (a, b) => {}

表明参数 a, b 的数据类型以及返回的形式(但是我本人真的觉得没必要这么写,更倾向于第一种写法,但ts就是有这种语法......)

3.2 void 返回类型

void 表示没有任何类型,通常用于函数没有返回值时的情况。通过声明函数返回类型为 void,可以明确表明函数不会返回任何值。

示例:

ts 复制代码
function greet(): void {
  console.log('Hello!');
}

3.3 函数可选参数

使用函数实现某个功能时,参数可以传也可以不传。这种情况下,在给函数参数指定类型时,就用到可选参数了。比如数组的 slice 方法,可以slice(),也可以 slice(1)还可以 slice(1, 3)。

可选参数:在可传可不传的参数名称后面添加?(问号)。

注意:可选参数只能出现在参数列表的最后,也就是说可选参数后面不能再出现必选参数。

示例:

ts 复制代码
function mySlice(start?: number, end?: number): void{
    console.log('起始索引:', start ? start : 0, '结束索引:', end ? end : start ? start : 0)
}

mySlice()
mySlice(1)
mySlice(1, 3)

结果:

4. 对象类型

4.1 参数的类型及其返回值

JS 中的对象是由属性和方法构成的,而 TS 中对象的类型就是在描述对象的结构(有什么类型的属性和方法)。

示例:

ts 复制代码
//js 写法
let person = {
    name: 'mike',
    age: 18,
    say() {
        console.log('hello world')
    },
    greet(name){
        console.log(`${name} say hello`)
    }
}

//ts 写法
let person: { name: string, age: number, say(): void, greet(name: string): void } = {
    name: 'mike',
    age: 18,
    say() {
        console.log('hello world')
    },
    greet(name){
        console.log(`${name} say hello`)
    }
}
person.greet('mike') // 打印 mike say hello

特点:

  1. 使用{}来描述对象结构,属性采用 属性名:类型 的形式;方法采用 方法名():返回值类型 的形式。

  2. 如果方法有参数,就在方法名后面的小括号中指定参数类型,例如如: greet(name: string): void

  3. 在一行代码中指定对象的多个属性类型时,也可以使用;来分隔

  4. 如果一行代码只指定一个属性类型(通过换行来分隔多个属性类型)可以去掉;或者,

  5. 方法的类型也可以使用箭头函数形式,例如:say: () => void

示例:

ts 复制代码
let person: {
    name: string
    age: number
    say: () => void; 
    greet: (name: string) => void; 
} = {
    name: 'mike',
    age: 18,
    say: () => {
        console.log('hello world');
    },
    greet: (name: string) => {
        console.log(`${name} say hello`);
    }
}

person.greet('mike') // 打印 mike say hello

4.2 对象可选属性

对象的属性或方法,也可以是可选的,此时就用到可选属性了。 比如,我们在使用axios时,如果发送 GET 请求,method属性就可以省略。

可选属性的语法与函数可选参数的语法一致,都使用?来表示。

示例:

ts 复制代码
function myAxios(config: { url: string, method?: string }){
    config.method = config.method ? config.method : 'get';
    return config
}
console.log(myAxios( { url: 'https://4399.com' } ))
// 打印:{ url: 'https://4399.com', method: 'get' }

function myUrl(baseurl: string, id?: string ) {
    return `${baseurl}` +  `${id ? id : '/flash/zmhj.htm'}`
}

console.log(myUrl( 'https://4399.com' ))
// 打印:https://4399.com/flash/zmhj.htm

5. 接口

5.1 interface 关键字

当一个对象类型被多次使用时,一般会使用接口interface来描述对象的类型,达到复用的目的。

特点:

  1. 使用 interface 关键字来声明接口。
  2. 接口名称,可以是任意合法的变量名称。
  3. 声明接口后,直接使用接口名称作为变量的类型。
  4. 若每一行只有一个属性类型,属性类型后可以没有;

示例:

ts 复制代码
interface myPerson {
    name: string
    age: number
    sayHi(): void
    greet(name: string): void
}

let person: myPerson = {
    name: 'mike',
    age: 19,
    say() {
        console.log('hello world')
    },
    greet(name) {
        console.log(`${name} say hello`);
    }
}

5.2 接口和类型别名的对比

interface(接口)type(类型别名)的对比:

  • 相同点:都可以给对象指定类型。
  • 不同点:接口只能为对象指定类型;类型别名还可以为任意类型指定别名。

示例:

typescript 复制代码
// type 关键字
type otherPerson = number | string
let otherperson: otherPerson = 'mike'

type myNewPerson = {
    name: string
    age: number
    sayHi(): void
    greet(name: string): void
}
let newperson: myNewPerson = {
    name: 'mike',
    age: 19,
    sayHi() {
        console.log('hello world')
    },
    greet(name) {
        console.log(`${name} say hello`);
    }
}

//interface 关键字
interface myPerson {
    name: string
    age: number
    sayHi(): void
    greet(name: string): void
}
let person: myPerson = {
    name: 'mike',
    age: 19,
    sayHi() {
        console.log('hello world')
    },
    greet(name) {
        console.log(`${name} say hello`);
    }
}

注意:interface(接口)类似于对对象的一种修饰(对象继承)不用添加=type(类型别名)是自定义类型需要添加=

5.3 接口继承

如果两个接口之间有相同的属性或方法,可以将公共的属性或方法抽离出来,通过继承来实现复用。例如,这两个接口都有x、y两个属性,虽然可以重复写两次,但很繁琐。

示例:

typescript 复制代码
interface A { x: number, y: number }
interface B { x: number, y: number, z: number }

这里可以使用extends关键字来实现继承,这种写法更推荐。

typescript 复制代码
interface A { x: number, y: number }
interface B extends A { z: number }

使用extends(继承)关键字实现了接口 B 继承 A。继承后,B 就有了 A 的所有属性和方,此时,B 同时有 x、y、z 三个属性。

ps:这里其实就是es6的类似于calss关键字的继承,编程就是一个巨大的java~

6. 元组

大家可以想一个场景下,在地图中,我们使用经纬度坐标来标记位置信息,那么是不是只有两个数字来进行代表呢?

示例:

ts 复制代码
// 数组
let position: number[]=[39.5427,116.2317]

// 可以出现多个数字的数组,但是我只想规定该数组只有俩个数字
//let position: number[]=[39.5427,116.2317,12.54,90.78]

可以使用数组来记录坐标,那么,但是我们的经纬度坐标的数组中只有两个元素,并且这两个元素都是数值类型,而number[]不严谨,因为该类型的数组中可以出现任意多个数字,这时候就可以用到元组了。

ts 复制代码
// 元组类型可以确切地标记出有多少个元素,以及每个元素的类型
let newposition: [number, number]=[39.5427,116.23171]
  • 使用 number[] 的缺点:不严谨,因为该类型的数组中可以出现任意多个数字
  • 更好的方式:元组(Tuple)。元组类型是另一种类型的数组,它确切地知道包含多少个元素,以及特定索引对应的类型

错误编写实例:

7. 类型推论

在TS中,某些没有明确指出类型的地方,TS的类型推论机制会帮助提供类型,换句话说,由于类型推论的存在,这些地方,类型注解可以省略不写!

如果不知道类型,可以通过鼠标放在变量名称上,利用VSCode的提示来查看类型。

发生类型推论的两种常见场景:

  1. 声明变量并初始化时
  2. 决定函数返回值时

注意:这两种情况下,类型注解可以省略不写!

如果是如下的第一种这种编写,声明变量并初始化时,不能再改变类型,因为此时类型已经通过类型推论决定好为number类型。如果是第二种写法声明变量并没有初始化时,则类型会声明为any类型。

注意:在js编写的情况下,不会存在这种问题。

ps:能省略类型注解的地方就省略(偷懒呗,充分利用TS类型推论的能力,可以提升开发效率)

8. 类型断言

有时候我们需要比TS更加明确一个值的类型,此时可以使用类型断言来指定更具体的类型。

示例:

ts 复制代码
<a href="http://www.baidu.com/" id="baidu">百度一下,你就知道</a>

注意getElementByld方法返回值的类型是HTMLElement,该类型只包含所有标签公共的属性或方法,不包含a标签特有的href等属性。 因此,这个类型太宽泛,指代类型不具体,无法操作href<a>标签特有的属性或方法,这种情况下就需要使用类型断言指定更加具体的类型。

常规写法: 会报错,因为HTMLElement类型无href等属性

类型断言写法: 不报错,因为通过类型断言判断类型为HTMLAnchorElement,具有href等属性

特点:

  1. 使用as关键字实现类型断言。
  2. 关键字as后面的类型是一个更加具体的类型,这里HTMLAnchorElementHTMLElement的子类型。通过类型断言,变量a的类型变得更加具体,这样就可以访问<a>标签特有的属性或方法了。
  3. 另一种写法,使用<>语法,这种语法形式我们知道就行了。

题外话:这种情况下怎么准确知道是哪种类型?

方法一:上网搜索

方法二:编译器提示

方法三:在浏览器控制台通过console.dir()打印DOM元素,在属性列表的最后面可看到该元素的类型。

9 字面量类型

注意看以下代码:

ts 复制代码
let say = 'hello world'
const newsay = 'hello world'

我们可以通过前面的学习知道say的类型是string,那么newsay呢?

我们会很惊奇地发现常量newsay的类型为'hello world',什么鬼,这也能作为类型的一种吗?没错,这就是字面量类型

通过类型推论机制,我们这么想:

  1. say 是一个let变量,它的值可以是任意字符串,所以类型为string
  2. newsay 是一个const常量,它的值不能变化只能是'hello world',所以,它的类型为'hello world'

此处的'hello world'就是一个字面量类型,也就是说某个特定的字符串(或者对象、数字等)都可以作为TS中的类型。

ts中的字面量类型可以用来约束变量的取值范围,具有以下几个常见的使用场景:

  1. 限制参数类型:字面量类型可以确保变量只能取特定的几个值,提供更严格的类型检查。
typescript 复制代码
function setColor(color: 'red' | 'green' | 'blue') {
    // 函数体
}

setColor('red');   // 正确
setColor('yellow'); // 错误,'yellow'不是有效的取值
  1. 表示状态:有些情况下,变量只可能处于特定的几种状态,这时可以使用字面量类型来定义。
typescript 复制代码
type Status = 'pending' | 'approved' | 'rejected';

let applicationStatus: Status = 'pending';
  1. 约束对象属性值:在定义对象类型时,可以使用字面量类型来约束对象的属性值,这样可以确保对象的属性值符合预期。
typescript 复制代码
type Person = {
    name: string;
    gender: 'male' | 'female';
    age: number;
}

let person: Person = {
    name: 'Alice',
    gender: 'female', // 只能是'male'或'female'
    age: 30
};

10. 枚举

枚举:定义一组命名常量,它描述一个值,该值可以是这些命名常量中的一个。使用enum关键字定义枚举、

枚举的功能类似于字面量类型加上联合类型组合的功能,也可以表示一组明确的可选值

10.1 数字枚举

示例:

ts 复制代码
enum Direction {
    Up,
    Down,
    Left = 10,
    Right
}

// 使用枚举
function move(direction: Direction){
    return direction;
}
console.log(move(Direction.Up)); // 输出 0
console.log(move(Direction.Down)); // 输出 1
console.log(move(Direction.Left)); // 输出 10
console.log(move(Direction.Right)); // 输出 11

特点:

  1. 使用enum关键字定义枚举、
  2. 约定枚举名称、枚举中的值以大写字母开头
  3. 枚举中的多个值之间必须要通过,(逗号)分隔。
  4. 定义好枚举后,直接使用枚举名称作为类型注解

注意:

形参direction的类型为枚举Direction,那么,实参的值就应该是枚举 Direction成员的任意一个访问枚举成员,类似于js中的对象,直接通过点(.)语法访问枚举的成员。

枚举成员是有值的,默认为从0开始自增的数值,可以初始化值,初始化之后的变量若没有初始化值则默认从前一个变量的值增长1,以此类推。

10.2 字符串枚举

示例:

ts 复制代码
enum Direction {
    Up = "UP",
    Down = "DOWN",
    Left = "LEFT",
    Right = "RIGHT",
}

// 使用枚举
function move(direction: Direction){
    return direction;
}
console.log(move(Direction.Up)); // 输出 "UP"

注意:

字符串枚举没有自增长行为,所以字符串枚举的每个成员除了第一个默认为数字类型外的其他所有成员必须有初始值,否则会报错。

第一个变量没有初始化会默认为数字类型值为0。

题外话:

枚举是ts为数不多的非js类型级扩展(不仅仅是类型)的特性之一。因为其他类型仅仅被当做类型,而枚举不仅用作类型,还提供值(每个枚举成员都是有默认值的),其他的类型会在编译为JS代码时自动移除。但是枚举类型会被编译为JS代码!

枚举与前面讲到的字面量类型+联合类型组合 的功能类似,都用来表示一组明确的可选值列表。一般情况下,推荐使用字面量类型+联合类型组合的方式,因为相比枚举,这种方式更加直观、简洁、高效。

11. any

原则:

不推荐使用any,因为这会让 TypeScript 变为 "AnyScript",会失去ts的类型保护机制,因为当值的类型为any时,可以对该值进行任意操作,并且不会有代码提示,这样的话就没必有ts这门语言了。

两者对比:

规范编写:会报一堆错误

any编写:并不会报错

特点:

any类型编写不会有任何类型错误提示,即使可能存在错误!尽可能的避免使用any类型,除非临时使用any来"避免"编写很长、很复杂的类型!

其他隐式具有any类型的情况:

  1. 声明变量不提供类型也不提供默认值
  2. 函数参数不加类型。

因为不推荐使用any,所以,这两种情况下都应该提供类型!

ts 拓展:typeof 方法

我们都知道JS中提供了typeof操作符,用来在js中获取数据的类型,实际上,ts也提供了typeof操作符,可以在类型上下文中引用变量或属性的类型(类型查询)

ts 复制代码
let a: string = "Hello World";
let b: 'Hello World' = 'Hello World';
enum Direction {
    Up,
    Down = "DOWN",
    Left = "LEFT",
    Right = "RIGHT",
}

console.log(typeof a); // 输出 string
console.log(typeof b); // 输出 string
console.log(typeof Direction); // 输出 object

let c = { x: 1, y: 2}
console.log(typeof c); // 输出 object
console.log(typeof c.x); // 输出 number
function add(point: typeof c){
    return point.x + point.y
}

console.log(add(c)) // 输出 3
console.log(add({x: 100, y: 200})) // 输出 300

特点:

  1. 使用typeof操作符来获取变量c的类型,也可以获取变量内部属性的类型。
  2. typeof可以出现在类型注解的位置(参数名称的冒号后面),所代表的类型基于环境上下文(区别于js的typeof返回的只是个字符串)
  3. typeof尽量只用来查询变量或属性的类型,无法通过类型注解来查询其他形式的类型(比如函数调用的类型)

特点1:

特点2:

特点3:

后记

这些就是ts常见类型的所有内容啦,后面还有ts高级类型等......

🦑了一个星期,慢慢磨洋工,总🧄写完了......

这篇文章是写的最慢最久的一篇了,写完了整个人都舒畅了很多......

参考内容:www.bilibili.com/video/BV14Z...

相关推荐
小样还想跑15 分钟前
axios无感刷新token
前端·javascript·vue.js
字节跳跃者16 分钟前
为什么Java已经不推荐使用Stack了?
javascript·后端
字节跳跃者16 分钟前
深入剖析HashMap:理解Hash、底层实现与扩容机制
javascript·后端
Java水解24 分钟前
一文了解Blob文件格式,前端必备技能之一
前端
用户3802258598241 小时前
vue3源码解析:响应式机制
前端·vue.js
UrbanJazzerati1 小时前
使用 Thunder Client 调用 Salesforce API 的完整指南
面试·visual studio code
bo521001 小时前
浏览器渲染机制详解(包含渲染流程、树结构、异步js)
前端·面试·浏览器
普通程序员1 小时前
Gemini CLI 新手安装与使用指南
前端·人工智能·后端
Web小助手1 小时前
js高级程序设计(日期)
javascript
Web小助手1 小时前
js高级程序设计(4/5章节)
javascript