TS学习与实践
文章目录
- 学习资料
- TypeScript 介绍
- TypeScript 是什么?
- TypeScript 增加了什么?
- TypeScript 开发环境搭建
- 基本类型
- 编译选项
- 类
- 声明
- 属性
- 属性修饰符
- getter 与 setter
- 方法
- static 静态方法
- 实例方法
- 构造函数
- 继承 与 super
- 抽象类
- 接口
- interface 定义接口
- implements 使用接口
- 多重继承
- 泛型
学习资料
【尚硅谷TypeScript教程(李立超老师TS新课)】
TypeScript演练场
TypeScript 介绍
TypeScript 是什么?
TypeScript 增加了什么?
TypeScript 开发环境搭建
- 下载Node.js https://nodejs.com.cn/
- 安装Node.js
- 使用
npm
全局安装typescript
- 进入命令行
- 输入:
npm i -g typescript
- 创建一个
ts
文件 - 使用
tsc
对ts
文件进行编译- 进入命令行
- 进入ts文件所在目录
- 执行命令:
tsc 文件名.ts
基本类型
-
类型声明
- 类型的声明是
TS
非常重要的一个特点 - 通过类型声明可以指定
TS
中的变量(参数、形参)的类型 - 指定类型后,当为变量赋值时,
TS编译器
会自动检查是否符合类型声明,符合则赋值,否则报错 - 简而言之,类型声明给变量设置了类型,使得变量只能存储某种类型的值
- 语法:
let 变量: 类型; let 变量: 类型 = 值; function fn(参数:类型, 参数:类型): 类型{ ... }
- 类型的声明是
-
自定类型判断
TS
拥有自动的类型判断机制- 当对变量的声明和赋值是
同时进行
的,TS编译器
会自动判断变量的类型 - 所以如果你的变量的声明和赋值是同时进行的,可以省略掉类型声明
-
类型
类型 例子 描述 number 1, -33, 2.5 任意数字 string ‘hi’, “hi” 任意字符串 boolean true、false 布尔值true 或 false 字面量 其本身 限制变量的值就是该字面量的值 any * 任意类型 unknown * 类型安全的any void 空(undefined) 没有值(或 undefined) never 没有值 不能是任何值 object {name:‘孙悟空’} 任意的JS对象 array [1,2,3] 任意JS数组 tuple [4,5] 元素,TS新增类型,固定长度数组 enum enum{A,B} 枚举,TS中新增类型 -
number
let decimal: number = 6; let hex: number = 0xf00d; let binary: number = 0b1010; let octal: number = 0o744; let big: bigint = 100n;
-
boolean
let isDone: boolean = false;
-
string
let color: string = "blue"; color = 'red'; let fullName: string = `Bob Bobington`; let age: number = 37; let sentence: string = `Hello, my name is ${fullName}. I ll be ${age +1} years old next month.`
-
字面量
也可以使用字面量去指定变量的类型,通过字面量可以确定变量的取值范围let color: 'red' | 'blue' | 'black'; let num: 1 | 2 | 3 | 4 | 5;
-
any
let d: any = 4; d = 'hello'; d == true;
-
unknown
let notSure: unknown = 4; notSure = 'hello';
-
void
let unsable: void = undefined;
-
never
function error(message: string): never { throw new Error(message); }
-
object(没啥用)
let obj: object = {};
-
array
let list: number[] = [1,2,3]; let list: Array<number> = [1,2,3];
-
tuple
let x: [string, number]; x = ["hello",10];
-
enum
enum Color { Red, Green, Blue } let c: Color = Color.Green; enum Color { Red = 1, Green = 2, Blue = 4 } let c: Color = Color.Green;
-
类型断言
- 有些情况下,变量的类型对于我们来说是很明确,但是TS编译器却不清楚,此时,可以通过类型断言来告诉编译器变量的类型,断言有两种形式:
- 第一种
let someValue: unknown = "this is a string"; let strLength: number = (someValue as string).length;
- 第二种
let someValue: unknown = "this is a string"; let strLength: number = (<string>someValue).length;
- 第一种
- 有些情况下,变量的类型对于我们来说是很明确,但是TS编译器却不清楚,此时,可以通过类型断言来告诉编译器变量的类型,断言有两种形式:
编译选项
- 自动编译文件
- 编译文件时,使用
-w
指令后,TS编译器会自动监视文件的变化,并在文件发生变化时对文件进行重新编译。 - 示例:
tsc xxx.ts -w
- 编译文件时,使用
- 自动编译整个项目
- 如果直接使用
tsc
指令,则可以自动将当前项目下的所有ts文件编译为js文件。 - 但是能直接使用
tsc
指令的前提是,要先在项目目录下创建一个ts的配置文件tsconfig.json
tsconfig.json
是一个JSON文件,添加配置文件后(添加配置文件命令,在VSCode终端中输入tsc -init
),只需要tsc
指令即可完成对整个项目的编译。- 配置选项:
- include
- 定义希望被编译文件所在的目录
- 默认值:
[**/*]
- 示例:
上述例子中,所有src目录和tests目录下的文件都会被编译"include":["src/**/*","tests/**/*"]
- exclude
- 定义需要排除在外的目录
- 默认值:
["node_modules","bower_components","jspm_packages"]
- 示例:
上述示例中,src和hello目录下的文件都不会被编译"exclude": ["./src/hello/**/*"]
- extends【了解即可】
- 定义被继承的配置文件
- 示例:
"extends": "./configs/base"
上述示例中,当前配置文件会自动包含config目录下base.json中所有配置信息
- files【了解即可】
- 指定被编译文件的列表,只需要编译的文件少时才会用到
- 示例:
列表中的文件都会被TS编译器所编译"files": [ "core.ts", "sys.ts", "types.ts" ]
- compilerOptions(重点,以下为compilerOptions的子选项)
- 编译器的选项
{ "compilerOptions": { "target": "ES6", "module": "system", "outDir": "./dist", // "outFile": "./dist/app.js", "allowJs": false, "checkJs": false, "removeComments": false, "noEmit": false, "noEmitOnError": true, "strict": true, "alwaysStrict": true, "noImplicitAny": false, "noImplicitThis": true, "strictNullChecks": true } }
- target
- 用来指定ts被编译为的es版本
- 可选项:ES3(默认)、ES5、ES6/ES2015、ES7/ES2016、ES2017、ES2018、ES2019、ES2020、ESNext
- module
指定要使用的模块化的规范'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015', 'es2020', 'es2022', 'esnext', 'node16', 'nodenext'.
- lib
用来指定所需要的库,一般不用动,有默认值可选项:‘es5’, ‘es6’, ‘es2015’, ‘es7’, ‘es2016’, ‘es2017’, ‘es2018’, ‘es2019’, ‘es2020’, ‘es2021’, ‘esnext’, ‘dom’, ‘dom.iterable’, ‘webworker’, ‘webworker.importscripts’, ‘webworker.iterable’, ‘scripthost’, ‘es2015.core’, ‘es2015.collection’, ‘es2015.generator’, ‘es2015.iterable’, ‘es2015.promise’, ‘es2015.proxy’, ‘es2015.reflect’, ‘es2015.symbol’…
- outDir
- 用来指定编译后文件所在的目录
"outDir": "./dist"
- outFile
将代码合并成一个文件,设置outFile
后,所有的全局作用域合并在一个文件中,module
中只有’amd’
和‘system’
支持outFile
。"outFile": "./dist/app.js"
- allowJs
是否对js文件进行编译,默认是false,如果项目中某些模块用js写,可能需要编译"allowJs": false
- checkJs
检查js是否符合语法规范,默认是false"checkJs": false
- removeComments
是否移除注释,默认是false"removeComments": true
- noEmit
不生成编译后的文件,默认是false"noEmit": true
- noEmitOnError
当有错误时不生成编译后的文件,默认是false"noEmitOnError": false
- strict
所有严格模式的总开关,默认false(配置后相当于以下配置全都开启)"strict": true
- alwaysStrict
用来设置编译后的文件是否适用于严格模式,默认是false"alwaysStrict": true
- noImplicitAny
当某个变量不指定类型时,使用any类型"noImplicitAny": true
- noImplicitThis
不允许类型不明的 this"noImplicitThis": true
- strictNullChecks
严格地检查空值,默认false"strictNullChecks": true
- 编译器的选项
- include
- 如果直接使用
类
声明
用关键字class
来声明一个类
class Person{
...
}
属性
在TS中类的属性一共有三种,在属性面前添加相应的修饰符便可
class Person{
name:string = "zhubajie" // 实例属性
static age = 89 // 类属性
readonly addr:string = "高老庄" // 只读属性
}
- 实例属性
直接定义的属性属于实例属性
他必须通过类实例化之后才能使用 - 类属性
以static
开头的属性为类属性
他可以通过类直接访问Person.age
- 只读属性
以readonly
定义的属性为只读属性,不可修改
readonly
也可以放再static
后面
属性修饰符
pubilc
公共属性,可以再任意位置访问和修改(实例属性,实例化之后访问)
private
私有属性,只能在类的内部进行访问和修改(一般声明的时候我们会以_开头)
protected
受保护的属性,只能在当前类和当前类的子类中进行访问
class Person{
public name = "SunWuKong"
private age = 30
protected sex = "male"
}
const person = new Person()
console.log(person.name)
console.log(person.age) // 错误 只能在Person这个类中进行访问
console.log(person.sex) // 错误 只能在Person这个类和其子类中访问
此外,还有readOnly
属性,以他修饰的属性只能读取不能修改
getter 与 setter
- 类中的每一个属性内置
getter
方法和setter
方法
getter
方法用于获取属性
setter
方法用于设置属性 - 这样,我们可以对属性读取和操作做一些拦截,设置如下
当我们进行读取的时候,其实是走的get这个逻辑class Person1{ private _name:string constructor(name:string){ this._name = name; } set name(value:string){ this._name = value } get name(){ return this._name } }
当我们对于属性进行赋值的时候,其实是走的set这个逻辑
方法
static 静态方法
经过static
关键字修饰的方法属于类方法,可以通过类直接使用
class BaJie{
name = "BaJie"
static age = 18
static sayName(){
console.log("八戒")
}
}
// 通过类直接访问
BaJie.sayName()
console.log(BaJie.age);
const bajie = new BaJie()
bajie.sayName() // 实例化之后不可访问
当有不规范的语法的时候,ts就不会进行编译,如上面的编译如下
var BaJie = /** @class */ (function () {
function BaJie() {
this.name = "BaJie";
}
BaJie.sayName = function () {
console.log("八戒");
};
BaJie.age = 18;
return BaJie;
}());
// 通过类直接访问
BaJie.sayName();
console.log(BaJie.age);
实例方法
在类中直接定义的方法为实例方法,没有任何关键字的修饰
这种方法只能在类实例化之后进行使用
class BaJie{
name = "BaJie"
age = 18
sayName(){
console.log("八戒")
}
}
// 通过类直接访问
BaJie.sayName() // 错误的访问方法
console.log(BaJie.age); // 错误的访问方法
// 同样,实例化之后也是可以访问的
const bajie = new BaJie()
bajie.sayName()
console.log(bajie.name);
构造函数
class Person{
name:string;
age:nubmer;
constructor(name:string,age:number){
this.name = name
this.age = age
}
}
const p1 = new Person('张三',18);
const p2 = new Person('李四',20);
console.log(p1);
console.log(p2);
constructor
被称为构造函数
构造函数会在对象创建时调用
继承 与 super
// 父类
class Animal{
name:string;
age: number;
constructor(name:string,age:number){
this.name = name;
this.age = age
}
sayName(){
console.log(this.name)
}
}
// 子类
class Dog extends Animal{
run(){
console.log(`${this.name}run`);
}
}
class Cat extends Animal{
sayName(){
console.log(`我是${this.name}`);
}
}
const dog = new Dog("旺财",19)
const cat = new Cat("汤姆",20)
dog.sayName()
dog.run()
cat.sayName()
可以将代码复制到 TypeScript演练场 运行
我们使用extends
关键字来进行继承
,其中被继承的Animal
为父类,Dog
为子类
继承之后,子类会拥有父类的一切属性和方法
子类也可以自己定义一些方法,如上面例子中的run
子类也可以写与父类相同的方法,这样执行方法的时候会执行子类的方法,叫做方法重写
(Cat
类中重写了sayName
方法)
抽象类
abstract class Animal{
name:string;
constructor(name:string){
this.name = name;
}
abstract sayName():void;
}
class Dog extends Animal{
age:number;
constructor(name:string,age:number){
super(name);
this.age = age;
}
sayName(): void {
console.log(`我是${this.name},我今年${this.age}岁了`);
}
}
const dog = new Dog("旺财",23)
dog.sayName() // 我是旺财,我今年23岁了
用abstract
关键字来定义抽象类和抽象方法
- 抽象类
抽象类不能用来创建对象
抽象类只能用于继承
(说明类中有哪些属性,哪些方法)
在抽象类中可以添加抽象方法(如sayName方法) - 抽象方法
抽象方法没有方法体
(如sayName方法),且只能定义在抽象类中
子类必须对于抽象类中的抽象方法进行复写
接口
接口用于描述一个类或者一个对象的结构,描述他们的属性和方法,所以接口可以当做一个类的声明
interface 定义接口
我们使用interface
来定义一个接口,定义的方法全部为抽象方法
,必须重写
接口用来定义一个类结构,用来定义一个类中应该包含哪些属性和方法
同时接口也可以当成类型声明去使用
interface myInterface{
name:string,
age:number,
sex:"male"|"female",
sayName():void
}
implements 使用接口
一般我们使用implements
关键字来使用接口
class Person implements myInterface{
name: string;
age: number;
sex: "male" | "female";
sayName(): void {
console.log(this.name);
}
}
多重继承
使用extends
来进行接口的继承,且后可以跟多个接口
,实现多重继承
interface Obj {
[propName:string]:any
}
interface myInterface{
name:string,
age:number
}
interface IPerson extends myInterface,Obj{
sex:"male"|"female",
sayName():void
}
class Person implements IPerson{
name: string;
age: number;
sex: "male" | "female";
hobby:"read"|"write"
sayName(): void {
console.log(this.name);
}
}
泛型
在指定函数或者类的时候,如果遇到类型不明确的话,就可以使用泛型
(不先指定类型,使用的时候进行类型的转换)
一般在声明的变量后面加一个尖括号来声明泛型
当我们进行类型转换后,编辑器就会有相应的提示
当然我们也可以不指定泛型,ts会进行自动的类型转换
- 指定多个泛型
function fn1<T,K>(a:T,b:K):K{ console.log(a); return b } fn1<number,string>(1,'hello')
- 定义类
class Person3<T>{ name:T constructor(name:T){ this.name = name } } const person4 = new Person3<string>("Jack") console.log(person.name);
- 泛型与接口
泛型也可以继承接口,使用extends
关键字,他表示泛型必须满足接口的条件interface IPerson1{ name:string, age:number } function fn2<T extends NameInterface>(a:T):string{ return a.name } fn2<IPerson1>({name:"小敏",age:10})