TypeScript笔记

1 TypeScript 安装

全局安装 TypeScript(使用的 node 版本是 16.13.0

npm install -g typescript@5.1.6

安装完 TypeScript 之后会有一个全局命令 tsc,使用以下命令查看是否安装成功

tsc -v

使用以下例子命令可以将TypeScript 的 ts 文件所在的文件目录下编译新的 js 文件,如果此时语法有错误,就会在控制台输出,但默认配置下依旧会编译出新的 js 文件:

tsc test.ts

2 原始数据类型

原始数据类型:

  • boolean
  • null
  • undefined
  • number
  • bigInt
  • string
  • symbol
  • any
let isDone:boolean = false;
let age:number = 10;
let name1:string = '1123';
let u:undefined = undefined;
let n:null = null;

// union types, 联合类型
let num:number | string = '123';
num = 123;

let notSure:any = 4;
notSure = "mybe a string"; // 不报错
notSure.name; // 在 any 类型上访问任何属性都不报错
notSure.getName(); // 不报错

3 Array 和 Tuple

//最简单的方法是使用「类型 + 方括号」来表示数组:
let arrOfNumbers: number[] = [1, 2, 3, 4];
//数组的项中不允许出现其他的类型:
//数组的一些方法的参数也会根据数组在定义时约定的类型进行限制(不能传入联合类型):
arrOfNumbers.push(3);
arrOfNumbers.push('abc');

// 元祖的表示和数组非常类似,只不过它将类型写在了里面 这就对每一项起到了限定的作用
let user: [string, number] = ['viking', 20];
//但是当我们写少一项 就会报错 同样写多一项也会有问题
user = ['molly', 20, true];

4 接口 Interface

Duck Typing 概念:如果某个东西长得像鸭子,像鸭子一样游泳,像鸭子一样嘎嘎叫,那它就可以被看成是一只鸭子。

// 我们定义了一个接口 Person
interface Person {
  name: string;
  age: number;
}
// 接着定义了一个变量 viking,它的类型是 Person。这样,我们就约束了 viking 的形状必须和接口 Person 一致。
let viking: Person ={
  name: 'viking',
  age: 20
}

//有时我们希望不要完全匹配一个形状,那么可以用可选属性:
interface Person {
    name: string;
    age?: number;
}
let viking: Person = {
    name: 'Viking'
};

//接下来还有只读属性,有时候我们希望对象中的一些字段只能在创建的时候被赋值,那么可以用 readonly 定义只读属性

interface Person {
  readonly id: number;
  name: string;
  age?: number;
}
viking.id = 9527;

5 函数

// 来到我们的第一个例子,约定输入,约定输出
function add(x: number, y: number): number {
  return x + y
}
// 可选参数
function add(x: number, y: number, z?: number): number {
  if (typeof z === 'number') {
    return x + y + z
  } else {
    return x + y
  }
}

// 函数本身的类型
const add2: (x: number, y: number, z?:number) => number = add;

// interface 描述函数类型
const sum = (x: number, y: number) => {
  return x + y
}
interface ISum {
  (x: number, y: number): number
}
const sum2: ISum = sum;

6 类型推论、联合类型和类型断言

联合类型:

// 自动类型推论,无需声明类型,自动推论出数据类型,并绑定
let str = "123";

// 联合类型,我们只需要用中竖线来分割两个
let numberOrString: number | string ;
// 当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法:
numberOrString.length;
numberOrString.toString();
  • 类型断言 as
// 这里我们可以用 as 关键字,告诉typescript 编译器,你没法判断我的代码,但是我本人很清楚,这里我就把它看作是一个 string,你可以给他用 string 的方法。
function getLength(input: string | number): number {
  const str = input as string
  if (str.length) {
    return str.length
  } else {
    const number = input as number
    return number.toString().length
  }
}
  • 类型守卫 typeof
// typescript 在不同的条件分支里面,智能的缩小了范围,这样我们代码出错的几率就大大的降低了。
function getLength2(input: string | number): number {
  if (typeof input === 'string') {
    return input.length
  } else {
    return input.toString().length
  }
}

7 Class 类

面向对象编程的三大特点

  • 封装(Encapsulation):将对数据的操作细节隐藏起来,只暴露对外的接口。外界调用端不需要(也不可能)知道细节,就能通过对外提供的接口来访问该对象,
  • 继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性。
  • 多态(Polymorphism):由继承而产生了相关的不同的类,对同一个方法可以有不同的响应。

类成员的访问修饰符:

  • public 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public 的
  • private 修饰的属性或方法是私有的,不能在声明它的类的外部访问
  • protected 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的
class Animal {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  run() {
    return `${this.name} is running`;
  }
}
const snake = new Animal('lily');

// 继承的特性
class Dog extends Animal {
  bark() {
    return `${this.name} is barking`;
  }
}

const xiaobao = new Dog('xiaobao');
console.log(xiaobao.run());
console.log(xiaobao.bark());

// 这里我们重写构造函数,注意在子类的构造函数中,必须使用 super 调用父类的方法,要不就会报错。
class Cat extends Animal {
  constructor(name) {
    super(name);
    console.log(this.name);
  }
  run() {
    return 'Meow, ' + super.run();
  }
}
const maomao = new Cat('maomao');
console.log(maomao.run());

7.1 类与接口

类实现一个接口:

interface Radio {
  switchRadio(trigger: boolean): void;
}
class Car implements Radio {
  switchRadio(trigger) {
  }
}
class Cellphone implements Radio {
  switchRadio() {
  }
}

interface Battery {
  checkBatteryStatus(): void;
}

// 要实现多个接口,我们只需要中间用 逗号 隔开即可。
class Cellphone implements Radio, Battery {
  switchRadio() {
  }
  checkBatteryStatus() {
  }
}

8 枚举

// 数字枚举,一个数字枚举可以用 enum 这个关键词来定义,我们定义一系列的方向,然后这里面的值,枚举成员会被赋值为从 0 开始递增的数字,
enum Direction {
  Up,
  Down,
  Left,
  Right,
}
console.log(Direction.Up);

// 还有一个神奇的点是这个枚举还做了反向映射
console.log(Direction[0]);

// 字符串枚举
enum Direction {
  Up = 'UP',
  Down = 'DOWN',
  Left = 'LEFT',
  Right = 'RIGHT',
}
const value = 'UP';
if (value === Direction.Up) {
  console.log('go up!');
}

9 泛型

泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。

function echo(arg) {
  return arg;
}
const result = echo(123);
// 这时候我们发现了一个问题,我们传入了数字,但是返回了 any

function echo<T>(arg: T): T {
  return arg;
}
const result = echo(123);

// 泛型也可以传入多个值
function swap<T, U>(tuple: [T, U]): [U, T] {
  return [tuple[1], tuple[0]];
}

const result = swap(['string', 123]);

9.1 泛型约束

在函数内部使用泛型变量的时候,由于事先不知道它是哪种类型,所以不能随意的操作它的属性或方法。

function echoWithArr<T>(arg: T): T {
  console.log(arg.length);
  return arg;
}

// 上例中,泛型 T 不一定包含属性 length,我们可以给他传入任意类型,当然有些不包括 length 属性,那样就会报错
interface IWithLength {
  length: number;
}
function echoWithLength<T extends IWithLength>(arg: T): T {
  console.log(arg.length);
  return arg;
}

echoWithLength('str');
const result3 = echoWithLength({length: 10});
const result4 = echoWithLength([1, 2, 3]);

9.2 泛型与类和接口

class Queue<T> {
  private data: T[] = [];
  push(item: T) {
    return this.data.push(item);
  }
  pop() {
    return this.data.shift();
  }
}
const queue = new Queue<number>();

queue.push(1);

const poped = queue.pop();

if (poped) {
  poped.toFixed();
}

//在上述代码中存在一个问题,它允许你向队列中添加任何类型的数据,当然,当数据被弹出队列时,也可以是任意类型。在上面的示例中,看起来人们可以向队列中添加string 类型的数据,但是那么在使用的过程中,就会出现我们无法捕捉到的错误,
class Queue<T> {
  private data = [];
  push(item: T) {
    return this.data.push(item);
  }
  pop(): T {
    return this.data.shift();
  }
}
const queue = new Queue<number>();

//泛型和 interface
interface KeyPair<T, U> {
  key: T;
  value: U;
}

let kp1: KeyPair<number, string> = { key: 1, value: "str"};
let kp2: KeyPair<string, number> = { key: "str", value: 123};

10 类型别名,字面量和交叉类型

类型别名,就是给类型起一个别名,让它可以更方便的被重用。

let sum: (x: number, y: number) => number;
const result = sum(1,2);
type PlusType = (x: number, y: number) => number;
let sum2: PlusType;

// 支持联合类型
type StrOrNumber = string | number;
let result2: StrOrNumber = '123';
result2 = 123;

// 字符串字面量
// 字面量类型,当一个变量的类型定义成一个数字或者字符串,那么该变量的数值就只能声明为该数字或字符串
type Directions = 'Up' | 'Down' | 'Left' | 'Right';
let toWhere: Directions = 'Up'; // 该变量只能声明为 'up' | 'right' | 'down' | 'left',此时代码编辑器也会有代码自动补全提醒


11 声明文件

类型声明文件以 .d.ts 作为文件名后缀:

// 在 typescript 使用由 javascript 编写的第三方库,它本身没有声明类型,此时就需要引入该库的 类型声明 配置文件了 
// 类型声明文件可以帮助编译器识别该库某个类、方法、变量是怎么一个类型,从而在使用第三方库的时候也能享受到 typescript 的便利,自动补全,类型提示等等(interface, class, function 等)
// 声明文件的后缀是 d.ts
// 声明文件的用处就是提供类型声明,没有任何实际实现代码
// 如果该第三方库没有提供类型声明配置文件,就需要自行编写类型声明配置文件了

// 使用 declare 声明函数类型
declare function IAxios(url: string):string

interface IAxios2 {
    get: (url:string) => string;
    post: (url:string, data:any) => string;
}

// 使用 declare 声明变量类型
declare const IAxios3: IAxios2

以下是测试代码:

// 代码编辑器同时打开多个 ts 文件,此时所有文件的变量都会处于在同一个作用域
// 使用声明文件声明过的函数
IAxios("url")
// 使用声明文件声明过的变量
IAxios3.get("url")

// npm install axios, 导入
import axios from 'axios'
axios.get('url')

import {caculator, ICaculator, caculator2} from './caculator'

let test:ICaculator = (operator:string, numbers:number[]):number => {
    return 12;
};
test('minus', [1,2]);
// 使用在声明类型文件中已经声明了的变量
caculator('minus', [1,2]);

caculator2('minus', [1, 2]);
caculator2.minus([1, 2]);

12 内置类型

const a: Array<number> = [1,2,3]
// 大家可以看到这个类型,不同的文件中有多处定义,但是它们都是 内部定义的一部分,然后根据不同的版本或者功能合并在了一起,一个interface 或者 类多次定义会合并在一起。这些文件一般都是以 lib 开头,以 d.ts 结尾,告诉大家,我是一个内置对象类型欧
const date: Date = new Date()
const reg = /abc/
// 我们还可以使用一些 build in object,内置对象,比如 Math 与其他全局对象不同的是,Math 不是一个构造器。Math 的所有属性与方法都是静态的。

Math.pow(2,2)

// DOM 和 BOM 标准对象
// document 对象,返回的是一个 HTMLElement
let body: HTMLElement = document.body
// document 上面的query 方法,返回的是一个 nodeList 类型
let allLis = document.querySelectorAll('li')

//当然添加事件也是很重要的一部分,document 上面有 addEventListener 方法,注意这个回调函数,因为类型推断,这里面的 e 事件对象也自动获得了类型,这里是个 mouseEvent 类型,因为点击是一个鼠标事件,现在我们可以方便的使用 e 上面的方法和属性。
document.addEventListener('click', (e) => {
  e.preventDefault()
})

13 Utility Types

Typescript 还提供了一些功能性,帮助性的类型,这些类型,大家在 js 的世界是看不到的,这些类型叫做 utility types,提供一些简洁明快而且非常方便的功能。

// partial,它可以把传入的类型都变成可选
interface IPerson {
  name: string
  age: number
}

let viking: IPerson = { name: 'viking', age: 20 }
type IPartial = Partial<IPerson>
let viking2: IPartial = { }

// Omit,它返回的类型可以忽略传入类型的某个属性

type IOmit = Omit<IPerson, 'name'>
let viking3: IOmit = { age: 20 }

14 编译配置文件

配置文件的官方文档 编译配置文件 tsconfig.json

// 编译配置文件,在所在目录使用 tsc 命令生成 js 文件
{
    "files": ["function-types-more.ts"], // 编译特定文件文件路径(完整路径),d.ts 类型声明文件也要包含在内
    "include": [], // 与 files 类似,但可以输入 pattern 形式的文件路径
    "exclude": [], // 剔除某些文件编译
    "compilerOptions": {
        "outDir": "./output", // 编译生成的文件所在位置
        "module": "ESNext", // 输出 模块 的类型
        "target": "ES5", // 输出 javascript 的版本号
        "declaration": true, // 每个ts 文件都会生成 d.ts 类型声明文件
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值