TypeScript基本信息总结

作者主页: 仙女不下凡

前言介绍: 以下 内容都是我个人对于前端知识的总结,会定期更新欢迎持续关注!

欢迎点赞 收藏 ⭐留言 如有错误敬请指正!


一、安装与运行

1.安装

  • TypeScript官网地址:https://www.tslang.cn/play/index.html
# 安装命令
$ npm install -g typescript

# 按指定版本号进行安装
$ npm install -g [email protected]

# 安装完毕后,查看版本号
$ tsc -v

2.起步

# 创建文件夹
$ mkdir TypeScript

# 在TypeScript文件夹中创建demo.ts文件,其代码如下:
console.log('Hello,world')

3.编译

正常编译

# 编译命令
$ tsc demo.ts
# 编译命令执行之后,生成demo.js文件

# 再执行,输出一下内容:Hello,world
$ node demo.js

安装插件之后直接启动浏览器编译,不安装插件不能直接编译。

# 安装ts-node
$ npm install ts-node -g

# 编译并执行
$ ts-node demo.ts

二、基础

1. 原始数据类型

  • 原始数据类型包括:number、string、boolean、null、undefined和symbol。
let tsNum: number = 123
let tsStr: string = 'AAA'
let tsFlag: boolean = true
let tsNull: null = null
let tsUndefined: undefined = undefined

2.void空值

在JavaScript中,是没有空值(void)的概念的,但在TypeScript中,可以使用void来表示一个没有返回值的函数:

function sayHello (): void {
  console.log('Hello, world')
}

也可以定义一个void类型的变量,不过这样的变量没有什么意义,因为我们只能给这种变量赋值为null或undefined。

let voidValue1: void = null
let voidValue2: void = undefined

3.void、null和undefined区别

  • 在TypeScript中,null和undefined是所有类型的子类型,也就是说可以把undefined或null赋值给number等类型的变量,而对于void而言,它只能被赋值为null或者undefined。
let tsNumber1: number = undefined
let tsNumber2: number = null

// 这两行代码会编译报错
let voidValue1: void = 123
let voidValue2: void = '123'

4.任意值Any

// 以下代码是正确的,编译成功
let tsAny: any = 123
tsAny = '123'
  • 若定义了一个变量,没有指定其类型,也没有初始化,那么它默认为any类型:
// 以下代码是正确的,编译成功
let tsValue
tsValue = 123
tsValue = '123'

5.类型注解和类型推断

// typescript会自动为num1变量推断为number
let num1 = 123

// typescript会自动为num4变量推断为number
let num2 = 456
let num3 = 789
let num4 = num2 + num3
function add (num1: number, num2: number): number {
  return num1 + num2
}
// 或省略函数的返回值类型,因为typescript会基于num1和num1全部为number类型,从而推断出函数返回值为number类型
function add (num1: number, num2: number) {
  return num1 + num2
}
  • 建议:始终为函数返回值提供一个确定的类型是有一个比较推荐的好习惯

6.联合类型

// 联合类型:表示取值可以为多种类型中的一种,多种类型使用|分隔开。
let value: string | number
value = 123
value = '123'
// 会编译报错
function getLength (value: string | number): number {
  return value.length
}

// 以下代码不会编译报错
function valueToStr (value: string | number): string {
  return value.toString()
}
let tsValue: string | number
tsValue = '123'
console.log(tsValue.length) // 编译正确
tsValue = 123
console.log(tsValue.length) // 编译报错

7.接口

在TypeScript中,接口interface是一个比较重要的概念,它是对行为的抽象,而具体的行为需要由类去实现,接口interface中的任何代码都不会被最后编译到JavaScript中。

interface Person {
  name: string,
  age: number
}
let person: Person = {
  name: 'why',
  age: 23
}

在以上代码中,person变量它是Person类型的,那么此变量只能接受接口规定的属性,且属性值的类型也必须和接口中规定的一致,多一个属性或者少一个属性在TypeScript中都不是被允许的。

interface Person {
  name: string,
  age: number
}
// 编译报错
let person1: Person = {
  name: 'why'
}
// 编译报错
let person2: Person = {
  name: 'why',
  age: 23,
  sex: 'man'
}

接口中的任意属性

interface Person {
  name: string,
  age: number,
  // 任意属性
  [propName: string]: any
}
let person: Person = {
  name: 'why',
  age: 23,
  sex: 'man'
}

接口中的可选属性

interface Person {
  name: string,
  // age属性是可选的
  age?: number
}
// 编译成功
let person1: Person = {
  name: 'why'
}
let person2: Person = {
  name: 'why',
  age: 23
}

接口中的只读属性

interface Person {
  name: string,
  readonly age: number
}
let person: Person = {
  name: 'why',
  age: 23
}
// 编译报错
person.age = 32 // 属性为只读的,那么其不能被赋值。

8.函数的类型

接口定义函数

interface AddInterface {
  (x: number, y: number): number
}
const add: AddInterface = function (x: number, y: number): number {
  return x + y
}
console.log(add(1, 2))    // 输出3

可选参数

function getArea (a: number, b?: number): number {
  return  b ? a * b : a * a
}
console.log(getArea(4))     // 16
console.log(getArea(4, 5))  // 20
// 编译报错
function getArea (b?: number, a: number): number {
  return  b ? a * b : a * a // 可选参数必须放在最后一个位置,否则会报错。
}

参数默认值

function getArea (a: number, b: number = 1): number {
  return  a * b
}
console.log(getArea(4))     // 4
console.log(getArea(4, 5))  // 20

TIP:给一个参数设置了默认值后,就不再受TypeScript可选参数必须在最后一个位置的限制了。

function getArea (b: number = 1, a: number): number {
  return  a * b
}
// 此时必须显示的传递一个undefined进行占位
console.log(getArea(undefined,4)) // 4
console.log(getArea(4, 5))        // 20

剩余参数

在ES6中,我们可以使用…符号进行收缩剩余参数,在TypeScript中,我们依然可以这么做:

// rest是一个数组,我们可以使用数组的类型来定义它
function getTotal (a: number, ...rest: number[]) {
  console.log(a)    // 1
  console.log(rest) // [2, 3, 4]
}

getTotal(1, 2, 3, 4,)

函数重载

在TypeScript中对于函数重载的理解是:只要函数参数的类型或者函数参数的数量不同时,就可以认为这是两个函数(重载)。

// 前两个为函数声明,最后一个才是函数实现
function add (a: number, b: number): number;
function add (a: string, b: string): string;

function add (a: number | string, b: number | string): number | string {
  if (typeof a === 'number' && typeof b === 'number') {
    return a + b
  } else {
    return a + '' + b
  }
}
console.log(add(1, 2))      // 3
console.log(add('1', '2'))  // 12

TIP:在有函数重载时,会优先从第一个进行逐一匹配,因此如果重载函数有包含关系,应该将最精准的函数定义写在最前面。

9.类型断言

代码分析:在print函数中,我们接受的参数可以是Student或者Teacher,在此函数内部我们希望能够根据不同的类型来调用不同的方法。我们首先使用instanceof来判断参数是否为Student类的实例,是我们将person参数强制断言成Student类型,此时就可以安全的调用sayHi方法了,Teacher同理。

class Student {
  name: string = 'student'
  sayHi () {
    console.log(this.name)
  }
}
class Teacher {
  name: string = 'teacher'
  sayHello () {
    console.log(this.name)
  }
}
function print(person: Student | Teacher) {
  if (person instanceof Student) {
    // 强制断言为Student类型
    (person as Student).sayHi()
  } else {
    // 强制断言为Teacher类型
    (person as Teacher).sayHello()
  }
}

let stu = new Student()
let teacher = new Teacher()
print(stu)      // student
print(teacher)  // teacher

10.类型别名

类型别名用type关键字来给一个类型起一个新的名字,类型别名常用于联合类型。

type combineType = number | string
type typeObj = {
  age: number;
  name: string;
}
const value1: combineType = 123
const obj: typeObj = {
  age: 123,
  name: 'why'
}

11.字符串字面量类型

字符串字面量类型用来表示一个变量只能取某几个字符串值中的一个。

type eventName = 'click' | 'scroll' | 'mousemove'
function handleEvent (event: eventName) {
  console.log(event)
}
handleEvent('click')    // click
handleEvent('scroll')   // scroll
handleEvent('dbclick')  // 编译报错

三、进阶

1.数组和元组

数组

基础写法

# 第一种写法
let numArray: number[] = [1, 2, 3]
// 只允许存储string类型
let strArray: string[] = ['1', '2', '3']

# 第二种写法,不常用
let numArray: Array<number> = [1,2,3]
let strArray: Array<string> = ['1', '2', '3']

联合类型

// 只允许存储number和string类型的值
let tsArray: (number|string)[] = [1,2,'3']

let tsArray2:Array<number|string>=[1,2,'3'] // 写法二,不常用

数组中存储对象类型

// 只允许存储对象仅有name和age,且name为string类型,age为number类型的对象
let objArray:({name: string, age:number})[] = [
    {name: 'zz', age: 25}
]

// 为了更加方便撰写代码,可以使用类型别名的方式来管理以上类型
type person = {
    name: string,
    age: number
}
let objArray2: person[] = [
    {name: 'zz', age:25}
]

元组

元组:一个数组如果知道它确定的长度,且每个位置的值的类型也是确定的,那么就可以把这样的数组称为元组。

// tuple数组只有2个元素,并且第一个元素类型为string,第二个元素类型为number
let tuple: [string, number] = ['AAA', 123]

WARNING:当访问元组中已知位置的索引时,将得到其对应正确的值;当访问元组中未知位置的索引时,会报错。

let tuple: [string, number] = ['zz', 25]
console.log(tuple[1])// 25
console.log(tuple[2])// 报错

2.枚举 Enum

枚举Enum类型用来表示取值限定在指定的范围,例如一周只能有七天,颜色只能有红、绿、蓝等。

/ 我们可以在打印的内容发现,其输出值从0开始,依次累加1。这是枚举类型的默认行为 /
enum colors {
    red,
    green,
    blue
}
console.log(colors.red) // 0
console.log(colors.green) // 1
console.log(colors.blue) // 2

手动设置一个起始值:

enum colors {
    red = 10,
    green,
    blue
}
console.log(colors.red) // 10
console.log(colors.green) // 11
console.log(colors.blue) // 12

// 我们不仅可以正向的获取值,还可以通过值反向获取枚举
console.log(colors[10]) // red
console.log(colors[11]) // green
console.log(colors[12]) // blue

3.类

类的继承

在JavaScript中,通过extends关键字来实现子类继承父类,子类也可以通过super关键字来访问父类的属性或者方法。

class Person {
    name: string
    constructor(name: string) {
        this.name = name
    }
    seyHello() {
        console.log(`hello,${this.name}`)
    }
}
class Teacher extends Person {
    constructor(name: string) {
        super(name) // 调用父类的构造函数
    }
    sayTeacherHello() {
        return super.seyHello() // 调用父类的方法
    }
}
let teacher = new Teacher('whr')
teacher.seyHello()        // hello, why
teacher.sayTeacherHello() // hello, why

TIP:有一种关于类属性的简写方式,就是在类的构造函数中指明访问修饰符。

// 简写形式
class Person {
  constructor (public name: string) {}
}
// 等价于
class Person {
  name: string
  constructor (name: string) {
    this.name = name
  }
}

存取器

在class中,可以通过getter和setter来改变属性的读取和赋值行为。

class Person4 {
    // 私有属性,只能在类中进行访问
    private _name:string
    constructor(_name:string) {
        this._name =  _name
    }
    get name(){
        return this._name
    }
    set name(name) {
        this._name = name
    }
}
let person = new Person4('why')
console.log(person.name) // why
person.name = 'zz'
console.log(person.name) // zz

静态属性和静态方法

静态属性和静态方法:只能通过类来进行访问,不能通过类的实例来进行访问。在众多设计模式中,有一种设计模式叫做单例设计模式,可以使用static静态方法来辅助我们完成单例设计模式。

class Person {
    private static _instance: Person
    private constructor () {}
    public static getInstance () {
        if(!this._instance) {
            this._instance = new Person()
        }
    }
}
const person1 = Person.getInstance()
const person2 = Person.getInstance()
console.log(person1 === person2) // true

TypeScript类的访问修饰符

  • public:公有的,在任何地方都可以访问到。
  • protected:受保护的,只能在类的内部及其类的子类内部使用。
  • private:私有的,只能在类的内部使用
class Person {
    private age: number
    protected address: string
    public name: string
    constructor(age: number, address: string, name: string) {
        this.age = age
        this.address = address
        this.name = name
    }
}
class Teacher extends Person {
    sayHello() {
        console.log(`my address is ${this.address}`) // my address is 广东广州
        console.log(`my name is ${this.name}`) // my name is why
        console.log(`my age is ${this.age}`) // 编译报错
    }
}

const person = new Person(25, '广东广州', 'why')
console.log(person.name) // why
console.log(person.age) // 编译报错
console.log(person.address) // 编译报错

只读属性

可以使用readonly关键字来表示属性是只读的。

class Person {
    constructor (public readonly name: string) {}
}
let person = new Person('AAA')
console.log(person.name) // AAA
person.name = 'zz' // 编译报错

抽象类abstract

  • 抽象类不能被实例化,只能被继承。
abstract class Animal {
  name: string
  constructor (name: string) {
    this.name = name
  }
}
class Person extends Animal{}
const person = new Person('why')
console.log(person.name)    // why
const animal = new Animal() // 编译报错
  • 抽象类中的抽象方法必须被子类实现。
abstract class Animal {
  name: string
  constructor (name: string) {
    this.name = name
    }
  abstract eat(): void
}
class Person extends Animal{
    // 子类必须实现抽象类中的抽象方法
    eat() {
        console.log('person is eating')
    }
}
const person = new Person('why')
console.log(person.name)    // why
person.eat() // person is eating

4.类和接口

类实现接口

TIP:一个类可以实现一个或者多个接口,用逗号分隔。

/若定义一个接口,然后类去实现它,那么这个接口中的属性和方法,在类中必须全部都要存在,否则会编译报错。/
interface Animal {
    age: number
    sayHello(): void
}
class Person implements Animal {
    age: number
    sayHello() {
        console.log(this.age)
    }
}

接口继承接口

interface Animal {
    age: number
    sayHello(): void
}
interface Person extends Animal {
    name: string // Person接口继承了Animal,拥有了Animal中所有的属性和方法
}
class Person implements Person {
    age: number
    sayHello () {
        console.log(this.age)
    }
}

接口继承类

  • 在有些语言中,接口一般而言是不能继承类的,但在TypeScript中是可以继承的,接口继承类以后,就拥有类中所有的属性和方法。
class Point {
    x: number
    y: number
    constructor(x: number, y: number) {
        this.x = x
        this.y = y
    }
}
interface Point2 extends Point {
    z: number
}
let point2: Point2 = {
    x: 10,
    y: 10,
    z: 10
}
console.log(point2) // { x:10, y:10, z:10 }

5.泛型 generics

  • 泛型generics是指在定义函数、接口和类的时候,不预先指定其具体类型,而在使用的时候再去指定的一种特性。

函数中的泛型

  • 假设有如下函数,要求:其中参数a和b接受的类型必须为相同的类型。
// 如下写法参数a&b必须是number或string类型,但是没有办法限制a&b是同一个类型。
function join (a: number | string, b: number | string) {
  return `${a}${b}`
}

当这种情况时候我们可以像如下写法

function join<T> (a: T, b: T): string {
  return `${a}${b}`
}
console.log(join(1, 2))     // 12    
console.log(join('1', '2')) // 12
console.log(join(1, '2'))   // 编译报错
  • 注意: 我们在调用join()函数并进行传参的时候,TypeScript会自动帮我们推断参数的类型,以上三行代码也可以像如下方式进行撰写:
console.log(join<number>(1, 2))     // 12    
console.log(join<string>('1', '2')) // 12
console.log(join<number>(1, '2'))   // 编译报错

TIP:泛型可以是多个的。

function join<T, P> (a: T, b: P): string {
  return `${a}${b}`
}
console.log(join(1, 2))     // 12    
console.log(join('1', '2')) // 12
console.log(join(1, '2'))   // 12

类中的泛型

class CreateClass<T> {
    zeroValue: T
    add: (x: T, y: T) => T
}
let createNumber = new CreateClass<number>()
createNumber.zeroValue = 25
createNumber.add = function (x, y) {
    return x + y
}
let createString = new CreateClass<string>()
createString.zeroValue = '25'
createString.add = function (x, y) {
    return `${x}${y}`
}
console.log(createNumber.add(1, 2))     // 3
console.log(createString.add('1', '2')) // 12

TIP:[email protected]+以后的版本,我们可以为泛型提供一个默认值。

class CreateClass<T = number> { // 默认值为number
    zeroValue: T
    add: (x: T, y: T) => T
}
let createNumber = new CreateClass()
createNumber.zeroValue = 25
createNumber.add = function (x, y) {
    return x + y
}
let createString = new CreateClass<string>()
createString.zeroValue = '25'
createString.add = function (x, y) {
    return `${x}${y}`
}
console.log(createNumber.add(1, 2))     // 3
console.log(createString.add('1', '2')) // 12

接口中的泛型

interface CreateArray {
  <T>(length: number, value: T): T[]
}
let createArrayFunc: CreateArray = function (length, value) {
  let result = []
  for (let index = 0; index < length; index++) {
    result[index] = value
  }
  return result
}
console.log(createArrayFunc(3, 'AAA'))  // ['AAA', 'AAA', 'AAA']
console.log(createArrayFunc(2, true))   // [true, true]

6.声明合并

  • 如果定义了两个相同名字的函数、接口或类,那么它们会合并成一个类型。 声明合并,我们在上面已经有实例的案例了,那就是函数的重载。
function add (a: number, b: number): number;
function add (a: string, b: string): string;
function add (a: number | string, b: number | string): number | string {
  if (typeof a === 'number' && typeof b === 'number') {
    return a + b
  } else {
    return a + '' + b
  }
}
console.log(add(1, 2))      // 3
console.log(add('1', '2'))  // 12
// 当重复定义同一个接口时,会进行接口合并:
interface Person {
  name: string,
  address: string
}
interface Person {
  name: string,
  age: 23
}
// 相当于
interface Person {
  name: string,
  address: string,
  age: 23
}

WARNING: 当合并的属性类型不一致时,会报错。

interface Person {
  name: string,
  address: string
}
interface Person {
  // 报错,name类型冲突
  name: number,
  age: 23
}

7.命名空间

你可能感兴趣的:(js+ts,typescript,前端,javascript)