Skip to content

基础

01. 介绍

特点:

  1. 编译型语言:编译为 js 后运行,单独无法运行;
  2. 强类型语言;
  3. 面向对象的语言;

优势:

  1. 类型系统实际上是最好的文档,大部分的函数看看类型的定义就可以知道如何使用;
  2. 可以在编译阶段就发现大部分错误,这总比在运行时候出错好;
  3. 增强了编辑器和 IDE 的功能,包括代码补全、接口提示、跳转到定义、重构等;

总结:TypeScript 增加了代码的可读性和可维护性。

1.1 安装

需要有 node 环境,通过npm或者yarn安装

bash
npm install -g typescript

1.2 编码

使用 .ts 文件扩展名, 使用 typescript 编写使用 React 时,使用 .tsx 扩展名 。

使用 : 指定变量的类型,: 的前后有没有空格都可以;

typescript
let str: string = 'Hello TypeScript!'

function add(num1: number, num2: number) {
  return num1 + num2
}

console.log(str)
console.log(add(1, 2))

1.3 编译

使用 tsc 命令可编译 .ts 文件, 生成一个同名 .js 文件;编译的时候即使报错了,还是 会生成编译结果(.js),可通过 tsconfig.json文件配置

bash
tsc index.ts

1.4 配置 tsconfig.json

json
{
  "compilerOptions": {
    "target": "ES6", // 用来指定 ES 版本
    "module": "ESNext", // 指定要使用模块化的规范
    "outDir": "./js", // // 用来指定编译后文件的存放位置
    "strict": true // 开启严格模式
  }
}

2. 基础类型

2.1 Number 类型

typescript
//Numbers类型声明
let n1: number = 100
let n2: number = 0b100
let n3: number = 0o100
let n4: number = 0x100
let n5: number = 100.11

console.log(n1, n2, n3, n4, n5) // Output: 100 4 64 256 100.11

2.2 Boolean 类型

typescript
// Boolean类型声明
let b1: boolean = true
let b2: boolean = 20 > 5
let b3: boolean = !!100

console.log(b1, b2, b3) //Output: true true true

2.3 String 类型

typescript
//string类型声明
let s1: string = 'bddxg'
let s2: string = `http://www.${s1}.top`
console.log(s1, s2) // Output: bddxg http://www.bddxg.top

2.4 Array 类型

typescript
//Array类型声明
let arr1: string[] = ['a', 'b', 'c', 'd', 'e']
let arr2: number[] = [1, 2.1, 0o100, 0x100, 3.33]
let arr3: Array<number> = [1, 2, 3] // 泛型

console.log(arr1, arr2, arr3) // Output: [ 'a', 'b', 'c', 'd', 'e' ] [ 1, 2.1, 64, 256, 3.33 ] [ 1, 2, 3 ]

2.5 特殊类型

typescript
//特殊类型
let t1: any // 表示任意类型 想当于关闭了ts类型检测
t1 = 'hello'
t1 = 123
t1 = 20 < 30
let t2: unknown = '大西瓜' // 未知类型,一个类型安全的any
let t22: string
// 第一种:if判断
if (typeof t2 === 'string') {
  t22 = t2
  console.log(t22)
}
// 第二种:断言
t22 = t2 as string
console.log(t22)
// 第三种:<>
t22 = <string>t1 // 当t1的值类型为string时才给t22赋值
//void用来表示空,以函数为例,表示没有返回值的函数
function v1(): void {
  //return 123 //此时再return就会报错
}

2.6 对象和函数类型声明

typescript
let o = {
  name: '小明',
  age: 18,
}
console.log(o.name)
// o.gender = 'male'; // 无法添加属性

let userInfo: { name: string; age?: number } // age属性可以存在也可以没有
userInfo = { name: '小红' }
console.log(userInfo)

let school: { name: string; addr: string; [propName: string]: any } // [propName: string]:any 允许添加新的属性
school = { name: '小学', addr: '西街', students: 6000 }
console.log(school)

// 函数类型
let nums: number[] = [1, 3, 2, NaN, 10, 5, 4]
nums.sort((a, b) => a - b)
console.log(nums)

2.7 运算符号和应用

typescript
// 联合选择
let a: string | string[]
a = 'hello'
a = ['h', 'e', 'l', 'l', 'o']

// 同时(基本用不到)
let c: { name: string } & { age: number }
c = { name: '混混', age: 28 }

// 字面量 直接用值作为类型,不能为其他
let gender: 'male' | 'female'

// 元组(可以限制个数以及每一位元素的类型)
let arr: [string, number, boolean] = ['hello', 3090, true]

//枚举 enum
enum state {
  start,
  end,
}

console.log(state.start)

// 类型别名
type myType = 1 | 2 | 3 | 4 | 5
let t1: myType = 1
let t2: myType = 2

2.8 类型断言

typescript
// 类型断言 类似于类型强制转换,强制让typescript认为数据类型为指定的类型

let someValue = 'this is string'
// 尖括号语法
let strLength: number = (<string>someValue).length
// as语法
let strLength2: number = (someValue as string).length
// as const 转换为字面量
let str = 'hello' as const
// 等同于下面,不能给其他值
let str1: 'hello' = 'hello'
// 数组使用as const 会变为只读的元组
let arr1 = ['hello', 13] as const

2.9 非空断言

javascript
let str: string
str = 'abc'
// 开启strictNullChecks 为true 不允许值为空
// str = null // 不能将类型“null”分配给类型“string”
// str = undefined // 不能将类型“undefined”分配给类型“string”
// 非空断言 !
let el = document.querySelector('.box')!
// 类型断言
let el2: HTMLDivElement = document.querySelector('.box2') as HTMLDivElement

3. 函数、类、封装

3.1 函数类型

typescript
type myFun = (num1: number, num2: number) => number

function calc(a: number, b: number, fn: myFun) {
  return fn(a, b)
}
// 方法一
let result = calc(1, 2, (n, m) => n + m)
// 方法二
let result2 = calc(1, 2, function (n, m) {
  return n * m
})
console.log(result, result2)
// 在对象中限制函数类型
let user: {
  name: string
  age: number
  say: () => void
}
user = {
  name: '小明',
  age: 10,
  say: () => console.log('hai'),
}
user.say()

3.2 函数参数的灵活处理

typescript
// 可选参数 ?
const func = function (a: number, b?: number): number {
  return 10
}
console.log(func(10))
console.log(func(10, 20))
// 默认参数
const func2 = (a: number, b: number = 10) => {
  return 20
}
console.log(func2(10, 20))
// 可变参数
const func3 = (a: number, b: number, ...args: unknown[]): void => {
  console.log(args)
}
func3(10, 20, [33, '22', true])
typescript
// 重载出现的条件:方法/函数名相同,参数类型不同 或者参数数量不同 或者参数顺序不同
function add(n: number, m: number): number
function add(n: string, m: string): string
function add(n: number, m: string): string
function add(n: string, m: number): string

function add(n: any, m: any): any {
  return n + m
}
console.log(add(2, 'hello'))
console.log(add(2, 3))

3.4 类的定义

TS 中类的使用:

typescript
class Person {
  name: string
  age?: number

  constructor(name: string, age: number = 0) {
    this.name = name
    this.age = age
  }
  say(): void {
    console.log(this.name + '说话')
  }
}

const p1 = new Person('张三')
const p2 = new Person('麻花', 24)

p2.say()

类的继承:

typescript
class Student extends Person {
  school: string
  constructor(name: string, school: string, age: number = 0) {
    super(name, age)
    this.school = school
  }
  study() {
    console.log(`${this.name}${this.school}上学`)
  }
  // 重写方法
  say() {
    console.log(this.name + '说今天天气不错!')
  }
}

const s1 = new Student('西瓜', '大学', 18)
s1.study()
s1.say()

3.5 访问修饰符

typescript
class Person {
  name: string
  age: number
  protected gender: 'male' | 'female' // protected
  birthdays: string
  constructor(
    name: string,
    age: number,
    gender: 'male' | 'female',
    birthdays: string,
  ) {
    this.name = name
    this.age = age
    this.gender = gender
    this.birthdays = birthdays
  }
  say(): void {
    console.log(this.name + '#' + this.age)
  }
}

class Student extends Person {
  readonly school: string
  private score: number
  constructor(
    name: string,
    age: number,
    gender: 'male' | 'female',
    birthdays: string,
    school: string,
    score: number,
  ) {
    super(name, age, gender, birthdays)
    this.school = school
    this.score = score
  }
  study() {
    console.log(this.name + '' + this.gender)
  }
}

const p1 = new Person('西瓜', 18, 'male', '11-11')
console.log(p1)
// 属性“gender”受保护,只能在类“Person”及其子类中访问。
// p1.gender

const s1 = new Student('麻花', 18, 'female', '11-10', '高中', 98)
console.log(s1)
// 属性“score”为私有属性,只能在类“Student”中访问。
// s1.score

s1.study()

// 属性“gender”受保护,只能在类“Person”及其子类中访问。
// s1.gender
// 无法分配到 "school" ,因为它是只读属性。
// s1.school = '小学'

3.6 面向对象的封装和存取器

typescript
class Person {
  name: string
  private _age: number

  constructor(name: string, _age: number) {
    this.name = name
    this._age = _age
  }

  set age(value: number) {
    if (value >= 0 && value <= 120) this._age = value
  }
  get age() {
    // 25岁之前返回真实年龄,
    return this._age >= 25 ? this._age - 5 : this._age
  }
  run() {
    this.left()
    this.right()
    this.go()
  }
  private left() {
    console.log('迈左腿')
  }
  private right() {
    console.log('迈右腿')
  }
  private go() {
    console.log('Go!')
  }
}
const p1 = new Person('混混', 28)
console.log(p1.age)
console.log(p1.run())
// 属性“right”为私有属性,只能在类“Person”中访问。
// console.log(p1.right());

3.7 static 的应用

typescript
class Student {
  name: string
  age: number
  static school: string = '高中'

  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
  study() {
    console.log('学!')
    // 非静态方法可以访问静态资源属性
    console.log(this.study.name + Student.school)
  }
  static run() {
    console.log('跑步!')
    // 静态方法可以访问静态属性
    console.log(this.run.name + this.school)
    // 静态方法不可以访问非静态成员属性
    // console.log(this.run.name + this.age)
  }
}

const s1 = new Student('麻花', 18)
console.log(s1.name)
s1.study()
// 属性“run”在类型“Student”上不存在。你的意思是改为访问静态成员“Student.run”吗?
// s1.run()
Student.run()
console.log('--------------')

console.log(Student.school)

// 什么时候使用到静态属性和方法

// 在所有对象中共同用到的同一种属性或方法,可以声明为静态属性、方法

// 常见的需要初始化使用的类  const date= new Date()

// 常见的直接使用的类 Math.random()

// 静态方法不可以访问非静态成员属性

// 非静态方法可以访问静态成员属性

3.8 抽象类与接口

typescript
// 抽象类是一种特殊的类
// 接口是一种特殊的抽象类

// 抽象类 需要使用absract 关键字申明
abstract class Person {
  name: string
  constructor(name: string) {
    this.name = name
  }
  // 在一个类中没有方法体的方法,就被称为抽象方法
  abstract run(): void
}
// 抽象类无法初始化: 无法创建抽象类的实例。
// const p1 = new Person('麻花')

// 如果继承抽象类的类没有实现抽象类的方法,则这个类只能称为抽象类
abstract class Children extends Person {}

// 子类继承了抽象类并实现了抽象类的方法,则子类可以正常被初始化使用
class Student extends Person {
  constructor(name: string) {
    super(name)
  }
  run() {
    console.log('gogo!')
  }
}

const s1 = new Student('麻花')
s1.run()

// 接口中所有方法都是抽象的,都是公有的
interface One {
  // 无需给出具体的值,只是一个约束
  hello: string
  say: () => void
}

interface Two {}

// 可以实现继承多个接口
interface Three extends One, Two {
  foo(): void
}
// 创建类来实现接口,类必须实现接口所有描述才能正常使用
class Four implements Three {
  hello = 'hello'
  say() {
    console.log(this.hello)
  }
  foo = () => console.log('foo')
}

3.9 面向对象的多态性

typescript
// 由继承而产生的了相关的不同的类,对同一个方法可以有不同的响应
interface USB {
  start(): void
  run(): void
  end(): void
}

// implements是一个类实现一个接口用的关键字.
class Mouse implements USB {
  start = () => console.log('鼠标载入')
  run = () => console.log('鼠标移动')
  end = () => console.log('鼠标卸载')
}

class KeyBoard implements USB {
  start = () => console.log('键盘载入')
  run = () => console.log('键盘点击')
  end = () => console.log('键盘卸载')
}

function demo(u: USB) {
  u.start()
  u.run()
  u.end()
}

// 执行相同的方法,有不同的响应
demo(new Mouse())
demo(new KeyBoard())

3.10 接口的灵活运用

typescript
// 使用接口可以更加灵活的定义一个对象的类型
interface Person {
  name: string
  age: number
}

type Myobj = {
  color: string
  size: number
}

const p1: Person = { name: '张三', age: 17 }
const m1: Myobj = { color: 'red', size: 5 }

// 接口在后期一些灵活的用法

interface L1 {
  [index: number]: string
}

const l1: L1 = { 1: 'a', 2: 'b', 3: 'c' }

interface L2 {
  [key: string]: number
}

const l2: L2 = { a: 1, b: 2 }

3.11 认识和使用泛型

typescript
// 在申明的时候不指定类型,在应用的时候由应用者决定具体类型
function fun<T, W>(a: T, b: W) {
  return [a, b]
}
const result = fun(1, '西瓜')
console.log(result)

3.12 泛型的灵活运用

typescript
// 泛型参数默认值
interface Person<T = number, W = string> {
  id: T
  name: W
}
function player<P extends Person>(user: P) {
  return [user.id, user.name]
}

const result = player({ id: 333, name: 'job' })
console.log(result)

// 如果没有length属性,则不符合规范示例
interface ILength {
  length: number
}

function getLength<L extends ILength>(arg: L) {
  return arg.length
}

const res = getLength('abcdef')
const res1 = getLength([1, 2, 3, 4, 5])
const res2 = getLength({ name: '西瓜', age: 18, length: 18 })
console.log(res)
console.log(res1)
console.log(res2)

4.工程化中使用 TS

4.1 webpack 打包 TS

安装模块:

bash
yarn add -D webpack webpack-cli webpack-dev-server typescript ts-loader html-webpack-plugin

配置文件:

javascript
// webpack.config.js
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
  // 设置主入口文件
  entry: './src/index.ts',
  output: {
    filename: 'bundle.js',
    path: resolve(__dirname, 'dist'),
  },
  module: {
    // 设置以ts结尾的文件,用ts-loader 来解析,排除node_modules文件夹
    rules: [{ test: /\.ts$/, use: 'ts-loader', exclude: /node_modules/ }],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
    }),
  ],
  // 告知打包工具,对以下格式的文件进行打包
  resolve: {
    extensions: ['.js', '.ts'],
  },
  mode: 'development',
}

入口文件:

typescript
// index.ts
const sub = require('./subtract')
function sum(a: number, b: number): number {
  return a + b
}

console.log(sum(5, 10))
console.log(sub(20, 18))

其他模块:

typescript
// subract.ts
export = function sub(a: number, b: number): number {
  return a - b
}

4.2 命名空间

typescript
// 命名空间一个最明确的目的就是解决重名问题
// 虽然函数名相同可以通过重载的方式解决,但类却不可以
namespace One {
  export let school: string = '小学'
}
namespace Two {
  export let school: string = '初中'
}
console.log(One.school)
// 调用其他文件命名空间,则对应文件的命名空间和变量必须已经导出
// const Three = require('./high')
// 在ts中使用require和import 需要安装@types/node模块
import { Three } from './high'
console.log(Three.school)

其他模块:

typescript
export namespace Three {
  export let school: string = '高中'
}

4.3 vue3 中使用 TS

按照脚手架步骤,对项目进行初始化即可

// TODO