主题
TypeScript 核心(一)
本页对应 TypeScript 核心,共 6 段视频,约 51 分钟。
学习目标
- 能用自己的话说明 TypeScript 与 JavaScript 的关系(TS = JS + 类型约束),并指出 TS 属于强类型语言、JS 属于弱类型语言在变量赋值上的具体差异。
- 能独立完成 TypeScript 编译环境的搭建:全局安装
typescript包、使用tsc命令将.ts文件编译为.js文件并用node运行,或使用工程化脚手架命令创建具备自动编译能力的项目并验证代码在浏览器中正常运行。 - 能使用「冒号 + 类型」的语法为变量添加类型注解,说明类型注解对赋值范围的限制和类型提示两个作用,并正确注解
number、string、boolean等简单类型。 - 能用「类型 + 中括号」和「
Array<类型>」两种语法为数组添加类型注解,并解释注解同时约束了数组成员类型的意义。 - 能使用竖线
|编写联合类型(含在数组成员中配合小括号使用的正确写法),并用type关键字定义类型别名来简化和复用复杂类型。 - 能为函数声明和函数表达式添加参数与返回值类型注解(含分开注解和借助类型别名整体注解两种写法),并说明参数注解同时带来的「类型限制」与「必填」两重约束。
知识点
认识 TypeScript:从 JS 到强类型
为什么需要:JS 是弱类型语言,变量可以随意赋不同类型的值,错误往往要到运行时才暴露。怎么想:TS 可以理解为“JS + 类型约束”,即 TS = JS + type,让变量一旦标注类型,就不能随意改成其他类型。怎么做:先建立这个整体认识:TS 代码不能直接在浏览器或 Node 中运行,需要先编译成 JS;开发时享受类型检查,运行时执行编译后的 JS。易错点:不要把 TS 当成一门完全脱离 JS 的新语言,它最终仍要编译为 JS。
ts
let count: number = 100
console.log(count)搭建编译环境:手动与自动
为什么需要:浏览器和 Node 不能直接运行 TS,必须把 TS 编译成 JS。怎么想:手动编译适合理解原理,自动编译适合实际开发。怎么做:先全局安装 TypeScript,执行 npm install typescript -g,再用 tsc -v 检查版本。新建 hello.ts,执行 tsc hello.ts 会生成 hello.js,最后用 node hello.js 运行。实际项目可用 Vite:npm create vite@latest ts-pro -- --template vanilla-ts,然后 npm install、npm run dev。易错点:手动编译时不要忘记先编译再运行;自动环境里通常在 src/main.ts 写代码,浏览器控制台查看结果。转写稿中的调试过程是:tsc hello.ts 后生成 hello.js,node hello.js 输出 100,说明“开发写 TS、运行跑 JS”。
类型注解:变量只能按约定赋值
为什么需要:给变量加类型约束,可以提前发现赋值错误,并获得属性/方法提示。怎么想:语法是“变量名: 类型”,例如 message: string,表示只能赋字符串。怎么做:
ts
let message: string = 'hello'
message = 'world'
// message = 100 // 报错:不能将类型 number 分配给类型 string输入 message. 时,编辑器会提示字符串相关方法。易错点:简单类型注解要全小写,如 number、string、boolean;不要写成大写或拼错。常见报错是“不能将类型 number 分配给类型 string”。
数组类型:限制成员类型
为什么需要:只限制“是数组”还不够,还要限制数组成员类型。否则 [1, 2, 3, '4'] 在遍历调用 toFixed 时可能运行时报错。怎么想:数组注解有两种写法:number[] 和 Array<number>,实际开发更常用前者。怎么做:
ts
let arr: number[] = [1, 2, 3]
let arr2: Array<number> = [1, 2, 3]
// let arr3: number[] = [1, '2'] // 报错易错点:number[] 限制的是“数组且每项都是 number”;泛型写法 Array<number> 作用相同。不要误以为只限制数组整体而不限制成员。
联合类型与类型别名
为什么需要:一个数组可能既要存字符串又要存数字,单一类型不够用。怎么想:用 | 把多个类型合并,如 string | number;复杂类型可抽成 type 别名复用。怎么做:
ts
let arr: (string | number)[] = ['jack', 100]
type ItemType = (string | number)[]
let arr2: ItemType = ['jack', 100]
let arr3: ItemType = [100, 'tom']易错点:数组联合类型必须加括号。(string | number)[] 表示“数组,成员是 string 或 number”;string | number[] 表示“string 或 number 数组”,含义不同。类型别名用 type 定义,适合简化和复用。
函数类型:约束参数与返回值
为什么需要:函数参数传错类型会导致内部逻辑错误;返回值类型也能帮助调用方正确接收。怎么想:函数类型本质是约束参数和返回值。参数注解后既限制类型,也默认必填;返回值注解限制返回类型。怎么做:
ts
function add(a: number, b: number): number {
return a + b
}
const add2 = (a: number, b: number): number => a + b
type AddFn = (a: number, b: number) => number
const add3: AddFn = (a, b) => a + b易错点:函数声明和函数表达式“分开写”较常用;整体注解常用 type 抽取,库文件中利于复用。记不住箭头写法时,可把参数和返回值注解整体挪过来,把冒号改成箭头推导。调用时少传参数会报“应有 2 个参数,但获得 0 个”;返回值类型不匹配也会报错。
认识 TypeScript:JS 加类型

为什么需要:先弄清 TS 是什么,才能理解后面为什么要写类型注解、为什么要搭建编译环境。
怎么想:转写稿给了一个很直观的公式:TS = JS + type,可以把它理解成“JS 的超集”。JS 本身能做的事,TS 同样能做,同时在此基础上增加了类型约束。
怎么做:记住定义——TypeScript 是具有类型语法的 JavaScript,是一门强类型的编程语言。这里的 type 不是指 JS 里已经学过的 number、string 等简单类型,而是指强类型语言中的“类型约束”。
易错点:不要把 TS 理解成完全脱离 JS 的新语言;它仍然以 JS 为基础,只是多了类型语法和约束。
强类型约束:变量类型确定后不能随意改
为什么需要:如果变量可以随便赋不同类型的值,代码运行到后面就容易出现难以发现的逻辑错误。
怎么想:对比 JS 的弱类型和 TS 的强类型。转写稿中的例子是:count 一开始是数字 100,随后又改成字符串 '100'。在 JS 里这完全允许;在 TS 里,一旦把 count 标注为 number,再赋字符串就会报错。
怎么做:先按 JS 写常规赋值,再改写成 TS,观察编辑器提示:
js
let count = 100
count = '100' // JS 允许:变量可以赋不同类型的值ts
let count: number = 100
count = '100' // TS 报错:不能将 string 类型赋给 number 类型调试时可以鼠标移到报错处,看到“类型不匹配”的提示。这个推导过程说明:TS 的“强类型”表现为变量一旦被标注类型,就不能随意修改成其他类型。
易错点:不要以为 TS 只是“多了几个类型名字”;核心是变量类型被确定后,赋值要受约束。
两个直接收益:静态检查和代码提示
为什么需要:错误越早发现越好;开发时少查接口文档、少拼错属性,也能提高效率。
怎么想:TS 的第一个好处是“静态类型检查,提前发现错误”。转写稿中的函数示例里,传入的是字符串,却调用了字符串身上没有的方法。JS 必须把代码跑起来,运行时才会看到 is not a function 之类的错误;TS 在写代码、传参时就提示参数类型不对。第二个好处是“良好的代码提示”。例如列表 list 里有很多对象字段,输入点语法时,TS 会自动联想当前对象下面的属性,不需要反复对照接口文档或截图。
怎么做:写函数时给参数和返回值补类型;遍历列表、访问对象属性时观察编辑器的自动联想,根据提示选择属性。
易错点:JS 的错误往往要运行时才暴露;TS 的提示发生在编码阶段。不要把“编辑器提示”误认为“代码已经运行”。
什么时候适合用 TypeScript
为什么需要:技术选型不能脱离具体业务和应用场景,TS 不是万能的。
怎么想:转写稿建议先问三个问题:当前是不是大型应用,模块多、体量大?是不是团队协作开发,需要多人配合?开发的是不是通用代码库,比如组件库之类?
怎么做:如果以上三个问题都是 yes,就可以有选择地加入 TS。总体上说,TS 更适合大型项目、通用 JS 代码库或团队协作的开发模式。
易错点:不是要求把之前所有 JS 代码都改成 TS;也不要在小项目里为了用 TS 而用 TS,选型要服务业务。
学习路径与本节小结
为什么需要:知道学习顺序,后面才不会东学一块、西学一块。
怎么想:转写稿把学习路径分成两部分:先学 TS 核心基础语法,配合综合案例打基础;再学 Vue3 + TS,加上实践案例,重点看组合式 API 如何配合 TS 使用。
怎么做:先掌握本节的概念——TS 是什么、强类型约束、静态检查与代码提示、适用场景;下一小节再搭建 TS 环境,进入具体语法学习。
易错点:不要跳过“什么是 TS”直接背语法;先理解 TS = JS + type 和强类型约束,后面的类型注解、数组、联合类型、函数类型才有统一逻辑。
先看一个对比:JS 的“自由”和 TS 的“约束”
TypeScript 不是一门完全陌生的语言,它和 JavaScript 的关系可以用一句话概括:TS = JS + 类型(type)。JS 本身能做的事,TS 同样能做;TS 在此基础上增加了类型约束。转写稿里特别强调,这里的“类型”不是指 JS 里已经有的 number、string、对象这些值类型,而是指强类型语言中的“类型约束”:变量一旦被标注类型,就不能随意改成其他类型。
先看 JS 的常规赋值:
js
let count = 100
count = '100' // JS 允许,变量可以改成字符串同样的代码放到 TS 里,给 count 加上类型注解后:
ts
let count: number = 100
count = '100' // TS 报错:不能将类型“string”分配给类型“number”

这就是 TS 类型约束最直观的表现。理解了这个对比,才能理解它带来的两个好处:静态类型检查和代码提示。
好处一:静态类型检查,提前发现代码错误
为什么需要?
JS 是弱类型语言,变量可以随便赋不同类型的值。写的时候看似自由,但很多错误要等到代码真正运行起来才会暴露。转写稿里举了一个函数调用的例子:调用函数时传过去的是字符串,而字符串身上并没有某个方法,运行时就会得到 “is not a function” 之类的错误。也就是说,在 JS 里,你必须把代码跑起来,才能发现这个错误。
怎么想?
错误发现得越早,修复成本越低。写代码时发现,改一行就行;上线后才发现,可能要排查日志、复现问题、重新发版。TS 的静态类型检查,就是在代码还没运行之前,先根据类型信息检查一遍:参数类型对不对、变量赋值类型对不对、调用的方法在这个类型上存不存在。
怎么做?
给变量、函数参数、返回值加上类型注解。例如求和函数:
ts
function add(a: number, b: number): number {
return a + b
}
add('1', 2) // TS 报错:参数类型不匹配参数 a、b 都标注为 number,调用时传字符串,编辑器会直接提示类型不对。这样不用等运行,就能提前发现问题。
易错点:
静态类型检查不是万能的,它主要检查“类型层面”的错误,不能代替业务逻辑测试。另外,不要以为加了类型注解,浏览器就能直接运行 TS 代码;TS 仍然需要编译成 JS 后才能运行。类型检查发生在开发阶段,运行阶段执行的还是 JS。
好处二:良好的代码提示,提升开发效率
为什么需要?
转写稿里举了列表遍历的场景:list 里有很多对象,对象里有很多字段。通过点语法渲染字段时,如果只靠接口文档或者后端返回数据的截图,一边看一边写,既容易拼错,也很浪费时间。字段名记错、大小写写错,都是常见问题。
怎么想?
编辑器之所以能提示,是因为它知道当前变量是什么类型、这个类型上有哪些属性和方法。类型信息越准确,提示越可靠。TS 的价值不只是“报错”,还包括“告诉你有什么可用”。
怎么做?
当你写下对象变量加一个点时,编辑器会自动联想当前对象下面所有的属性,你根据当前需要选择即可。数组遍历时,成员的类型如果定义清楚,成员身上可用的属性和方法也会被提示出来。这样不用手动拼写,减少查文档和反复确认的时间。

易错点:
代码提示依赖类型定义是否准确。如果到处写 any,或者对象类型定义得过于宽泛,提示就会变弱甚至消失。所以不要为了“先跑通”随便用 any,否则 TS 的两个好处都会被削弱。
把两个好处放回真实开发:什么时候值得用
转写稿里给了一个很务实的技术选型建议:TS 不是万能的,技术选型不能脱离具体业务和应用场景。可以问自己几个问题:现在做的是不是大型应用?是不是团队协作开发?是不是在做通用代码库或组件库?如果答案多为“是”,就可以有选择地加入 TS。它更适合大型项目、通用 JS 代码库和多人协作的开发模式。
易错点:
不要因为 TS 有好处,就把所有旧 JS 项目一次性重写。也不要脱离业务场景盲目上 TS。类型带来的收益,需要和项目体量、团队协作成本一起衡量。
小结
TS 带来的两个最明显好处是:第一,静态类型检查,把错误提前到写代码阶段;第二,良好的代码提示,减少查文档、拼写和记忆成本。理解这两个好处,后续再学类型注解、数组类型、联合类型、函数类型时,就不会只把它们当成“语法负担”,而会知道它们是在为检查和提示提供信息。
课件图示








代码示例
代码 1:编译环境验证 + 类型注解基础(hello.ts / main.ts)
ts
// ① 手动编译环境验证:hello.ts
// 终端执行:npm i typescript -g → tsc hello.ts → node hello.js
let count: number = 100
console.log(count) // 100
// ② 在 vite 工程的 src/main.ts 中编写(需 export {} 消除"模块"报错)
export {}
// 字符串类型注解
let message: string = 'this is message'
message = 'this is message' // ✅ 允许
// message = 100 // ❌ 不能将类型"number"分配给类型"string"
// 数字类型注解
let num: number = 100
// num = 'this is message' // ❌ 类型不匹配
// 布尔类型注解
let loading: boolean = true
loading = false // ✅
// loading = 100 // ❌ 类型不匹配
console.log(message, num, loading)关键行与易错点
let count: number = 100:冒号 + 全小写类型名就是类型注解语法,一旦标注,变量就只能赋该类型的值。- 浏览器 / Node 都不能直接运行 TS,必须先由
typescript包(核心是tsc命令)编译成 JS,运行的是编译后的 JS。 - 全局装包必须加
-g(Mac 需sudo npm i typescript -g),装完才有tsc命令,用tsc -v验证。 - 在 vite 的
src/main.ts里写代码会提示"需要一个默认导出",补一行export {}即可,否则误以为是语法错误。 - 类型注解写全小写:
string/number/boolean,不要写成String、Number。
代码 2:数组类型注解 + 联合类型 + 类型别名
ts
export {}
// ① 数组注解语法一:类型 + 中括号(推荐)
let arr1: number[] = [1, 2, 3]
// arr1 = [1, 2, '3'] // ❌ 成员必须是 number
// ② 数组注解语法二:泛型写法
let arr2: Array<number> = [1, 2, 3]
// arr2 = [1, 2, true] // ❌ 成员必须是 number
// ③ 联合类型:数组里既可以放 string 也可以放 number
let arr3: (string | number)[] = ['jack', 100]
// arr3 = [true] // ❌ 不在 string | number 范围内
// ④ 易错点:不加小括号,含义完全变了
let arr4: string | number[] = 'jack' // string 或 number 数组
arr4 = [1, 2, 3] // ✅
// ⑤ 类型别名:简化 + 复用
type ItemType = (string | number)[]
let arr5: ItemType = ['jack', 100]
let arr6: ItemType = ['tom', 200]
// arr6 = [false] // ❌ 不满足 ItemType
// ⑥ 思考题:既能是 number,也能是"成员全是字符串的数组"
let arr7: number | string[] = 100
arr7 = ['a', 'b'] // ✅
// arr7 = [100] // ❌ 数组里必须是字符串
console.log(arr1, arr2, arr3, arr4, arr5, arr6, arr7)关键行与易错点
number[]与Array<number>作用相同,实际开发推荐number[]。- 数组注解不只是"限制是数组",还限制每个成员的类型——这样才能在遍历时正确提示成员的属性和方法。
let arr: string | number[]与let arr: (string | number)[]是两种完全不同的意思,前者是"字符串或数字数组",后者才是"字符串和数字混搭的数组",联合类型做数组成员时必须加小括号。type定义的是类型别名,只用于类型层面,编译后不产生任何 JS 代码。- 别名命名建议首字母大写(
ItemType),便于和变量区分。
代码 3:函数类型注解(函数声明 + 函数表达式两种写法)
ts
export {}
// ① 函数声明:参数 + 返回值分开注解
function add(a: number, b: number): number {
return a + b
}
// add() // ❌ 应有两个参数,但获得 0 个(参数注解后即必填)
// add(1, '2') // ❌ 第二个参数必须是 number
console.log(add(1, 2)) // 3
// ② 函数表达式——分开注解
const add2 = (a: number, b: number): number => {
return a + b
}
// ③ 函数表达式——整体注解(借助类型别名)
type AddFn = (a: number, b: number) => number
const add3: AddFn = (a, b) => {
return a + b
}
console.log(add2(3, 4), add3(5, 6))
// ④ 易错点:用变量接收返回值时,变量自身类型要与返回值匹配
let res: string = 'init'
// res = add(1, 2) // ❌ 不能将类型"number"分配给类型"string"
res = 'ok'
console.log(res)关键行与易错点
- 函数类型注解的本质是给参数和返回值加约束:
(a: number, b: number): number。 - 参数一旦注解,就同时变成必填项,少传、多传都会报错。
- 返回值注解写在参数小括号的后面,不是写在
return上。 - 整体注解用到的
type AddFn = (a, b) => number是箭头函数形式,注意这里用的是=>而不是冒号——记不住时可以把分开写法整体挪过来、把冒号改成箭头推导一下。 - 实际业务代码多用"分开写";"整体写"多用于库文件或需要大量复用同一函数签名时。
- 用变量接收函数返回值时,变量声明的类型必须与返回值类型一致,这是高频踩坑点。
常见错误
1. 直接 node hello.ts 或把 .ts 丢进浏览器
- 现象:报
SyntaxError: Unexpected token ':'或Unknown file extension ".ts"。 - 原因:TS 只是开发期写法,JS 引擎(浏览器 / Node)不认识类型语法,必须先编译成 JS。
- 修法:
tsc hello.ts生成hello.js后再node hello.js;工程化项目则交给 Vite 自动编译,不要在 .ts 上手动跑 node。
2. tsc -v 提示 command not found / 不是内部或外部命令
- 现象:装完包敲 tsc 依然找不到命令。
- 原因:包没真正全局装上(漏了
-g),或安装过程报错被忽略;Mac/Linux 全局目录无写权限导致静默失败。 - 修法:
npm i typescript -g后必须用tsc -v回验版本号;Mac/Linux 权限问题加sudo,或改用npx tsc。
3. let msg: string = 'hi' 之后再 msg = 100 报错,误以为 TS 有 bug
- 现象:不能将类型 number 分配给类型 string。
- 原因:类型注解一旦确定就是"终身制",注解的语义是"只能被赋这种值",不是"这一行是这种值"。
- 修法:确实要多类型就用联合类型
string | number;否则另起变量名,别复用同一个注解变量做类型漂移。
4. 数组想同时放字符串和数字,写成 string | number[] 结果报错
- 现象:赋值
['a', 1]报错,只有纯数字数组能过。 - 原因:
|的优先级低于[],这句被解析成"字符串 或 数字数组",而不是"字符串或数字组成的数组"。 - 修法:用括号把联合类型括成整体:
(string | number)[]。
5. 函数表达式的整体注解把 => 写成 :
- 现象:
type Fn = (a: number, b: number): number直接语法报错。 - 原因:类型层面的函数签名只认箭头,冒号是函数实现里标注返回值用的,两者不能混。
- 修法:写成
(a: number, b: number) => number;记不住就把分开写法的: number挪到参数括号后面改成箭头。
6. 变量接收函数返回值时类型对不上
- 现象:
const res: string = add(1, 2)报"不能将 number 分配给 string"。 - 原因:函数返回值已注解为 number,接收方变量的注解也必须兼容。
- 修法:把接收变量注解改成 number,或干脆
const res = add(1,2)让 TS 自动推断。
练习
练习 1(数组 + 联合 + 别名) 声明类型别名 Item,表示"由字符串和数字组成的数组";再用它注解两个变量 list1、list2,分别赋值为 ['a', 1] 和 [2, 'b'],并尝试给 list1 追加一个布尔值观察报错。
练习 2(函数注解两种写法) 用函数声明写 multiply(a, b) 返回两数乘积,参数与返回值都注解为 number;再用函数表达式 + 类型别名的方式写一份功能相同的 multiply2。
练习 3(找错改错)
ts
let arr: string | number[] = ['tom', 18]
function greet(name: string) {
return 'hi ' + name
}
const s: number = greet('tom')指出两处类型错误并给出最小修改方案。
练习 4(环境排错) 同学 A 在 Mac 上执行 npm i typescript(无 -g),随后 tsc hello.ts 报找不到命令;同学 B 装好了包,但 node hello.ts 报扩展名错误。分别说明原因和修复命令。
练习 5(综合) 写一个函数 joinNames,参数为"字符串数组",返回值为字符串(用逗号拼接)。要求参数和返回值都有类型注解,并让调用方用一个 string 变量接收结果。
参考答案
type Item = (string | number)[];let list1: Item = ['a', 1];追加true报错,因为成员只能是 string 或 number。function multiply(a: number, b: number): number { return a * b };type Fn = (a: number, b: number) => number; const multiply2: Fn = (a, b) => a * b。- ① 应改为
(string | number)[];② 应改为const s: string = greet('tom')。 - A:漏了
-g,改sudo npm i typescript -g,再tsc -v验证;B:TS 不能直接跑,先tsc hello.ts再node hello.js。 function joinNames(arr: string[]): string { return arr.join(',') };const r: string = joinNames(['a','b'])。
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | TS核心-认识TypeScript | 06:22 |
| 2 | TS核心-编译环境搭建 | 11:01 |
| 3 | TS核心-类型注解的概念 | 06:13 |
| 4 | TS核心-注解数组 | 07:27 |
| 5 | TS核心-联合类型和别名类型 | 08:22 |
| 6 | TS核心-函数类型-概念和基本使用 | 11:34 |
小结
- TS = JS + 类型,编译产物仍是 JS,运行时没有任何类型开销,收益全部发生在写代码和编译阶段。
- 类型注解是"约束 + 提示"的双重契约:既拦住错误赋值,也驱动编辑器的自动补全。
- 报错越早越便宜——TS 把运行时才炸的问题提前到编辑器里,这是它最核心的工程价值。
- 技术选型看场景:大型项目、团队协作、通用库值得上 TS,小脚本和一次性页面不必强上。
- 编译环境分两种心智模型:手动
tsc用于理解原理,工程化(Vite 等)自动编译才是日常姿势。 - 数组、联合类型、类型别名三者组合时,括号和优先级是高频翻车点,写复杂类型先想"谁先结合"。
- 函数注解盯三件事:参数必填、参数类型、返回值类型;函数表达式的类型签名用箭头而非冒号。
- 类型别名(
type)解决的是"复杂类型写一遍、到处复用",也是后续接口、泛型等高级类型的复用基础。