TS速通笔记
编译 TypeScript
1.命令行编译
要把
.ts文件编译为.js文件,需要配置TypeScript的编译环境
- 创建一个 demo.ts
const person = {
name: '张三',
age: 18,
}
console.log(`姓名:${person.name},年龄:${person.age}`)
- 安装
typescript
npm i -g typescript
- 编译
tsc demo.tx
2.自动化编译
# 在文件夹下执行
tsc --init
# 监视文件
tsc --watch demo.tx # 或tsc -w
# 监视当前工作目录
tsc --watch
init 后会生成 tsconfig.json 文件,在这里可以设置编译 JavaScript 的版本、严格模式等,同时建议打开配置文件中的 noEmitOnError,避免后续出现错误的代码也被编译为 .js 文件。
tsconfig.json 配置部分说明
- target:转换目标语法,可以选择
es6等 - noEmitOnError:当出现错误时不要提交
- 一般使用脚手架 Webpack 和 Vite,他会有自己的方式
类型声明
TypeScript使⽤ : 来对变量或函数形参,进⾏类型声明:
let a: string // 变量a只能存储字符串类型,不能是包装类,TS官方推荐写法
let b: number // 变量b只能存储数值类型
let c: boolean // 变量c只能存储布尔值
a = 'hello'
b = -10
c = true
// 参数x必须是数字类型,参数y也必须是数字类型,函数返回值也必须是数字类型
// 参数只能是2个,不能是1个或者3个
function count(x: number, y: number): number {
return x + y
}
let result = count(10, 20)
console.log(result)
数据类型
类型总览
- JavaScript 中的数据类型
number
string
boolean
undefined
null
bigint
symbol
object
备注:其中 object 包含: Array、Function、Date、Error 等...
- TypeScript 中的数据类型
- JavaScript中的8个数据类型
- 6个新的数据类型
- 2个用于自定义类型的方式:① type ② interface
常用类型
- any:摆烂型数据,不建议用
- unknow:可以使用,不会污染别的数据
let a: unknown
a = 99
a = false
a = 'hello'
console.log(a)
let x: string
// 第一种
if (typeof a === 'string') {
x = a
}
// 第二种(断言)
x = a as string
// 第三种(断言)
x = <string>a
let str1: unknown
str1 = 'hello';
// 类型转换
(str1 as string).toUpperCase()
-
never: 不用管
-
void:通常用于返回值,
undefined -
object/Object:用的很少
声明对象类型
实际开发中通常使用以下
// 用 ,
let person1: { name: string, age?: number }
// 用 ;
let person2: { name: string; age?: number }
// 用 换行
let person3: {
name: string
age?: number // 加?代表可以为空
}
// 如下赋值均可以
person1 = {name:'李四',age:18}
person2 = {name:'张三'}
person3 = {name:'王五'}
索引签名
let person: {
name: string
age?: number
[key: string]: any
}
// 赋值合法
person = {
name:'张三',
age:18,
gender:'男'
}
声明函数类型
let count: (a: number, b: number) => number
count = function (x, y) { return x + y }
声明数组
let arr1: string[]
let arr2: Array<string>
arr1 = ['a','b','c']
arr2 = ['hello','world']
tuple
let arr1: [string,number]
let arr2: [number,boolean?]
let arr3: [number,...string[]]
// 可以赋值
arr1 = ['hello',123]
arr2 = [100,false]
arr2 = [200]
arr3 = [100,'hello','world']
arr3 = [100]
枚举
数字枚举
enum Direction {
Up,
Down,
Left,
Right
}
console.log(Direction);
// 反向映射
console.log(Direction.Up);
console.log(Direction[0]);
字符串枚举
enum Direction {
Up = "up",
Down = "down",
Left = "left",
Right = "right"
}
let dir: Direction = Direction.Up;
console.log(dir); // 输出: "up"
常量枚举
const enum|Directions {
Up,
Down,
Left,
Right
}
let x = Directions.Up;
编译之后生成的 JavaScript
"use strict";
let x = 0 /* Directions.Up */;
type
联合类型
type Status = number | string;
type Gender = '男' | '女';
// 限制
function printStatus(status: Status) {
console.log(status);
}
function logGender(str: Gender) {
console.log(str);
}
// 调用函数
printStatus(404);
printStatus('200');
printStatus('501');
logGender('男');
logGender('女');
交叉类型
//⾯积
type Area = {
height: number; //⾼
width: number; //宽
};
//地址
type Address = {
num: number; //楼号
cell: number; //单元号
room: string; //房间号
};
// 定义类型House,且House是Area和Address组成的交叉类型
type House = Area & Address;
// 赋值
const house: House = {
height: 180,
width: 75,
num: 6,
cell: 3,
room: '702'
};
定义函数与特殊情况
// 函数类型声明
type LogFunc = () => void;
// 定义函数
const f1: LogFunc = () => {
console.log("hello");
return 999; // 不报错
};
// 这里出现了特殊情况:我在声明了返回值为void,但是实际返回了 999,结果没有报错
官方解释,是为了如下代码成立:
const src = [1, 2, 3];
const dst = [0];
// foreach 的定义中:返回值是 void,但是这个案例中,通过 dst.push(e1) 是有返回值的
src.forEach((e1) => dst.push(e1));
再比如:
// 定义函数类型
type LogFunc = () => void;
const f1: LogFunc = () => {
return 999;
};
let x = f1();
console.log(x); // 虽然可以打印,但是没办法做后续操作
官⽅⽂档的说明:Assignability of Functions
类
关注如下几个点:
- 类定义
- 属性定义
- 构造器
- 类继承
- 新增属性
- 重写方法
// 类定义
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
say() {
console.log(`我是${this.name}, 我今年${this.age}岁`);
}
}
// 实例化
const p1 = new Person("张三", 18);
p1.say();
// 类继承
class Student extends Person {
// 新增属性
grade: string;
constructor(name: string, age: number, grade: string) {
// 调用父类的构造函数
super(name, age);
// 初始化新增属性
this.grade = grade;
}
// 重写方法
override say() {
console.log(`我是${this.name}, 我今年${this.age}岁, 我在读${this.grade}`);
}
// 新增方法
study() {
console.log(`${this.name}正在学习`);
}
}
const s1 = new Student("李四", 18, "三年级");
s1.say();
s1.study();
属性修饰符
| 修饰符 | 含义 | 具体规则 |
|---|---|---|
| public | 公开的 | 可以被:类内部、子类、类外部访问。 |
| protected | 受保护的 | 可以被: 类内部、子类访问。 |
| private | 私有的 | 可以被: 类内部访问。 |
| readonly | 只读属性 | 属性无法修改。 |
- public 可以用作类的简写
// 完整
class Person {
public name: string;
public age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
// 简写
class Person {
constructor(
public name: string,
public age: number
) {}
}
抽象类
抽象类示例
// 抽象类
abstract class Package {
// 构造器(简略写法)
constructor(public weight: number) {}
// 抽象方法
abstract calculate(): number;
// 具体方法
printPackage() {
return `包裹重量为: ${this.weight}kg, 运费为: ${this.calculate()}元`;
}
}
class StandardPackage extends Package {
// 构造器 注意:这里重量是继承的,简写方式可以省略 publicc,但是 unitPrice 是新增的属性,不能省略 public
constructor(weight: number, public unitPrice: number) {
// 调用父类的构造器
super(weight);
}
// 实现抽象方法
calculate(): number {
return this.weight * this.unitPrice;
}
}
const p1 = new StandardPackage(10, 100);
console.log(p1.printPackage());
控制台
包裹重量为: 10kg, 运费为: 1000元
总结:何时使用抽象类
- 定义通用接口 :为⼀组相关的类定义通⽤的⾏为(⽅法或属性)时。
- 提供基础实现:在抽象类中提供某些⽅法或为其提供基础实现,这样派⽣类就可以继承这 些实现。
- 确保关键实现:强制派⽣类实现⼀些关键⾏为。
- 共享代码和逻辑:当多个类需要共享部分代码时,抽象类可以避免代码重复。
interface
定义类结构
// 接口定义类
interface IPerson {
name: string;
age: number;
speak(n: number): void;
}
class Person implements IPerson {
constructor(public name: string, public age: number) {}
speak(n: number): void {
for (let i = 0; i < n; i++) {
console.log(`我是${this.name}, 我今年${this.age}岁`);
}
}
}
const p1 = new Person('张三', 18);
p1.speak(3);
控制台输出
我是张三, 我今年18岁
我是张三, 我今年18岁
我是张三, 我今年18岁
定义对象结构
// 接口定义对象
interface IUser {
name: string;
readonly gender: string;
age?: number;
run: (n: number) => void;
}
const user: IUser = {
name: "张三",
gender: "男",
age: 18,
run: (n: number) => {
console.log(`我是${user.name}, 我今年${user.age}岁, 奔跑了${n}米`);
}
}
user.run(10);
用接口限制函数
// 接口定义函数
interface ICount {
(a: number, b: number): number;
}
const count: ICount = (a, b) => {
return a + b;
}
接口可以继承
// 接口之间的继承
interface IPerson {
name: string;
age: number;
}
interface IStudent extends IPerson {
grade: string;
}
const stu: IStudent = {
name: "张三",
age: 18,
grade: "一年级",
}
接口自动合并(可重复定义)
// 接口自动合并
interface IPerson {
name: string;
age: number;
}
interface IPerson {
gender: string;
}
const p: IPerson = {
name: "张三",
age: 18,
gender: "男",
}
接口总结:何时使用接口?
- 定义对象的格式:描述数据模型、API 响应格式、配置对象…等等,是开发中⽤的最多的场景。
- 类的契约:规定⼀个类需要实现哪些属性和⽅法。
- 自动合并:⼀般⽤于扩展第三⽅库的类型, 这种特性在⼤型项⽬中可能会⽤到。
一些相似概念的区别
interface与type的区别
- 相同点:
interface和type都可以定义对象结构,两者在许多场景中可以互换 - 不同点:
interface:更专注于定义对象和类的结构,支持继承、合并type:可以定义类型别名、联合类型、交叉类型,但不支持继承和自动合并
代码示例两者的互换
定义对象结构
// 使用 interface 定义 Person 对象
interface IPerson {
name: string;
age: number;
speak(): void;
}
// 使用 type 定义 Person 对象
type TPerson = {
name: string;
age: number;
speak(): void;
}
继承与合并
// 使用 interface 定义 Person 对象
interface IPerson {
name: string;
age: number;
}
interface IPerson {
speak(): void;
}
// 使用 interface 定义 Student 类型,并通过继承 IPerson 接口
interface IStudent extends IPerson {
grade: string;
}
const stu: IStudent = {
name: "张三",
age: 18,
grade: "一年级",
speak() {
console.log("我是一个学生");
},
};
// 使用 type 定义 Person 对象
type TPerson = {
name: string;
age: number;
} & {
speak(): void;
};
// 使用 type 定义 Student 类型,并通过交叉类型继承 PersonType
type TStudent = TPerson & {
grade: string;
};
const stu2: TStudent = {
name: "李四",
age: 20,
grade: "二年级",
speak() {
console.log("我是一个学生");
},
};
interface与抽象类的区别
- 相同点:都能定义⼀个类的格式(定义类应遵循的契约)
- 不同点:
- 接⼝:只能描述结构,不能有任何实现代码,⼀个类可以实现多个接⼝。
- 抽象类:既可以包含抽象⽅法,也可以包含具体⽅法,⼀个类只能继承⼀个抽象类。
// 接口
interface IFly {
fly(): void;
}
interface ISwim {
swim(): void;
}
class Duck implements IFly, ISwim {
fly() {
console.log("我会飞");
}
swim() {
console.log("我会游泳");
}
}
泛型
泛型函数
// 泛型
function logData<T>(data: T): void {
console.log(data);
}
logData<string>("hello");
logData<number>(123);
泛型接口
interface IPerson<T> {
name: string;
age: number;
extraInfo: T;
}
type JobInfo = {
title: string;
company: string;
}
let p: IPerson<JobInfo> = {
name: "张三",
age: 18,
extraInfo: {
title: "前端开发",
company: "公司A",
},
};
类型声明文件
类型声明⽂件是 TypeScript 中的⼀种特殊⽂件,通常以 .d.ts 作为扩展名。它的主要作⽤是为现有的 JavaScript 代码提供类型信息,使得 TypeScript 能够在使用这些 JavaScript 库或模块时进行类型检查和提示。
ts引用js所导致的问题:
先定义一个 demo1.js
export function add(a, b) {
return a + b;
}
export function sub(a, b) {
return a - b;
}
再 ts 中引用
import { add, sub } from "./demo1.js";
此时控制台报错
无法找到模块“./demo1.js”的声明文件。“xxx/ts-demo/demo1.js”隐式拥有 "any" 类型。ts(7016)
类型声明解决
定义一个 demo1.d.ts,有 2 种写法
declare module "./demo1.js" {
export function add(a: number, b: number): number;
export function sub(a: number, b: number): number;
}
写法2:
declare function add(a: number, b: number): number;
declare function sub(a: number, b: number): number;
export { add, sub };
这些文件一般不用自己写,老代码的组织会封装好。核心能力就是为现有的 js 代码提供类型信息,方便 ts 引用。