使用场景: 大型项目或者多人协作
优点:提高开发效率,提高项目健壮性
TS vs JS
- 是JS的超集,遵循JS的语法和语义,最终编译为原生JS
- 跨平台且开源
- 可以重用JS,并且可以无缝使用JS的流行的库(如需要使用,则要tsconfig.json配置文件中开启allowJs: true)
- TS提供了类、接口和泛型的概念,更易于开发和维护
- 前端三大框架均支持TS语言环境
01.Typescript开发构建和调试
// 安装前置依赖包
// 安装ts
npm i typescript -g
// 查看typescript安装的版本, @3.8.3
tsc -v
// 一般解决第三方模块文件声明问题
npm i @types/node -D
// 创建tsconfig.json配置文件
tsc --init
// 编译对应的文件
tsc test.ts
以vscode编辑器为例,这里打开终端--运行任务,选择tsc watch -tsconfig.json,自动监视ts文件的改动并编译为js文件到指定输出目录(修改tsconfig.json, "outDir": "./jsFile"即可),对编译后的js文件使用nodemon xxx.js即可运行编译后文件
如需引入第三方组件,则需要安装第三方组件的TS依赖或者自己别写x.d.ts第三方声明,同时也可以通过@ts-ignore不对其进行TS检查
02.Typescript变量类型
TS类型共13种
- undefined
- null空类型
- string字符串类型
- number数值类型
- boolean布尔类型
- enum枚举类型
- array数组类型
- tuple元组类型
- void空类型
- any任意类型
- never
- object
- 类型断
字符串
// 1. 字符串
const str: string = 'wangcai';
const intro = `
你好,
我是${str}
`;
console.log(intro);
数值和布尔
//2. 数字
const num1: number = 16; // 十进制
const num2: number = 0x10; // 16进制
const num3: number = 0o20; // 8进制
const num4: number = 0b10000; // 2进制
console.log(num1, num2, num3, num4); // 16
// 3. 布尔
const bool: boolean = true;
数组
// 4. 数组(两种方式)
const arr1: number[] = [1, 2]; // 数字类型数组
// 范型数组定义形式
const arr2: Array<string> = ['4', '5']; // 字符串数组
console.log(arr1, arr2);
元组
// 5. turple元组,已知类型、顺序和数量确定的(可以定义多种元素类型,但是要按类型顺序赋值),局限性比较大,不推荐使用,使用场景极少
const tur1: [number, string, boolean] = [1, '2', false];
// 使用对应的元素,会自动推断出该元素类型的方法
console.log(tur1[2].valueOf());
枚举
// 6. 枚举
enum Sex {
male,
female,
}
const male: number = Sex.male;
const female:number = Sex.female;
console.log(male, female) // 下标索引
// 手动赋值方式
enum Color {
red = 2,
green = 'green',
}
const red: number = Color.red;
const green: string = Color.green;
console.log(red, green); // 2 'green'
any
// 7. any类型, 不声明变量类型,默认是any,常用场景:比如我们只知道一部分数据类型时,可以使用,但是尽量少用
let any1: any;
any1 = 7;
any1 = false;
let any2: any[];
any2 = [1, false, 'xm'];
// 8. void类型(空类型),使用场景:一般用于函数的返回值声明
function void1(): void {
console.log('void类型');
}
void1();
null 和 undefined
// 9. null和undefined, 所有类型的子类型,可以赋值给任意类型(但是这里实践会报错),使用场景: 对变量是否有值不确定时
// 使用联合判断
const null1: object | null = null;
const undefined1: number | undefined = 12;
console.log(null1, undefined1); // null 12
Never
// 10. never:永远不会有值的类型,是任意类型的子类型,但是它没有自己的子类型。使用场景:抛出异常的函数,很少使用,一般使用void进行处理
function never1(msg: string): never {
throw new Error(msg);
}
// never1('never实践 ');
function never2(): never {
while(true) {
console.log('never2');
}
}
// never2();
object
// 11. object类型,非原始类型,除string、number、boolean、symbol之外的类型
const obj1: object = { age: 18, name: 'xm' };
const obj2: object = [1, 2];
console.log(obj1, obj2);
function obj3(o: object) {
console.log(o);
}
obj3({ a: 1 });
obj3([1, 2]);
类型断言
// 12. 类型断言,修改已知的变量类型为自定义的变量类型(没有运行时的影响,只会在编译阶段起作用)
// 方式1
const asset1: any = 'i like, like i';
const asset2: string = (<string>asset1).substring(0, 3);
const asset3: any = 456;
const asset4: string = (<number>asset3).toFixed(4);
console.log(asset2, asset3);
// 方式2
const asset5: any = false;
const asset6: boolean = (asset5 as boolean).valueOf();
console.log(asset6)
03.函数的参数
基本函数参数
// 基本函数参数
function fun2(params: string): void {
console.log(params);
}
fun2('基本函数参数');
可选参数的函数,必选参数不能放在可选参数后
// 可选参数的函数,必选参数不能放在可选参数后
function fun3(params1: boolean, params2?:number) { // 函数不显式指定返回类型时,默认为void类型
console.log(params1, params2);
}
fun3(false); // false, undefined
fun3(true, 13); // true, 13
默认参数的函数
// 默认参数的函数
function fun4(params:string[] = ['str']) {
console.log(params);
}
fun4(); // ['str']
fun4(['test']); // ['test']
剩余参数的函数
// 剩余参数的函数
function fun5(...args: any[]): string {
return args.reduce((prev, curr) => {
return prev += (curr + '、');
}, '');
}
const res: string = fun5('cs', 27, 175, '65kg', false);
console.log(res);
04.接口
作用:在开发中,为类命名和为项目中的代码或者第三方代码库定义规范
接口属性必填
// 1. 定义接口
interface Person {
name: string,
job: string,
age: number,
}
// 规范化变量
const person: Person = {
name: 'xm',
age: 18,
job: 'it',
}
// 规范化函数参数
function outPerson(person: Person) {
console.log(person.name);
}
outPerson(person);
接口中可选属性
// 2. 接口中可选属性
interface CircleParam {
color?: string,
radius?: number,
}
interface CircleRes {
color: string,
area: number,
}
function createCircle(circle:CircleParam): CircleRes {
const defaultCircle = { color: 'red', area: 100 };
if (circle.color) {
defaultCircle.color = circle.color;
}
if (circle.radius) {
defaultCircle.area = Math.PI * Math.pow(circle.radius, 2);
}
return defaultCircle;
}
const createCircle1 = createCircle({color: 'blue'});
const createCircle2 = createCircle({radius: 12});
const createCircle3 = createCircle({color: 'green', radius: 14});
console.log(createCircle1, createCircle2, createCircle3);
接口只读属性
// 3. 接口的只读属性
interface FullName {
readonly firstName: string,
readonly lastName: string,
}
const fullName: FullName = {
firstName: 'c',
lastName: 'xm',
}
fullName.lastName = 'ss'; // warning: read-only
console.log(fullName) // 虽然可以编译痛殴,但是ts已经报了警告
// 只读数组
const colors: ReadonlyArray<number> = [1, 2, 3];
colors.push(5); // 直接警告colors read-only
console.log(colors);
接口的任意定义属性
interface SquareConfig {
width: number,
height: number,
[propName: string]: any, // 自定义属性名称
}
function area(params: SquareConfig): void {
const areaVal = params.width * params.height;
console.log(areaVal, params);
}
area({ width: 20, height: 10, desc: '矩形', title: '矩形title' });
05.定义函数类型
// 最简洁方式
interface Func1 {
(param1: number, param2: number): boolean, // 定义接口函数
}
// 对箭头函数进行接口约束,适用于模块化开发
const func1Test: Func1 = (a, b) => {
console.log(a > b);
return a > b;
}
func1Test(4, 1);
06.索引类型
// 可索引类型
interface IndexType {
[index: number]: string, // 自定义字符串类型的索引数组,适用于模块化开发
}
const indexArr: IndexType = ['xm', '2'];
const indexStr: string = indexArr[0];
console.log(indexStr);
07.类类型
// 类类型
// 接口中声明属性和方法(只会检查类的公共属性和方法, 私有和受保护类型不会检查)
interface ClockInterface {
currDate: Date,
setDate(d: Date): void,
}
// 对类通过接口进行规范
class Clock implements ClockInterface {
currDate: Date; // 声明接口中的属性,方法不用重新声明
constructor(currDate: Date) {
this.currDate = currDate;
console.log(this.currDate);
}
setDate(d: Date) {
console.log(d);
}
}
const clock = new Clock(new Date());
clock.setDate(new Date());
08.接口继承
接口继承(一个接口的成员复制到另一个接口)
分割重用的思想
// 继承单接口
interface People {
firstName: string,
}
// 接口继承,并且继承firstName属性
interface Programmer extends People {
job: string,
}
// 使用断言的方式,定义属性规范
const programmer = {} as Programmer;
programmer.firstName = 'wang';
programmer.job = 'web';
console.log(programmer);
// 继承多接口
interface Male {
sex: string,
}
// 多接口继承,用','分开
interface Student extends Male, People {
class: string,
}
const student = {} as Student;
student.firstName = 'li';
student.sex = '男';
student.class = '三年级';
console.log(student);
09.类与继承
class Animal {
name: string;
constructor(props: { name: string }) {
this.name = props.name;
}
sayAnimal() {
console.log('sayAnimal' + this.name);
}
}
class Cat extends Animal {
kind: string;
constructor(props: { name: string, kind: string }) {
super(props);
this.kind = props.kind;
}
sayCat() {
console.log(`我是${this.name},属于${this.kind}`);
}
}
const cat = new Cat({ name: '小黑', kind: '猫' });
cat.sayAnimal();
cat.sayCat();
10.子类重写并在内部调用父类方法
内部通过super调用父类的方法
class Mammal {
name: string;
constructor(name: string) {
this.name = name;
}
move(distance: number = 0) {
console.log(`${this.name}跑了${distance}m`);
}
}
class Horse extends Mammal {
constructor(name: string) {
super(name);
}
move() { // 重写父类方法
console.log('开始调用父类的方法');
// 内部通过super调用父类的方法
super.move(200);
}
}
const horse = new Horse('小马');
horse.move();
11.类类型说明与比较
公共类型
公共类型作用域在当前类、当前类实例和其子类以及子类实例中
// 当前类
class People {
// 不声明默认就是public类型
name: string;
public age: number; // 公共类型
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
peopleSay() {
console.log(this.name, this.age);
}
}
// 子类
class Man extends People {
constructor() {
super('xm', 18);
}
manSay() {
// 公共属性可以访问到
console.log(this.name, this.age);
}
}
const people = new People('people', 24);
const man = new Man();
// 公共属性外层实例可以访问到
people.peopleSay();
console.log('people.name', people.name);
man.manSay();
console.log('man.name', man.name);
受保护类型
受保护类型作用域仅在当前类中和其子类中
// 受保护类型
class Car {
protected price: number;
constructor(price: number) {
this.price = price;
}
carSay() {
console.log('car say', this.price);
}
}
class Auto extends Car {
constructor() {
super(2000);
}
autoSay() {
console.log('auto say', this.price);
}
}
const car = new Car(30000);
car.carSay();
console.log(car.price);
const auto = new Auto();
auto.autoSay();
console.log(auto.price);
私有类型
私有类型作用域仅在当前类中
// 私有类型
class Job {
private place: string;
constructor(place: string) {
this.place = place;
}
jobSay() {
console.log('job place', this.place);
}
}
class Teacher extends Job {
constructor() {
super('学校');
}
teacherSay() {
console.log('teacher place', this.place); // 属性“place”为私有属性,只能在类“Job”中访问。
}
}
const teacher = new Teacher();
console.log(teacher.place); // 属性“place”为私有属性,只能在类“Job”中访问。// 私有类型
class Job {
private place: string;
constructor(place: string) {
this.place = place;
}
jobSay() {
console.log('job place', this.place);
}
}
class Teacher extends Job {
constructor() {
super('学校');
}
teacherSay() {
console.log('teacher place', this.place); // 属性“place”为私有属性,只能在类“Job”中访问。
}
}
const job = new Job('工厂');
job.jobSay();
console.log('jon 实例', job.place); // 属性“place”为私有属性,只能在类“Job”中访问。
const teacher = new Teacher();
console.log(teacher.place); // 属性“place”为私有属性,只能在类“Job”中访问。
类修饰符
readonly 只能在声明时或者构造函数中被初始化,作用域为当前类、当前类实例和其子类以及子类实例中都可访问,但不能修改
// readonly
class DecoratorClass {
readonly name: string = 'readonly';
updateProp() {
this.name = 'update readonly'; // Cannot assign to 'name' because it is a read-only
}
}
class SubDecoratorClass extends DecoratorClass {
constructor() {
super();
}
subUpdateProp() {
console.log('sub', this.name);
}
}
const decoratorClass = new DecoratorClass();
console.log(decoratorClass.name);
const subDecoratorClass = new SubDecoratorClass();
subDecoratorClass.subUpdateProp();
12.类的静态属性
静态属性只属于当前类的属性,不属于类实例的属性
// static
class StaticClass {
static fullName: string = 'xm';
age: number = 18;
desc() {
// 静态属性只能通过StaticClass去访问,不属于实例对象this的属性
console.log(StaticClass.fullName + '芳龄' + this.age);
}
}
const staticClass = new StaticClass();
staticClass.desc();
13.抽象类
- 抽象类是派生类的基类,直观理解:抽象类是父类,派生类是子类。抽象类中也可以定义抽象方法,只需定义签名方法就可以,具体实现可以放在派生类中实现
- 派生类只能继承抽象方法、公共方法与属性,不能继承基类的抽象属性
- 抽象类不能进行实例化,一般使用场景很少,只做了解
// 抽象类
abstract class Department {
go() {
console.log('开始go');
}
abstract say(): void; // 不能有具体实现,必须定义在抽象类中
}
class Hr extends Department {
say() { // 对抽象方法具体实现
console.log('我是派生类的具体实现');
}
}
const hr = new Hr();
hr.go();
hr.say();
14.泛型
基础使用
可灵活定义数据类型,用封装的思想将数据类型统一规范化
// 泛型基本使用,对输入输出类型统一化
// 基本格式
function print<T>(param: T): T {
return param;
}
console.log(print('xm'));
console.log(print(true));
console.log(print(10));
// 泛型基本使用,对输入输出类型统一
function print<T>(param: T[]): T[] {
console.log(param.length);
return param;
}
// 使用泛型定义数组,比元组更好用的方式,数组中元素可以是任意类型
console.log(print(['xm', false, 10, [1, 2]]));
强制规范泛型的属性
强制传入的参数中含有某个属性
// 例如传入参数中必须包含length属性
interface LengthProp {
length: number;
}
class Len<T extends LengthProp> {
name: T;
constructor(name: T) {
console.log(name.length);
this.name = name;
}
}
const len1 = new Len('100'); // 3
const len2 = new Len(100); // undefined, error: 数值100不含有length属性
const len3 = new Len({ a: 1, length: 999 }); // 999
// 传入对象中必须包含某个key, 使用keyof关键字进行判断,可作为进阶用法了解
class ObjKey<T, K extends keyof T> {
constructor(obj: T, key: K) {
console.log(obj[key]);
}
}
const objKey1 = new ObjKey({a: 1}, 'a');
const objKey2 = new ObjKey({a: 1}, 'c'); // error: c不是{a: 1}中的属性
15.类型推断(比较简单,不做详细记录)
类型推断默认使用最佳通用规则自动推断,如果我们对类型加上注解之后,就会以我们添加的类型注解进行推断
// 类型自动推断
let hobby = 'ball'; // string
hobby = 10; // 不能将类型“number”分配给类型“string”
const props = ['xm', 10, null, undefined, false]; // (string | number | boolean | null | undefined)[]
网友评论