TypeScript学习(1)

🔥keyipatience:个人主页

🎬作者简介:C/C++后端开发学习者

🌟专栏传送门:《c++》《linux》《c++高阶数据结构》《c++数据结构与算法》《前端学习》

⭐️patience is key in life

TypeScript 是什么

  • TypeScript(简称:TS)是 JavaScript 的超集(JS 有的 TS 都有)。
  • TypeScript = Type + JavaScript(在 JS 基础之上,为 JS 添加了类型支持)。
  • TypeScript 是微软开发的开源编程语言,可以在任何运行 JavaScript 的地方运行。

简单理解

  1. 超集:所有合法的 JS 代码,都是合法的 TS 代码;TS 在 JS 基础上新增语法。
  2. 核心特点 :给 JS 加上静态类型,写代码时就能检查类型错误,而不是等到运行时报错。
  3. 运行原理 :TS 不能直接在浏览器 / Node 执行,需要编译转换成普通 JS再运行。

TypeScript 为什么要为 JS 添加类型支持?

背景

JS 的类型系统存在先天缺陷 ,JS 代码里绝大多数错误都是类型错误(Uncaught TypeError)

后果:增加找 Bug、改 Bug 的时间,降低开发效率

静态类型 vs 动态类型

  • TS:静态类型语言 → 编译期做类型检查
  • JS:动态类型语言 → 执行期做类型检查

代码流程顺序:1 编译 → 2 执行

  1. JS:代码运行起来之后,才能发现类型错误(发现得晚)
  2. TS:编译阶段(执行代码之前) 就能捕获错误;搭配 VSCode,写代码的时候就提示报错
  3. 收益:提前发现问题,减少排查、修复 bug 的时间

对比

  • JS:边跑边检查,出错在运行时
  • TS:写代码 / 编译时就检查,提前拦截类型错误

eg:

复制代码
// JS:不会报错,直到运行才崩
let num = 100
num.toUpperCase() 
// 运行时报 Uncaught TypeError,代码写完看不出问题

// TS:写代码时就标红提示错误
let num:number = 100
num.toUpperCase() 
// 还没运行,编辑器直接提示:数字没有toUpperCase方法

TypeScript 相比 JS 的优势

  1. 更早发现错误:写代码的时候就能捕获问题,减少排查、修复 bug 的时间,提升开发效率。
  2. 完善代码提示:代码任意位置都有智能提示,开发更安心,提升开发体验。
  3. 代码可维护性强:类型系统让重构代码更简单,适合大型项目。
  4. 支持最新 ES 语法:可以优先使用 ECMAScript 新特性。
  5. 类型推断:不用每一处都手动写类型标注,在获得类型好处的同时降低编码成本。

框架生态:Vue3 源码由 TS 重写,Angular 原生默认 TS,React 和 TS 可以完美配合。TS 已经是大中型前端项目首选语言。

类型推论

两个最常见场景

1. 声明变量并赋值

复制代码
let age = 18

TS 看到18,自动推断age是number类型,等价于

复制代码
let age: number = 18

这里: number可以省略

  1. 函数返回值推断

    function add(num1: number, num2: number) {
    return num1 + num2
    }

TS 发现 return 返回两个数字相加,自动推断返回值是 number,等价于:

复制代码
function add(num1: number, num2: number): number {
  return num1 + num2
}

返回值: number可以省略

核心规则

  • 变量只声明、不赋值,TS 无法推断,此时必须手动标注类型

    let a; // 推断为 any(不推荐)
    let b: number; // 没有初始化,必须写类型

推荐原则:能让 TS 自动推断,就不手动写类型,代码更简洁

技巧:如果不知道类型,可以通过鼠标放到变量名称上,用vscode的提示看类型

类型注解

因为有类型推断,所以复杂场景才需要手动写类型注解

复制代码
let age: number = 18

: number 这部分就是类型注解

  • 作用 :给变量增加类型约束 。这里约定变量age只能是number数值类型。
  • 规则 :约定好类型之后,只能赋值同类型数据,类型不匹配就直接报错

错误示例

复制代码
let age: number = '18'

报错信息:不能将类型string分配给类型number,ts (2322) 解析:'18'是字符串,和约定的 number 类型冲突,编辑器直接标红。

常用基础类型

TS 基础类型分为两大类:JS 已有类型 、TS 新增类型

1. JS 已有类型

  • 原始类型:number / string / boolean / null / undefined / symbol

  • 对象类型:object(包含数组、普通对象、函数等)

    let age: number = 18
    let myName: string = '刘老师'
    let isLoading: boolean = false
    // null、undefined、symbol写法同理
    let n: null = null
    let u: undefined = undefined
    let s: symbol = Symbol()
    //每次调用Symbol()生成的值,永远是唯一的。

2. TS 新增类型

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

数组类型

数组两种写法

复制代码
// 写法1:简写,推荐
let numbers: number[] = [1, 3, 5]

// 写法2:泛型写法 Array<类型>
let strings: Array<string> = ['a', 'b', 'c']

数组包含多种类型(联合类型 |)

需求:数组里同时有 number 和 string

复制代码
let arr: (number | string)[] = [1, 'a', 3, 'b']
  • | 是联合类型 :代表可以是竖线两边任意一种类型
  • 注意:TS 联合是单竖线 | ,不要和 JS 逻辑或 || 搞混
  • 括号不能少:(number | string)[] 代表数组的成员可以是数字或字符串

易错对比: number | string[] → 含义是:要么是一个数字,要么是字符串数组(不是我们想要的)

类型别名

类型别名(自定义类型):给任意类型起一个简短的别名。

适用场景:复杂类型需要重复写很多次的时候,用别名简化代码

复制代码
// type 关键字定义别名
type CustomArray = (number | string)[]

// 直接使用别名当做类型
let arr1: CustomArray = [1, 'a', 3, 'b']
let arr2: CustomArray = ['x', 'y', 6, 7]

函数类型

函数类型 = 参数类型 + 返回值类型 两种写法:

  1. 直接给参数、返回值标注类型
  2. 一次性整体定义函数类型

写法 1:普通 function 声明

复制代码
function add(num1: number, num2: number): number {
  return num1 + num2
}
  • (num1: number, num2: number): 标记参数类型
  • 括号后面的 : number标注返回值类型
  • 这里没有类型描述用的=>,就是普通 js 函数

写法 2:箭头函数

复制代码
const add = (num1: number, num2: number): number => {
  return num1 + num2
}
  • (num1: number, num2: number): 标记参数类型
  • 括号后 : number 标记返回值类型
  • =>:JS 箭头函数本身的语法,代表函数体开始

写法3:整体标注函数类型

复制代码
const add: (num1: number, num2: number) => number = (num1, num2) => {
  return num1 + num2
}

const add: 冒号 : 的意思:定义变量 add 的类型

(num1: number, num2: number) => number 这一段就是add 的类型

  • num1: number:参数 1 是数字,num2: number:参数 2 是数字
  • => number :意思:这个函数接收上面两个参数,返回数字 这是写类型 ,所以用 =>,不能写 :number

= 右边:函数真正的代码(函数实现,干活的)

(num1, num2) => { return num1 + num2 }

  • 这里不用写参数类型! TS 会看左边的说明书,自动推断 num1、num2 都是 number
  • 这是 JS 原生箭头 => ,代表函数体开始

注意:

: 是给「变量 / 参数」标记类型

(参数) => 返回值 是 TS 专门用来描述【函数类型】的固定模板

复制代码
// 1. 变量是数字(普通类型,用 :)
let x: number

// 2. 变量是函数(函数类型,用 =>)
let fn: () => number
//() => number描述fn的类型
// fn是函数,无参数,返回number。这里不能写成 ():number

如果函数没有 return,返回类型写void

复制代码
function logMsg(msg:string): void {
  console.log(msg)
}

可选参数

复制代码
function mySlice(start?: number, end?: number): void {
  console.log('起始索引:', start, '结束索引:', end)
}
  1. 语法 :参数名后面加 ? → 代表这个参数可传、可不传

    • start?: number 等价于 start: number | undefined
  2. 硬性要求 :可选参数必须放最后,可选后面不能跟必选参数

    // ❌ 错误!可选参数后面有必选
    function fn(a?:number, b:number){}

    // ✅ 正确,必选在前,可选在后
    function fn(a:number, b?:number){}

调用示例

复制代码
mySlice()         // 不传,start=undefined, end=undefined
mySlice(1)        // 只传start
mySlice(1,3)      // 两个都传

默认参数

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

参数后面直接写 = 默认值 start: number = 0 含义:调用时不传这个参数,start 自动等于

带默认值的参数,也建议放后面

复制代码
mySlice()        // start=0,end=undefined
mySlice(2)       // start=2,end=undefined
mySlice(2,5)     // start=2,end=5

可选参数 ? 的区别

  1. start?: number:不传 → undefined
  2. start = 0:不传 → 自动赋值 0,不用写类型,TS 会自动推断

剩余参数

把多个剩余的参数打包成数组,用 ...。

...xxx: 类型[],收集剩下所有参数,只能放末尾

复制代码
function sum(a:number, ...others: number[]){
  console.log(a, others)
}
sum(1,2,3,4) 
// a=1,others=[2,3,4]

TS 对象类型

TS 对象类型:描述对象长什么样子(有哪些属性、哪些方法,各自是什么类型)

写法规则

1.TS 在对象类型的大括号内,逗号 ,、分号 ;、换行不写分隔符,三种都支持,效果完全一样。

复制代码
let person: { 
  name: string; 
  age: number; 
  sayHi(): void 
} = {
  name: 'jack',
  age: 19,
  sayHi() {}
}

// 逗号分隔合法
let person: {    
  name: string,   
  age: number,   
  sayHi(): void  
} = {   
  name: 'jack',   
  age: 19,   
  sayHi() {} 
}

// 直接不写
let person: {    
  name: string
  age: number   
  sayHi(): void  
} = {   
  name: 'jack',   
  age: 19,   
  sayHi() {} 
}

// 1. 逗号 
{ name: string, age: number, sayHi(): void }

// 2. 分号 ; 
{ name: string; age: number; sayHi(): void }

// 3. 换行,什么都不写
{
  name: string
  age: number
  sayHi(): void
}

2.属性:属性名: 类型

  • name:string → name 必须是字符串
  • age:number → age 必须是数字

3.函数方法的两种写法(等价)

复制代码
// 写法1
sayHi(): void
仿普通函数声明的语法

- sayHi:方法名
- ():无参数
- : void`:返回值类型


// 写法2 箭头函数形式(TS推荐)
sayHi: () => void

把它看成属性的类型标注

- sayHi: → sayHi 是一个属性
- () => void → 这个属性的类型是「一个无参、返回 void 的函数」

TS 对象可选属性

复制代码
function myAxios(config: { url: string; method?: string }) {
  console.log(config)
}
  • function myAxios:是函数,是我们定义的函数本身
  • config:是传给这个函数的参数 ,这个参数的类型是对象类型 { url:string; method?:string }
  • method?: string:是对象里面的属性 ,叫对象可选属性

拆开看

1.myAxios 是函数,调用的时候,要传一个对象进去

复制代码
// 传入的 {} 就是对象
myAxios({ url: '/api' })

2.这个传入的对象,结构要求:必须有 url(必选属性)method 可有可无(对象的可选属性)

重点:? 不是加在函数参数上!

  • 如果是函数可选参数 长这样:function fn(a?: number){} a 整个参数可传可不传
  • 现在是:config: { method?: string }
    • config 这个参数必须传(不能不传 config)
复制代码
// ① 函数【可选参数】:整个参数config可以不传
function myAxios(config?: { url:string, method:string }){}
myAxios() // ✅ 可以直接不传config

// ② 对象【可选属性】:config必须传,只是对象内部的method属性可以没有
function myAxios(config: { url:string, method?: string }){}
myAxios() // ❌ 报错!必须传config对象
myAxios({url:"/api"}) // ✅ 传了config对象,对象里省略method

interface 接口

一句话:接口就是把前面写在 {} 里的对象类型单独抽出来,起个名字,方便重复使用

复制代码
// 定义接口 IPerson,描述对象结构
interface IPerson {
  name: string
  age: number
  sayHi(): void
}

// 直接用 IPerson 当做类型
let person: IPerson = {
  name: 'jack',
  age: 19,
  sayHi() {}
}

和直接写对象字面量类型对比

复制代码
// 原来的写法:每次都要把一长串{}写一遍
let person: { name:string; age:number; sayHi():void } = {...}

// 接口写法:定义一次IPerson,到处复用
interface IPerson {
  name: string
  age: number
  sayHi(): void
}
let p1: IPerson = {...}
let p2: IPerson = {...}
let p3: IPerson = {...}

核心目的:复用对象类型 ,不用重复写一大串{...}

interface 和 type(类型别名)对比

相同点

都能描述对象的结构,下面两段代码效果完全一样:

复制代码
// interface 写法
interface IPerson {
  name: string
  age: number
  sayHi(): void
}

// type 类型别名写法
type IPerson = {
  name: string
  age: number
  sayHi(): void
}

不同点

  1. interface 接口

    • 只能用来描述对象(普通对象、函数对象、类)
    • 不能给基础类型、联合类型起别名
    • 特点:支持自动合并声明 、extends 继承扩展(后面马上讲)
  2. type 类型别名

    • 万能 :可以给任意类型起别名,不限于对象
    • ✅ 对象
    • ✅ 基础类型 type Num = number
    • ✅ 联合类型type NumStr = number | string

    // type能干,interface做不到的例子
    type NumStr = number | string
    let a: NumStr = 10
    let b: NumStr = 'abc'

    // type 给基础类型起别名
    type MyStr = string
    let s: MyStr = 'hello'

接口继承 extends

核心一句话:A extends B,代表接口 A 自动拥有接口 B 里面全部属性,再加上自己新增的属性,复用公共代码,避免重复抄写

复制代码
// 父接口:二维点
interface Point2D { 
  x: number; 
  y: number 
}

// 子接口 Point3D 继承 Point2D
interface Point3D extends Point2D  {z: number}

继承完成后,Point3D 等价于同时拥有 x,y,z 三个属性

调用示例

复制代码
let p: Point3D = {
  x:10,
  y:20,
  z:30
}

可以多继承(一个接口同时继承多个接口)

复制代码
interface A {a:number}
interface B {b:number}
interface C extends A,B {c:number}
// C 拥有 a,b,c
相关推荐
xifangge20251 小时前
基于 JavaScript 实现 CNC 数控切削参数与螺纹公差纯前端核算引擎
开发语言·前端·javascript
wangruofeng2 小时前
2026 Web 技术选型指南,AI 时代如何选择适合项目的技术栈
前端·aigc·ai编程
honkun62 小时前
vue 表格组件 vxe-table 修改滚动条样式,修改滚动条样式,统一宽度、颜色与自动隐藏
前端·javascript·vue.js
lazy H2 小时前
【计算机网络】第 2 篇:一条数据如何穿过网络?彻底理解数据封装与解封装
网络·经验分享·后端·学习·计算机网络
我命由我123452 小时前
软件工程概念 - 可插拔性
java·后端·学习·java-ee·系统架构·软件工程·学习方法
爱勇宝2 小时前
研究数字孪生的这半年,做了个 Demo,也有了一些新想法
前端·程序员
不可能片场3 小时前
工程实践 一个缓存把选片从十分钟压到一分钟
前端·electron
前端小万3 小时前
探索副业五个月,最终以放弃告终
前端·后端·面试
FRing033 小时前
JavaScript入门到精通二十四(正则表达式、元字符、修饰符)
开发语言·前端·javascript·正则表达式