主题
TS 初体验与TS 语法(Day11 1/3)
本页对应 React + TypeScript + 医通项目,共 6 段视频,约 59 分钟。
学习目标
- 能够为函数参数和返回值添加类型注解,并使用声明式、字面量完整写法和字面量简写形式定义函数。
- 能够定义并使用可选参数、默认参数和剩余参数,并说明可选参数与默认参数不能同时出现在同一参数上的原因。
- 能够声明类属性类型、编写构造函数和方法类型,并通过
extends实现类继承及调用super。 - 能够使用
public、protected、private修饰类成员,并准确判断其在类内部、子类和类外部的可访问性。 - 能够使用接口限定对象结构,定义可选属性、只读属性和方法,并通过
implements让类实现一个或多个接口。 - 能够定义接口继承关系,并使实现该接口的类补齐所有继承来的属性和方法;能够配置 Webpack 打包 TS 文件并验证运行结果。
知识点
先理解“TS 初体验”要解决什么
为什么需要: TypeScript 不能像 JavaScript 那样直接被 Node.js 执行。我们写的 .ts 文件里有类型注解,比如 let str: string、function add(a: number, b: number): number,这些内容需要先由 TypeScript 编译器 tsc 转成 .js,再由 Node.js 运行。
怎么想: 初体验的流程可以固定成四步:初始化配置 → 写一个 .ts 文件 → 用 tsc 编译 → 用 node 运行编译后的 .js。开发时再加 tsc -w 监听变化。
怎么做: 先创建 tsconfig.json,再写 hello.ts,然后编译、运行。
易错点: 不要把 node hello.ts 当成运行 TypeScript 的常规方式;本节流程里 Node.js 执行的是 hello.js。
初始化配置文件:让 tsc 有统一规则
为什么需要: 直接运行 tsc 时,编译器需要知道按什么规则编译、输出到哪里、是否开启严格检查等。tsconfig.json 就是 TypeScript 项目的配置文件。
怎么想: 先让项目有一个“配置中心”,后续命令和编辑器提示都围绕它工作。
怎么做: 在项目目录下执行:
sh
tsc --init执行后会生成 tsconfig.json。本节笔记只要求完成初始化,没有展开具体配置项,所以先保留默认生成结果即可。
易错点: tsc --init 要在项目根目录执行,否则配置文件可能生成到别处;后续编译和编辑器提示可能对不上。转写稿后面提到过“项目启动配置”和“编辑器识别配置”的差异,调试时要注意命令行与编辑器使用的是不是同一份配置。
创建 hello.ts:写一个能体现类型检查的文件
为什么需要: 只看配置文件没有体感,需要一个最小文件同时包含变量类型、函数参数类型和返回值类型。
怎么想: 代码不要复杂,能看出“写了类型”和“编译后仍是 JS”即可。
怎么做: 创建 hello.ts,保留本节笔记代码:
javascript
let str: string = 'hello world';
console.log(str);
function add(a: number, b:number):number{
return a + b;
}
console.log(add(1, 100));这里 str 被限定为 string,add 的两个参数和返回值都被限定为 number。
易错点: 类型注解是给 TypeScript 检查用的,不是运行时逻辑;不要因为 .ts 里写了 : string 就以为 Node.js 能直接识别它。转写稿中多次出现“类型推断”“严格模式”等说法,说明类型检查会受配置影响;初体验阶段先保证代码能编译通过。
用 tsc hello.ts 编译:把 TS 变成 JS
为什么需要: Node.js 执行的是 JavaScript,所以要先编译。
怎么想: 编译命令针对单个文件时,直接指定 .ts 后缀;生成结果通常是同目录下的 .js 文件。
怎么做: 在命令行运行,注意后缀是 ts:
sh
tsc hello.ts执行后得到 hello.js。
易错点: 命令里写的是 hello.ts,不是 hello.js;如果修改了 hello.ts 却忘记重新编译,后面运行的还是旧 hello.js。另外,如果命令行报类型错误,应先按提示修正,再运行生成的 JS,避免把旧结果当成新结果。
用 node hello.js 运行:真正执行的是 JS
为什么需要: 编译只是生成 JS,真正输出结果要靠 Node.js。
怎么想: 编译和运行分开:tsc 负责转换,node 负责执行。
怎么做: 运行,注意后缀是 js:
shell
node hello.js正常情况下会输出:
text
hello world
101易错点: 不要写成 node hello.ts 来替代本节流程;本节笔记明确区分了 tsc hello.ts 和 node hello.js。如果输出不对,先检查是不是运行的旧 JS 文件。
用 tsc -w 开启自动编译
为什么需要: 开发时频繁改 .ts,如果每次都手动执行 tsc hello.ts 很麻烦。
怎么想: 让编译器监听文件变化,保存后自动生成 JS。
怎么做: 使用笔记中的命令:
sh
tsc -w开启后修改 hello.ts,编译器会自动重新编译;此时再运行 node hello.js 查看结果。
易错点: -w 是监听模式,不会自动帮你执行 Node.js;它只负责编译。运行结果仍要用 node hello.js 查看。转写稿里出现过“改配置后要重新启动项目,错误才消失”的调试过程,初体验时也要注意:改完配置或开启监听后,确认编译输出确实更新了。
小结:初体验的固定闭环
本节笔记的核心不是函数、类或接口,而是建立一条最小闭环:tsc --init 建配置 → 写 hello.ts → tsc hello.ts 编译 → node hello.js 运行 → 开发时用 tsc -w 自动编译。把这条链路走通,后面再学类型推断、严格模式、函数参数、类与接口时,才知道代码是怎样从 TS 变成可执行 JS 的。
函数:先声明参数和返回值
写 TS 函数时,第一反应不是“会不会推断”,而是“参数和返回值要不要约束”。因为函数是调用最频繁的代码单元,参数传错、返回值用错,后期最难查。转写稿里反复说的“死锥”就是 string,“安迪范”就是 undefined。
声明式写法最直观:参数后面写类型,括号后写返回值类型。返回值必须和声明一致,否则立刻报错。
ts
function add(a: number, b: number): number {
return a + b;
}字面量写法分完整写法和简写。完整写法要先声明变量类型,再赋一个函数值,写起来长;简写利用类型推断,给变量赋函数值时,TS 自动推出参数和返回值类型。
ts
let f1: (a: number, b: number) => number = function (a, b) {
return a + b;
};
const f2 = (a: number, b: number): number => a + b;易错点:完整写法中“变量类型”和“赋值的函数类型”必须一致;简写不是没类型,而是靠类型推断。转写稿说完整写法“几乎不会用”,实际项目更常用声明式和简写。
可选参数、默认参数与剩余参数
可选参数解决“这个参数可传可不传”的需求。怎么做?在参数名后加 ?。但要注意:加了问号只解决调用时参数数量报错,函数体里它仍可能是 undefined,直接当 number 用可能报错。
ts
function f3(a: number, b?: number): number {
return b ? a + b : a + 1;
}转写稿里的调试过程很典型:先定义两个参数,调用时只传一个,TS 报“应有 2 个参数,但获得 1 个”;给 b 加 ? 后调用不报错,但函数体里若直接把 b 当 number 用,仍可能报 undefined 不能赋给 number。这跟 strict 严格模式有关;非严格模式下 undefined、null 可作为任何类型的子类型赋值,但项目启动配置改了要重启才生效。
默认参数解决“不传就用默认值”。写法是 b: number = 0。易错点:可选参数和默认参数不能同时写在一个参数上,转写稿明确说“参数不能包含问号和初始值”,因为二者都能表达“可不传”,同时存在会冲突。
剩余参数用 ...args: number[] 接收剩余实参。求和例子中先累加前两个参数,再用 reduce 累加剩余数组;args 是数组,不是单个数字。易错点:剩余参数必须放最后,类型要写成数组类型。
类的基本写法与继承
TS 类接近 ES6 类,差别主要是属性和方法要加类型,并且类里有哪些属性要先声明。为什么?因为 TS 要知道 this.name、this.age 是否存在、是什么类型。
ts
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
say(): void {
console.log(this.name, this.age);
}
}继承用 extends。子类构造器里要先 super(name, age) 调父类构造器,再给自己的属性赋值。易错点:子类没调 super 就使用 this 会报错;父类私有属性子类不能访问。
成员修饰符:public、protected、private
修饰符解决“谁能访问这个成员”。public 类内部、子类、类外部都能用;protected 类内部和子类能用,外部不能用;private 只有类自己能用,子类也不行。转写稿演示子类里访问父类 private secret 立刻报错,注释掉才恢复。
ts
class Father {
public name: string;
protected money: number;
private secret: string;
constructor(name: string, money: number, secret: string) {
this.name = name;
this.money = money;
this.secret = secret;
}
show(): void {
console.log(this.name, this.money, this.secret);
}
}易错点:protected 不是“谁都能保护”,而是“子类可用、外部不可用”;private 连子类都不能用。
接口:可选属性与只读属性
接口用来严格限定对象结构:对象多了属性不行,少了也不行。定义用 interface,名字通常首字母大写,转写稿习惯以 I 开头,便于识别。属性之间可用分号、逗号或换行分隔。
ts
interface IPerson {
name: string;
age: number;
sex?: string;
readonly id: string;
}sex? 是可选属性,解决同一接口下不同对象属性不完全一样的问题。readonly id 是只读属性,初始化后不能改;转写稿用身份证号类比,改了 ID 就“不再是你”。易错点:可选属性不是“随便写”,写了仍要符合类型;只读属性一旦赋值再修改会报“只读属性不可修改”。
接口限定方法、限定类与继承
接口不仅能限定属性,也能限定方法。方法有两种写法:cook(): void 和 cook: () => void。实现接口的类用 implements,并且必须把接口要求的属性和方法都实现出来,方法要有具体函数体。
ts
interface IBoyfriend {
name: string;
sex: string;
cook(): void;
}
class Boy implements IBoyfriend {
name: string;
sex: string;
constructor(name: string, sex: string) {
this.name = name;
this.sex = sex;
}
cook(): void {
console.log("会做饭");
}
}一个类可以实现多个接口,用逗号隔开;一个接口也可以 extends 多个接口,子接口就拥有父接口的全部限定。易错点:类实现接口后少一个属性或方法都会报错;接口继承后,用类实现该接口时,父接口的要求也都要补齐。转写稿的 IGoddess extends IHuman, IGirlfriend 就是这个推导:女神接口继承了人的要求和女朋友的要求,实现它的类必须全部满足。
本节目标与目录准备
为什么需要:项目里已经有 .ts 文件,但浏览器不能直接识别 TypeScript,需要先编译成 JavaScript。Webpack 可以统一处理入口、输出、开发服务和 HTML 模板,ts-loader 则把 TS 编译接入 Webpack 流程。
怎么想:先准备最小目录:源码放 src,HTML 模板放 public。入口文件是 src/index.ts,最终由 Webpack 打包到 build 目录。
怎么做:创建如下结构:
- src
- index.ts
- public
- index.html易错点:入口路径、模板路径要和配置文件一致;public/index.html 不存在时,HtmlWebpackPlugin 会报模板找不到。
安装依赖与初始化
为什么需要:Webpack 核心负责打包,webpack-cli 提供命令行,webpack-dev-server 提供开发服务,ts-loader 负责编译 TS,html-webpack-plugin 生成并注入 HTML,typescript 是 ts-loader 的编译基础。
怎么想:这些都是开发期工具,统一用 -D 安装到 devDependencies。先 npm init 生成 package.json,再安装依赖。
怎么做:
sh
npm init
npm i webpack webpack-cli webpack-dev-server ts-loader html-webpack-plugin typescript -D易错点:不要漏装 typescript,否则 ts-loader 无法正常编译;命令要在项目根目录执行,确保生成 package.json 和 node_modules。
配置 webpack.config.js
为什么需要:Webpack 默认不认识 .ts,也不知道入口、输出目录、HTML 模板和开发服务端口,因此要用配置文件把这些规则写清楚。
怎么想:入口用 entry,输出用 output;.ts/.tsx 交给 ts-loader;resolve.extensions 让导入时可省略扩展名;HtmlWebpackPlugin 负责以 public/index.html 为模板生成页面;devServer 设置端口和自动打开。
怎么做:在 src 同级目录创建 webpack.config.js:
js
const {resolve} = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './src/index.ts',
output: {
path: resolve(__dirname, 'build'),
filename: 'js/bundle.js'
},
mode: 'development',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader'
}
]
},
resolve: {
extensions: ['.ts','.tsx','.js']
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html'
})
],
devServer: {
port: 3000,
open: true
}
}易错点:output.path 要用绝对路径,所以这里用 resolve(__dirname, 'build');test: /\.tsx?$/ 同时匹配 .ts 和 .tsx;resolve.extensions 不写 .ts 时,导入 TS 模块可能解析失败;mode 建议明确写,避免默认模式带来的提示差异。
配置 tsconfig.json
为什么需要:ts-loader 编译 TS 时会读取 tsconfig.json。没有它,TS 的编译目标、模块规范、严格模式等就没有项目级约定。
怎么想:用 tsc --init 生成基础配置,再按项目需要调整。转写稿里提到过一个调试现象:改了 tsconfig.json 的严格模式后,编辑器已经识别,但正在运行的开发服务仍可能按旧配置工作,需要重启服务。这正说明 ts-loader 与 tsconfig.json 是联动的。
怎么做:项目根目录执行:
tsc --init易错点:tsconfig.json 要放在项目根目录或 Webpack 能识别的范围内;如果开启严格模式,undefined、null 赋给 number 等类型会直接报错,先检查 strict 相关配置;修改配置后若开发服务行为未变,重启 webpack-dev-server 再验证。
启动开发服务与打包
为什么需要:开发时希望改代码后自动编译、自动刷新;上线前则需要产出静态文件。
怎么想:开发阶段用 webpack-dev-server,它把打包结果放在内存中并提供服务;需要落盘产物时用 webpack。
怎么做:
npx webpack-dev-server # 启动开发服务
npx webpack # 打包代码易错点:webpack-dev-server 默认不会在项目里生成 build 实体文件,真正落盘要执行 npx webpack;端口被占用时改 devServer.port;如果 TS 类型检查报错,先看终端第一条错误,再回到对应 .ts 文件和 tsconfig.json 排查,不要只盯着浏览器页面。
课堂画面






代码示例
示例一:TS 初体验 + 函数(hello.ts)
ts
// 运行:tsc hello.ts && node hello.js
let str: string = 'hello world';
console.log(str);
// 1. 声明式定义:参数类型 + 返回值类型
function add(a: number, b: number): number {
return a + b;
}
console.log(add(1, 100));
// 2. 字面量完整写法:先声明变量的函数类型,再赋值
let f1: (a: number, b: number) => number = (a: number, b: number): number => {
return a + b;
};
// 3. 字面量简写:靠类型推断
const f2 = (a: number, b: number): number => {
return a + b;
};
console.log(f1(1, 2), f2(3, 4));
// 4. 可选参数:b 可传可不传(问号)
function f3(a: number, b?: number): number {
return a + 1;
}
console.log(f3(1));
// 5. 默认参数
function f4(a: number, b: number = 10): number {
return a + b;
}
console.log(f4(1), f4(1, 2));
// 6. 剩余参数:3、4、5、6、7 全部进入 args
function sum(a: number, b: number, ...args: number[]): number {
let total: number = 0;
total += a;
total += b;
total = args.reduce((pre, cur) => pre + cur, total);
return total;
}
console.log(sum(1, 2, 3, 4, 5, 6, 7));关键行与易错点
tsc hello.ts编译产出hello.js,node只认.js,所以运行node hello.js;开发时可用tsc -w自动编译。- 字面量完整写法里“冒号后面的
(a: number, b: number) => number”是变量的类型,等号后面的箭头函数是值,两处类型必须一致,否则立刻报错;简写形式f2之所以有类型,靠的是类型推断(赋值给它的函数长什么样,它就被推断成什么样)。 - 可选参数
b?: number的类型实际是number | undefined,所以函数体里直接return a + b会报“不能把undefined赋给number”——示例中用return a + 1绕开;要么就写if (b) {...}判空。 - 可选参数必须放在参数列表末尾;且问号和默认值不能同时写(
b?: number = 10会报Parameter cannot have question mark and initializer)。 - 剩余参数
...args: number[]是数组,reduce的初始值传total才能把前面已累加的a、b一起带上。
示例二:类、继承与成员修饰符(class.ts)
ts
// 运行:tsc class.ts && node class.js
class Father {
public name: string; // 公开:类内部、子类、外部都能用
protected money: number; // 受保护:类内部 + 子类可用
private secret: string; // 私有:只有自己类内部可用
constructor(name: string, money: number, secret: string) {
this.name = name;
this.money = money;
this.secret = secret;
}
say(): void {
// 类内部:三种属性都能访问
console.log(this.name, this.money, this.secret);
}
}
class Son extends Father {
constructor(name: string, money: number, secret: string) {
super(name, money, secret); // 必须先调父类构造器
}
hello(): void {
console.log(this.name); // ✅ public,子类可用
console.log(this.money); // ✅ protected,子类可用
// console.log(this.secret); // ❌ private,子类不可访问
}
}
const s1 = new Son('小S', 20, '暗语');
s1.hello();
console.log(s1.name); // ✅ 类外部可用
// console.log(s1.money); // ❌ protected,外部不可用
// console.log(s1.secret); // ❌ private,外部不可用
s1.say();关键行与易错点
- 与 ES6 唯一的大差别:属性必须先声明类型(
name: string;等),构造函数里再this.xxx = xxx赋值;ES6 里可以直接在 constructor 里赋值。 - 子类构造函数里
super(...)必须在访问this之前调用,否则报错。 - 三个修饰符的记忆口诀:
public全通;protected自己 + 子类,外部不行;private只有自己类内部,子类和外部都不行——示例中被注释掉的几行就是会报错的写法。 - 构造函数参数同样可以加可选(
?)或默认值,但二者不能同时使用,因为“可传可不传”和“不传给默认值”语义冲突。 - 方法没有返回值时写
: void,别漏。
示例三:接口(可选、只读、限定方法、限定类、接口继承)(interface.ts)
ts
// 运行:tsc interface.ts && node interface.js
// 1. 接口限定对象结构
interface IPerson {
name: string;
age: number;
sex?: string; // 可选属性
readonly id: string; // 只读属性
}
const p1: IPerson = { name: '艾硅谷', age: 10, id: 'xxx-1' };
const p2: IPerson = { name: '张三', age: 20, sex: 'boy', id: 'xxx-2' };
p1.age = 11; // ✅ 普通属性可改
// p1.id = 'xxx-9'; // ❌ 只读属性不可修改
console.log(p1.name, p1.age, p2.sex);
// 2. 接口限定属性 + 方法(两种方法写法)
interface IBoyFriend {
name: string;
sex: string;
car: string;
cook: () => void; // 写法一:函数类型
speak(): void; // 写法二:方法签名
}
const b1: IBoyFriend = {
name: '张三',
sex: '男',
car: '共享单车',
cook() {
console.log('会做西红柿炒鸡蛋');
},
speak() {
console.log('我不是哑巴');
}
};
b1.cook();
b1.speak();
// 3. 接口限定类(implements)、一个类实现多个接口、接口继承
interface IHuman {
name: string;
sex: string;
age: number;
}
interface IGirlfriend {
money: number;
cook(): void;
}
interface IGoddess extends IHuman, IGirlfriend {} // 接口继承多个接口
class Person implements IHuman, IGirlfriend {
name: string;
sex: string;
age: number;
money: number;
constructor(name: string, sex: string, age: number, money: number) {
this.name = name;
this.sex = sex;
this.age = age;
this.money = money;
}
cook(): void {
console.log(`${this.name} 会做饭`);
}
}
const g = new Person('女神', '女', 18, 100);
console.log(g.name, g.age, g.money);
g.cook();
// 用继承后的接口再限定一次(等价于要求 IHuman + IGirlfriend 的全部成员)
const g2: IGoddess = { name: '小美', sex: '女', age: 20, money: 999, cook() {} };
console.log(g2.name);关键行与易错点
- 接口名首字母大写是规范,习惯加
I前缀(IPerson),便于和类名、组件名区分。 - 属性之间用分号、逗号或直接换行都可以,但同一个项目里最好统一。
- 用接口限定对象时多了不行、少了也不行:
sex加问号才能不写;id是readonly,赋值即报错。 cook: () => void与speak(): void等价,都是限定方法,注意前者是属性写法(冒号),后者是方法写法(括号)。- 接口只声明结构,没有实现;类用
implements时必须把接口里所有属性和方法全部实现,方法体要自己写,否则报错。 - 一个类可以
implements A, B(逗号分隔多个接口),那么所有接口的成员都要具备;接口之间用extends A, B继承,继承后的接口就拥有了父接口的全部限定。 - 编译时若想放宽
undefined/null的检查(如笔记里“非严格模式下undefined、null是任何类型的子类型”),可把tsconfig.json的strict关掉;但初学阶段建议开着,错误提示更早暴露。
常见错误
tsc hello.ts后执行node hello.ts报SyntaxError: Unexpected token- 现象:编译通过,但 node 直接跑 ts 文件报错。
- 原因:tsc 只做「编译」产出 js,node 不认识 ts 语法与类型标注。
- 修法:跑
node hello.js;想一步到位就装ts-node或走 webpack-dev-server。
改了 ts 源码,页面/node 输出还是老结果
- 现象:逻辑改了没生效,怀疑类型写错。
- 原因:tsc 是编译产物,你改的是 ts,跑的是上一次的 js。
- 修法:
tsc -w开监听,或使用 webpack-dev-server 热更新;养成「先编译再运行」的顺序。
单文件编译时 tsconfig.json 不生效
- 现象:编辑器里没问题,命令行一跑就报
null/undefined 不能赋给 number。 - 原因:
tsc 文件名.ts是单文件模式,不读取 tsconfig.json,严格模式等配置没参与。 - 修法:项目内用
tsc(不带文件名)走配置编译,或先把 tsconfig 里相关开关调好再统一编译。
- 现象:编辑器里没问题,命令行一跑就报
webpack 打包报
Module parse failed/ 找不到入口- 现象:
npx webpack直接失败,提示无法解析.ts。 - 原因:
module.rules里没配ts-loader,或resolve.extensions漏了.ts,或根目录没生成tsconfig.json。 - 修法:补齐 loader 规则与 extensions,先
tsc --init再打包。
- 现象:
可选参数写
b?: number后又给默认值b = 0,编译报错- 现象:问号和初始值同时出现直接标红。
- 原因:两者语义冲突——有默认值就不必再标可选。
- 修法:二选一,需要「不传就用默认」只留默认值即可。
类里声明了属性却没赋值,报「属性没有初始化」
- 现象:
class Person { name: string }直接报错。 - 原因:严格初始化检查要求每个声明的属性在构造函数中被赋值。
- 修法:在 constructor 里
this.name = name;或在声明处给初始值;或确有外部赋值需求时用断言!。
- 现象:
子类访问父类
private属性编译失败- 现象:
this.secret在子类中报「私有属性只能在本类访问」。 - 原因:private 作用域仅限本类内部,子类和外部都不行。
- 修法:需要子类可用就改
protected;确实私密就提供 public 方法间接访问。
- 现象:
类
implements接口后,属性少了报错、多了也报错- 现象:少写属性提示「缺少属性」,多写属性提示「对象字面量只能指定已知属性」。
- 原因:接口是严格结构约束,多退少补都不允许。
- 修法:确实可能不存在的属性在接口里加
?;不该被改的加readonly;接口里声明的方法在类中必须写出完整实现。
练习
两种函数定义对照:分别用声明式、字面量简写定义同一个
add(a: number, b: number): number,把鼠标悬停在变量上看类型是否一致;然后故意把返回值改成a + 'b',观察报错位置,再改回。- 验收:能说出「简写形式没写类型却不报错」依赖的是什么机制。
参数三件套:写一个
sum(a: number, b?: number, ...rest: number[]): number的求和函数。分别调用sum(1)、sum(1, 2)、sum(1, 2, 3, 4, 5),并打印rest的实际内容。- 验收:三种调用都不报错,且能解释
b未传时函数内b的真实值。
- 验收:三种调用都不报错,且能解释
修饰符实验:定义
Father(public name / protected money / private secret)和继承它的Son。在 Son 内部依次访问三个属性,在类外部再依次访问,把每处报错记录下来;然后只把secret改成protected,看哪些报错消失。- 验收:能画出一张「public / protected / private × 类内部 / 子类 / 类外部」的可用性表格。
接口综合:定义
IHuman(name、age)与IGirlfriend(money、cook()),再用接口继承合成一个IGoddess,最后写一个类implements IGoddess。- 验收:故意先少实现
cook()看报错文案,补齐后编译通过;再给某个属性加readonly并尝试修改,确认报错。
- 验收:故意先少实现
工程化跑通:按笔记搭好 webpack + ts-loader + HtmlWebpackPlugin,启动 devServer,改动
index.ts里的console.log内容,确认浏览器自动刷新且输出更新。- 验收:
npx webpack与npx webpack-dev-server两条命令都能用,build/js/bundle.js正常产出。
- 验收:
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | ts-函数 | 17:01 |
| 2 | ts-class-01 | 07:42 |
| 3 | ts-class-类的修饰符 | 09:14 |
| 4 | ts-接口-可选-只读属性 | 09:46 |
| 5 | ts-接口-限定方法 | 03:54 |
| 6 | ts-接口限定类 | 11:49 |
小结
- tsc 是编译工具不是运行工具,
.ts → .js这层转换必须显式发生,命令行与 node 之间永远隔着一次编译。 tsc 文件.ts和tsc是两种模式,前者不读 tsconfig,配置类问题优先怀疑这一点。- 函数的三种参数修饰(可选
?、默认值、剩余...)互有约束,可选与默认值不能并存。 - 类型推断是省事的关键:字面量简写之所以不用手写类型,是因为赋的值已经把类型交代清楚了。
- 类的写法与 ES6 几乎一致,唯一新增的硬性动作是「先声明属性类型,再在构造函数中赋值」。
- 三个修饰符的差别只在作用域:public 全开、protected 到子类为止、private 止于本类。
- 接口是「结构契约」:可选
?放宽、readonly收紧,多写少写都会被拒。 implements是类对接口的履约,extends是接口对接口的继承;一个类可履约多个接口,一个接口可继承多个接口,但最终所有约束都要在类里落地实现。