TypeScript复习笔记

目录

安装编译TS的工具包

编译TS代码

ts热更新

类型注解

常用基础类型概述

原始类型

复杂类型

数组

数组元素都是单类型的

数组元素都是多类型的

联合类型

元组

别名

函数类型

void类型

可选参数

默认参数

剩余参数

函数重载

对象类型

可选属性

接口

任意属性

只读

函数接口

别名也可以声明对象

接口继承接口

接口还可以继承类

类型断言

字面量类型

枚举

声明枚举

 访问枚举

枚举中的值

字符串枚举

计算所得项

常数枚举

外部枚举

any类型

typeof

class类

实例属性的类型

实例方法的类型

静态方法

继承

实现接口

权限修饰(可见修饰符)

readonly修饰符

抽象类

类可以被当作类型

声明合并

类型兼容性

接口兼容性

函数兼容性

参数个数

函数类型

兼容性验证

返回值类型

交叉类型&

交叉类型与接口继承的区别

泛型

泛型函数

泛型约束

泛型类型变量可以多个

泛型接口

泛型类

泛型工具类

Partial

Readonly

Pick,keys>

Record,type>

索引签名类型

映射类型

联合类型创建新类型

对象类型创建新类型

Partial实现原理

索引查询

了解部分

ts配置

类型总结

编译文件

类的存取器


视频地址:b站黑马ts视频

视频地址2:2023最新ts

安装编译TS的工具包

Node.js/浏览器,只认识js代码,不认识ts代码。所以需要先将ts代码转换为js代码,才能正常运行

安装命令:npm i -g typescript

查看命令:tsc -v(查看typescript的版本)

typescript包就是用来编译ts代码的,提供了一个 tsc 的命令。实现 ts -> js 的转化

出现版本号即表示安装成功

编译TS代码

ts文件编译为js文件命令:tsc 文件名.ts

首先我们创建一个 test 的文件夹,里面新建一个 hello.ts 的文件。通过 vscode 打开

选中 hello.ts ,然后右键点击 在文件资源管理器中打开。会出现一个文件所在的窗口

然后点击文件路径空白的地方,并输入cmd,点击enter回车。此时就会弹出cmd窗口

在cmd窗口中输入  tsc hello.ts  就会多出一个 hello.js 的文件(vscode可以查看)。这个文件就是 ts 文件编译后的 js文件。(cmd输入文件的时候,可以输入文件的开头,然后按tab键可以补全)

编译后的js文件

最后就可以在cmd窗口通过 node hello.js 指令执行js文件

ts热更新

上面的 tsc xxx.ts 虽然可以实现ts文件到js文件的编译,但是每次更新,都需要手动输入编译命令,这样就很繁琐。我们可以使用  tsc xxx.ts -w  这个指令来实现实时编译,也就是热更新

tsc  xxx.ts  -w

类型注解

其实就是对变量或者方法或者类进行类型约束

我们声明类型有两种方式:第一种是显示注解,第二种是直接赋初始值(类型会自动推断为这种类型)

第一种

// 表示声明一个变量num,并且变量num的类型为number
let num: number

let num1: number = 100

第二种

// 表示声明一个变量num,并且变量num的类型为number(根据值判断出来的)
let num = 10

常用基础类型概述

可以分为两类:1. js已有类型。 2. ts新增类型

原始类型

案例代码(这里通过类型推断)

let num = 100 // number类型
let flag = true // boolean类型
let str = 'hello' // string类型
let n = null // null类型
let un = undefined // undefined类型
let sbl = Symbol() // sysbol类型 Symbol()会自动创建一个独一无二不重复的值(多用于对象中的键)

复杂类型

每个具体的对象都有自己的类型语言

数组

数组类型类型标注有两种方式(显示声明和类型推断)

数组元素都是单类型的

1. 类型推断

2. 直接标注数组元素的类型

数组元素都是多类型的

有时候数组中存放的不止一个类型,那么就需要用到 联合类型 或者 元组 

联合类型

1. 类型推断

2. 直接标注数组元素的类型

当然,你也可以直接写具体的值。写具体的值的话那就只能是规定的值了,不能变了。其实一个值也是一种类型,叫字面量类型

元组

而元组相比于联合类型,就更加严格一点,严格的地方在于顺序,元组规定好的顺序不能变

想添加新的元素就必须在类型标注中新增一个类型

别名

函数类型

函数类型实际指的是:函数参数类型函数返回值类型

// 只指定类型为函数
let fn: Function
fn = () => {
  console.log(123)
}

// 普通函数 参数num1为number类型,参数num2为number类型,返回值类型为number
function fn1(num1: number, num2: number): number {
  return num1 + num2
}

// 函数表达式
const fn2 = (num1: number, num2: number): number => {
  return num1 + num2
}

也可以同时指定参数和返回值的类型(但是这种只适用于函数表达式,不太好用)

// 函数表达式
const fn2: (num1: number, num2: number) => number = (num1, num2) => {
  return num1 + num2
}
void类型

如果函数没有返回值,则返回值类型就指定为void

// 函数无返回值
const fn: (num1: number, num2: number) => void = (num1, num2) => {
  console.log(num1 + num2)
}
可选参数

有的时候,参数可能不是必传的,就可以使用可选参数,其实就是在类型前加个 ?

默认参数
// 注意: 可选参数必须放到后面,如果有剩余参数,则剩余参数放最后
const getName = (a: string, b: string = '子'): string => {
  return a + b
}

const res: string = getName('王')
console.log(res)
剩余参数
// 如果有剩余参数,剩余参数要放最后
const getSum = (a: number, b: number, ...agrs: number[]): number => {
  let res: number = agrs.reduce((sum: number, item: number): number => {
    return (sum += item)
  }, 0)
  return a + b + res
}

let res: number = getSum(10, 20, 30, 40, 50)
console.log(res) // 150
函数重载
  • 重载:方法名相同,但是方法参数的个数,类型不同

// 函数重载声明
function rev(x: number): number
function rev(x: string): string
function rev(x: number | string): number | string | void {
  if (typeof x === 'number') {
    return x.toFixed(2)
  }
  if (typeof x === 'string') {
    return x.split('').reverse().join('')
  }
}

rev(10)
rev('张')

对象类型

可以指定类型为 object。但是推荐使用接口或者下面的方式

案例代码

let obj: {
  name: string
  age: number
  say(language: string): void
  run: () => void
} = {
  name: 'huihui',
  age: 25,
  say(language) {
    console.log('会说' + language)
  },
  run: () => {},
}

// 也可以指定几个必须有的,其他的都随意
// 这个表示对象里必须有name,其他的可以任意加
let obj2: { name: string; [propName: string]: any }
obj2 = {
  name: '李四',
  age: 25,
  gender: '男',
}
可选属性

有些属性可有可无,则可以使用可选属性,就是类型声明前加个 ?

let obj: {
  name: string
  age: number
  say(language?: string): void
  run: () => void
} = {
  name: 'huihui',
  age: 25,
  say() {},
  run: () => {},
}
接口

接口主要是能实现复用,避免重复声明

任意属性

有时候只需要规定几个必须有的,其他的可以任意传入

// 定义一个任意属性的接口
interface IPerson {
  name: string
  age: number
  gender?: number
  // 定义任意属性和值
  [propName: string]: any // 也可以使用联合类型声明更具体的类型
}

// 使用接口
let zs: IPerson = {
  // 这些固定的必须有
  name: '张三',
  age: 25,
  gender: 1,
  // 定义的任意属性可以有多个,也可以没有
  address: '地球村',
}
只读
  • 创建对象后,有些属性,我们希望只读,用户不能进行修改。例如id。则就可以使用只读

interface IPerson {
  // 只读属性
  readonly id: number
  name: string
  age: string
  gender: string
  // 定义任意属性和值(这里规定了值只能是string或者number)
  [propName: string]: string | number
}

// 使用接口
let zs: IPerson = {
  // 这些固定的必须有
  id: 1,
  name: '张三',
  age: '25',
  gender: '1',
  // 定义的任意属性可以有多个,也可以没有
  address: '地球村',
}
函数接口

interface 接口名 {

(参数:类型 , ...):返回值类型

}

代码案例

// 定义一个函数的接口
interface IGetSum {
  // (参数:类型 , ...):返回值类型
  (num1: number, num2: number): number
}

// 使用定义好的函数接口
let getSum: IGetSum = (sum1, sum2) => {
  return sum1 + sum2
}
别名也可以声明对象

类型别名更强大

// 接口
interface obj {
  name: String
  age: number
  sayHi(): void
}
let person: obj = {
  name: 'jack',
  age: 23,
  sayHi() {},
}
// 别名,可以为任意类型定义别名
type obj1 = {
  name: String
  age: number
  sayHi(): void
}
let p: obj1 = {
  name: '张三',
  age: 24,
  sayHi() {},
}

type numStr = number | string
let num: numStr = 20
接口继承接口

// 接口
interface person {
  name: String
  age: number
  sayHi(): void
}
// 直接使用接口
let zs: person = {
  name: 'jack',
  age: 23,
  sayHi() {},
}
// 继承接口
interface man extends person {
  sing: string
  dance: string
  rap: string
}
// 使用接口
let cxk: man = {
  name: 'cxk',
  age: 30,
  sayHi() {
    console.log('迎面走来的你让我蠢蠢欲动')
  },
  sing: '喂喂喂',
  dance: 'jijiji',
  rap: 'is freestyle',
}
接口还可以继承类
class Person {
  name: string
  age: number
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  say(language: string): void {
    console.log('会说' + language)
  }
}
// 接口不光能继承接口,还可以继承类,这点挺牛的
interface IAsian extends Person {
  work()
}

// 可以写一个测试类继承接口
class Chinese implements IAsian {
  name: string
  age: number
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  work() {
    console.log(
      '给外国人的感觉是特别卷,特别喜欢工作。但是我们是真的喜欢工作吗?'
    )
  }
  say(language: string): void {
    console.log('会说' + language)
  }
}

类型断言

使用 as 进行类型断言

控制台打印显示类型

  • any类型可以访问任何属性和方法。可以利用把一个属性定义为any,来添加属性和方法

(window as any).a = 100
  • 还可以将any断言为一个更具体的类型

function getSum(x: any,y: any): any {
    return x + y
}
let res = getSum(10,20) as number
let res = getSum('10','20') as string

字面量类型

字面量类型通常配合联合类型一起使用。可以实现枚举(就是固定的几个值的集合)的效果。

枚举

表明一组明确的可选值。例如季节,只有春夏秋冬。再比如星期,一周7天,周一到周日。再比如性别,只有男女。枚举就是一组固定可选的值组成的集合

声明枚举

  • 如果有同名的,则后面的会覆盖前面的

 访问枚举

枚举中的值

字符串枚举

除了数字枚举外,还有字符串枚举,但是字符串枚举需要赋初始值,并且没有自增行为

计算所得项

枚举中的项也可以是计算所得的

// 常数项
enum Season {
  spring = '春',
  summer = '夏',
  autumn = '秋',
  winter = '冬',
}
// 计算所得项
enum Com {
  length = 'str'.length,
  color = 'red' // 计算所得项后面的必须赋初始值,不然会编译报错
}

常数枚举

常数枚举是使用const enum 定义的枚举类型

常熟枚举不允许被修改,在编译阶段就会被删除,并且不能包含计算成员

// 常熟枚举,里面的每一项都不允许再修改了
const enum Season {
  spring = '春',
  summer = '夏',
  autumn = '秋',
  winter = '冬',
}
// Season.spring = '春天' // 会提示只读,不允许修改

外部枚举

  • 就是使用 declare enum 定义的枚举类型,不允许被修改,在编译阶段就会被删除,并且不能包含计算成员。主要用于声明文件

// 外部枚举
declare enum Season {
  spring = '春',
  summer = '夏',
  autumn = '秋',
  winter = '冬',
  // a = 'pink'.length,
}
// Season.spring = '春天' // 会提示只读,不允许修改

any类型

使用any类型就会失去ts类型保护,变为了js。不推荐用

隐式居右any类型的情况

1. 声明变量不提供类型,也不提供默认值

2. 函数参数不加类型

typeof

在类型声明的地方使用typeof,则使用的是ts的

代码案例

// 可以判断变量类型
console.log(typeof 100) // number

// 也可以根据已有变量的值,获取该值的类型,来简化类型书写
let p = { x: 1, y: 2 }
// 可以使用这种
function formatPoint(point: { x: number; y: number }) {  }
// 也可以使用typeof,判断已有变量的值,简化类型书写
function formatPoint(point: typeof p) {  }

// 注意:typeof只能用来查询变量或者属性的类型,无法查询其他形式的类型(比如:函数调用的类型)
let p = { x: 1, y: 2 }
// 通过typeof 变量.属性  来进行调用
let num: typeof p.x
// 下面这种是不可以的,不能直接进行函数调用
function add(num1: number, num2: number) {
    return num1 + num2
}
// let res: typeof add(1,2) // 会直接报错

以下的都是ts的高级类型

class类

实例属性的类型

声明类的实例属性时,需要在上面提供实例属性的具体类型。构造函数不能有返回值 

class Person {
    // 实例属性的类型
    name: string
    age: number

    // 构造函数(目的就是初始化实例属性的)
    // 每次new类的时候(实例化类,创建具体的对象),就会调用有参构造,而有参构造里的this就是传来的对象
    // 传来对象.name = name  此时后面的name就是局部变量,就近查找,就查找到了参数name,也就是zs。age同理
    constructor(name: string, age: number) {
        this.name = name
        this.age = age
    }
}

const zs = new Person('zs', 18)
console.log(zs)

此时我们编译并输出

实例方法的类型

就跟声明函数是一样的

class Person {
    // 实例属性
    name: string
    age: number

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

    // 实例方法
    sayHello(str: string): void {
        console.log(str)
    }
}

const zs = new Person('zs', 18)
console.log(zs.sayHello('hello'))

静态方法

使用static修饰符修饰的方法称为静态方法,他们不需要实例化,直接通过 类名. 来调用

只属于当前类的方法和属性,可以使用静态修饰,静态成员不会被继承

class Person {
  name: string
  constructor(name: string) {
    this.name = name
  }
  // 静态属性
  static money: string
  // 静态方法
  static staticFn() {
    console.log('静态方法')
  }
}
// 直接  类名.  进行调用
Person.staticFn() // 静态方法
console.log(Person.money) // undefined
Person.money = '9999999'
console.log(Person.money) // 9999999

继承

继承父类之后,就可以使用父类的实例属性和实例方法。但是继承只能单继承,并且只能继承一个父类。而接口就不同了,接口可以实现n多个

class Person {
    // 实例属性
    name: string
    age: number

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

    // 实例方法
    sayHello():void { console.log('父类的sayHello方法执行了') }
}

// 只需要继承父类,就可以使用父类提供的实例属性和方法。也可以扩展自己的实例方法和属性
class Student extends Person {
    // 子类特有的实例属性
    gender: number

    // 子类可以扩展自己的实例属性
    constructor(name: string, age: number, gender: number) {
        super(name, age)
        this.gender = gender
    }

    // 子类也可以重写父类的实例方法
    sayHello(): void {
        super.sayHello()
        console.log('子类的sayHello方法执行了')
    }
}
 
const zs = new Student('zs', 18, 1)
console.log(zs)
zs.sayHello()

编译并执行后

实现接口

实现接口,就需要实现接口中定义的属性和方法。属性和继承不冲突(先写继承,再实现接口)

interface ISayHello {
    gender: number
    sayHello(): void
}
interface ISayHi {
    sayHi(): void
}

// 类可以实现n多个接口,更方便扩展。实现接口后,就需要实现接口中声明的方法和属性
class Student implements ISayHello,ISayHi {
    // 实例属性
    name: string
    age: number
    gender: number
    constructor(name: string, age: number, gender: number) {
        this.name = name
        this.age = age
        this.gender = gender
    }

    // 实例方法
    sayHello(): void {
        console.log('sayHello方法执行了')
    }
    sayHi(): void {
        console.log('sayHi方法执行了')
    }
}
 
const zs = new Student('zs', 18, 1)
console.log(zs)
zs.sayHello()
zs.sayHi()

编译并执行

权限修饰(可见修饰符)

类的成员属性(又叫实例属性)和成员方法(实例方法)可以被权限修饰符修饰。以此来达到类里面成员的可见性控制

权限修饰符一共有3个(权限范围从大到小)

1. public:共有的,共有成员可以在任何地方访问。默认就是这个,可以直接省略

2. protected:受保护的,仅对其声明所在类和子类中(非实例对象)可见

注意:

在子类的方法内部可以通过this来访问父类中受保护的成员,但是,对实例不可见

  1. 声明的所在类内部可见
  2. 子类方法内部可见。子类的方法内部可以通过this来访问父类中受保护的成员

上面可以看出,实例对象是不可以使用的

正确用法

class Person {
    protected name: string
    protected age: number
    constructor(name: string, age: number) {
        this.name = name
        this.age = age
    }

    protected sayHi(): void { console.log('Hi~') }
}

class Students extends Person {
    sayHi(): void {
        super.sayHi()
        console.log(`I am ${this.name}`)
    }
}

new Students('Jim', 18).sayHi()

编译执行

3. private:私有的,仅在当前类中可见

可以看到,private修饰的成员,除了在本类中访问,其他的都访问不了

正确用法

class Person {
    private name: string
    private age: number
    constructor(name: string, age: number) {
        this.name = name
        this.age = age
    }

    // private修饰的私有成员可以在本类中进行访问
    sayHi(): void { console.log('Hi~' + this.name + '~' + this.age) }
}

class Students extends Person {}

new Students('Jim', 18).sayHi()

编译执行

readonly修饰符

只读的,只读修饰符。用来防止在构造函数之外对属性进行赋值。但是这个修饰符只能修饰成员属性,不能修饰成员方法。如果不想成员属性值被修改,则就可以使用readonly修饰。只要readonly来修饰的属性,必须手动提供明确的类型

其他写法

class Animal {
  // readonly name: string
  // constructor(name) {
  //   this.name = name
  // }
  // 上面写法,和下面的一样
  constructor(readonly name: string) {
    // this.name = name // 这个可有可无,因为是只读的,也不能赋值
  }
}

属性前,还可以用那三个权限修饰符

class Animal {
  // constructor(readonly name: string) {
  // constructor(public name: string) {
  // constructor(protected name: string) {
  constructor(private name: string) {
    this.name = name
  }
}

注意点:  readonly修饰的成员属性,必须提供明确的类型。

                接口或者{}表示的对象类型,也可以使用readonly。

// 接口
interface Person {
    readonly name: string
}

let zs: Person = {
    name: '张三'
}
// zs.name = '李四'   错误的,只读的不能进行修改

// 对象
let obj: { readonly name: string } = {
     name: '张三'
}
// obj.name = '李四'  错误的,只读的不能进行修改

抽象类

  • abstract用于定义抽象类和其中的抽象方法,抽象属性

  • 抽象类中可以有抽象属性/方法,也可以有非抽象属性/方法

  • 抽象类不能被实例化。需要子类进行继承

abstract class Animal {
  name: string
  constructor(name: string) {
    this.name = name
  }
  // 抽象类中的方法必须是抽象方法(抽象方法不能有函数体)
  abstract say()
}

子类继承父类,并实现父类的抽象方法/属性

abstract class Animal {
  abstract name: string
  abstract say()
  run() {
    console.log('run')
  }
}

// 子类
class Cat extends Animal {
  name: string
  constructor(name: string) {
    super()
    this.name = name
  }
  say() {
    console.log('hello')
  }
}

const mimi = new Cat('咪咪')
console.log(mimi.name)
mimi.run()
mimi.say()

类可以被当作类型

class Animal {
  name: string
  constructor(name: string) {
    this.name = name
  }
}

class Cat extends Animal {
  constructor(name) {
    super(name)
  }
}

const cat: Cat = new Cat('咪咪')
const cat1: Animal = new Cat('哈基米') // 子类实例也可以使用父类的类型

声明合并

如果定义了两个相同名字的函数接口,那么他们会合并成一个类型

  • 函数的合并,就是函数重载

  • 类合并与接口合并一样

  • 接口的合并,就是接口中的属性和方法会简单的合并到一个接口中。接口的属性合并时,类型必须是唯一的

interface ICommit {
  name: string
  run()
  eat()
}
interface ICommit {
  age: number
  say()
}
// 定义一个测试类
class Person implements ICommit {
  name: string
  age: number
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  run() {
    console.log('都会奔跑')
  }
  say() {
    console.log('都会说话')
  }
  eat() {
    console.log('都需要吃饭')
  }
}

类型兼容性

注意:范围大的,可以指定范围小的当类型。反之则不行(因为范围大的包含了小的,但是小的没有包含大的)

代码验证

接口兼容性

函数兼容性

参数个数

函数类型

兼容性验证

上面的就会提示,缺少个z。总的来说,就是范围大的包含范围小的,所以可以兼容。但是范围小的,你不能赋值给范围大的,因为缺少参数,自然就报错了

返回值类型

多的可以赋值给少的

交叉类型&

起到了 and 的作用,就是类型组合到一起了,形成一个新的类型

let obj3: { name: string } & { age: number }
obj3 = { name: '张三', age: 20 }

交叉类型与接口继承的区别

泛型

泛型函数

案例代码

泛型约束

为泛型添加约束来收缩类型,从而能使用特有的属性和方法不报警告

添加泛型的约束收缩类型,主要有两种方式:

1. 指定更加具体的类型

const fn = <Type>(params: Type[]): Type[] => {
    console.log(params.length)
    return params
}

fn(['1', '2', '3'])

2. 添加约束,添加约束后,则就必须满足约束

interface IHasLength { length: number }

const fn = <Type extends IHasLength>(params: Type): Type => {
    console.log(params.length)
    return params
}

fn(['1', '2', '3'])
fn('12345')
泛型类型变量可以多个

代码案例

创建一个函数来获取对象中属性的值

function getProp<Type,key extends keyof Type>(obj: Type,key: key) {
    // 对key做出约束,key必须是Type(我们传入的是对象,所以Typeof是对象)中的所有成员名称的联合类型中的一个
    return obj[key]
}
let person = { name: 'zs',age: 24 }
getProp(person,'name')

// 定义一个泛型函数(返回传入的这个对象里面的键)
function fn<Type, key extends keyof Type>(obj: Type, key: key) {
  return obj[key]
}
// 使用泛型函数
fn({ name: 'zs', age: 24 }, 'name')

类型变量keyType约束,可以理解为:key只能是Type所有键中的任意一个,或者说只能访问对象中存在的属性

  • keyof操作符,是将一个类型映射为它所有成员名称的联合类型

interface Person {
  name: string;
  age: number;
  gender: string;
}
type P = keyof Person; // "name" | "age" | "gender"
// 我们可以看到,keyof将Person这个对象类型映射成了一个联合类型
// 因此我们可以更方便的操作这个联合类型

泛型接口

代码案例

interface IHasLength<Type> { 
    a: Type
    arr: Type[]
    fn: (Type) => Type
}

const obj:IHasLength<number> = {
    a: 100,
    arr: [1, 2, 3],
    fn: (num: number) => num * 2
} 

泛型类

代码案例

使用的时候,<>可以省略掉

class Person<Type, Type1> {
    name: Type
    age: Type1

    constructor(name: Type, age: Type1) {
        this.name = name
        this.age = age
    }
}

const zs = new Person('zs', 23)

泛型工具类

Partial<Type>

代码案例

// 这样的话就是必传
interface IProps {
    vModel: string
    children: string[]
}
const obj1: IProps = {
    vModel: '123',
    children: ['1', '2', '3']
}

// 被Partial包裹后就可以不传
type newIProps = Partial<IProps>
const obj2: newIProps = {
    vModel: '123',
    // children: ['1', '2', '3']
}
Readonly<Type>

代码案例

Pick<Type,keys>

代码案例

Record<key,Type>

代码案例

type Person = Record<'name' | 'age', string>

const zs: Person = {
  name: 'zs',
  age: '25',
}

索引签名类型

不确定对象值的类型时

代码案例

interface AnyObject {
    // 声明索引签名类型
    [key: string]: number // 规定键必须是字符串的(本来对象中的键也是string的),值必须是数字型的
}
// 使用索引签名类型
let obj: AnyObject = {
    a: 1,
    b: 2
}

不确定数组类型时

代码案例

interface MyArray<Type> {
    [index: number]: Type
}
let arr: MyArray<number> = [1,3,5]

映射类型

联合类型创建新类型

代码案例

只适合对象中键的值都是同一类型的。我们可以先把对象中的键组成一个类型,然后利用签名索引对键组成的类型进行遍历,并把值统一的类型写在后面

type PropKeys = 'x' | 'y' | 'z'
// 与上面的唯一的关系就是都有x y z
type Type1 = { x: number; y: number; z: number }
// 可以简化成下面的(因为都有xyz,所以可以利用签名索引进行遍历)。这种只适合对象中的值都是同一类型的
type Type2 = { [key in PropKeys]: number }

对象类型创建新类型

代码案例

适合做对象中的键为不同类型的,同一类型的当然也可以

type PropKeys = { name: string; age: number }

type Type1 = { [key in keyof PropKeys]: PropKeys[key] }

const zs: Type1 = {
  name: 'zs',
  age: 25,
}

Partial<Type>实现原理

索引查询

代码案例

我们刚刚的对象类型创建新类型中也进行了使用

索引查询类型不光可以查询单个键的类型,也可以查询多个键的类型

了解部分

ts配置

创建ts的config文件(初始化ts)

tsc -init

如果是vue项目,则直接在tsconfig.json文件中配置

  • ts编译器的配置文件,ts可以根据他的信息进行编译

  • 可以写注释

  • ** 表示任意目录,表示任意文件

  • 配置选项(在大括号一级直接写的)

include

  • 定义希望被编译文件所在的目录

  • 默认值 ["**/*"]

"include":["src/**/*","test/**/*"] // 表示src目录和test目录下的文件都会被编译

exclude

  • 定义需要排除在外的目录

  • 默认值 ["node_modules","bower_components","jspm_packages"]

"exclude":["src/test/**/*"] // src下的test文件夹下的文件不会被编译

compilerOptions

  • 编译器的选项,包含多个子选项,用来完成对编译的配置

项目选项

  • target

    • 设置ts代码编译的目标版本

    • 可选值:ES3ES5ES6/ES2015ES2017ES2018ES2019ES2019ES2020、ESNext

"compilerOptions": {
    // 设置编译后的目标版本
    "target": "ES6"
  }
  • module

    • 模块化的规范

"compilerOptions": {
    // 指定要使用的模块化
    "module": "ES6"
  }
  • lib

    • 用来指定项目中要使用的库,一般不需要改

"compilerOptions": {
    // 指定项目中要使用的库,一般不需要改
    // "lib": ['dom']
  }
  • outDir

    • 指定编译后的文件所在的目录

"compilerOptions": {
    // 指定编译后的文件所在的目录
    "outDir": "./dist",
  }
  • outFile

    • 将代码合并为一个文件(全局作用域中多个ts文件编译合并到一个文件中)

"compilerOptions": {
    // 将代码合并为一个文件(全局作用域中多个ts文件编译合并到一个文件中)
    "outFile": "./dist/app.js" // 但是module的值只能为system
  }
  • allowJs

    • 是否对js文件进行编译,默认是false

"compilerOptions": {
    // 是否对js文件进行编译,默认是false
    "allowJs": false
  }
  • checkJs

    • 是否检查js代码是否符合语法规范,默认也是false

"compilerOptions": {
    // 是否检查js代码是否符合语法规范,默认也是false
    "checkJs": false
  }
  • removeComments

    • 是否移除注释,默认false

"compilerOptions": {
    // 是否移除注释,默认false
    "removeComments": false
  }
  • noEmit

    • 会编译,但是不生成编译后的文件,默认也false

"compilerOptions": {
    // 会编译,但是不生成编译后的文件
    "noEmit": false
  }
  • noEmitOnError

    • 当有错误时不生成编译后的文件

"compilerOptions": {
    // 设置编译后的目标版本
    "target": "ES6",
    // 指定要使用的模块化
    "module": "ES6",
    // 指定项目中要使用的库,一般不需要改
    // "lib": ['dom'],
    // 指定编译后的文件所在的目录
    "outDir": "./dist",
    // 将代码合并为一个文件(全局作用域中多个ts文件编译合并到一个文件中)
    "outFile": "./dist/app.js", // 但是module的值只能为system
    // 是否对js文件进行编译,默认是false
    "allowJs": false,
    // 是否检查js代码是否符合语法规范,默认也是false
    "checkJs": false,
    // 是否移除注释,默认false
    "removeComments": false,
    // 会编译,但是不生成编译后的文件
    "noEmit": false,
    // 当有错误时不生成编译后的文件
    "noEmitOnError": false
  }

语法检测相关

  • alwaysStrict

    • 用来设置编译后的文件是否使用严格模式

    • 注意:又模块代码(导入导出)的时候,js会自动进入严格模式

"compilerOptions": {
    // 用来设置编译后的文件是否使用严格模式
    "alwaysStrict": true
  }
  • noImplicitAny

    • 不允许隐式的any类型

"compilerOptions": {
    // 不允许隐式的any类型
    "noImplicitAny": true
  }
  • noImplicitThis

    • 不允许不明确类型的this

"compilerOptions": {
    // 不允许不明确类型的this
    "noImplicitThis": true
  }
  • strictNullChecks

    • 严格检查空值

"compilerOptions": {
    // 严格检查空值
    "strictNullChecks": true
  }
  • strict

    • 严格检查的总开关,最好写上面

  "compilerOptions": {
    // 严格检查的总开关
    "strict": true,
    // 设置编译后的目标版本
    "target": "ES6",
    // 指定要使用的模块化
    "module": "ES6",
    // 指定项目中要使用的库,一般不需要改
    // "lib": ['dom'],
    // 指定编译后的文件所在的目录
    "outDir": "./dist",
    // 将代码合并为一个文件(全局作用域中多个ts文件编译合并到一个文件中)
    "outFile": "./dist/app.js", // 但是module的值只能为system
    // 是否对js文件进行编译,默认是false
    "allowJs": false,
    // 是否检查js代码是否符合语法规范,默认也是false
    "checkJs": false,
    // 是否移除注释,默认false
    "removeComments": false,
    // 会编译,但是不生成编译后的文件
    "noEmit": false,
    // 当有错误时不生成编译后的文件
    "noEmitOnError": false,
    // 用来设置编译后的文件是否使用严格模式
    "alwaysStrict": true,
    // 不允许隐式的any类型
    "noImplicitAny": true,
    // 不允许不明确类型的this
    "noImplicitThis": true,
    // 严格检查空值
    "strictNullChecks": true
  }

类型总结

  • 可选的话,可以在前面加个? num?: number

类型例子描述
number1,-20,2.5任意数字
string'hello','123'任意字符串
booleantrue,false布尔值true和false
字面量其本身(let num: 10)限制变量的值就是该字面量的值
any*任意类型
unknown*类型安全的any
void空值(undefined)没有值或undefined
never没有值不能返回任何值
object{ name: '奥特曼' }任意的js对象
array[1,2,3]任意的js数组
tuple[4,5]元素,TS新增类型,固定长度数组
enumenum{A,B}枚举,TS中新增类

断言与泛型写法

// 类型断言(直接声明e的类型就是为string)
c = e as string
c = <string>e // 这俩写法都是一样的

编译文件

编译单个文件

  • 编译文件时,使用-w指令后,Ts编译器会自动监视文件的变化,并在文件发生变化时对文件进行重新编译

tsc xxx.ts -w

编译文件夹下的所有文件

  • 需要先创建一个tsconfig.json的配置文件

  • 输入tsc指令即可全部编译,如果输入tsc -w则会监听文件夹下的所有文件

tsc -w

类的存取器

  • 在类中,可以使用gettersetter改变属性的赋值和读取行为

  • get是读取器,set是设置器

Person.js

class Person {
  constructor(name) {
    this.name = name
  }
  // 存取器都是用来控制属性的
  get name() {
    console.log('get:', this._name)
    return this._name
  }
  set name(val) {
    // 可以通过存取器对属性进行一些简单判断
    console.log('set:', val)
    this._name = val
  }
}
​
let zs = new Person()
zs.name = '王' // set: undefined(因为只有执行完函数之后,才会是王,所以第一次是undefined)  set: 王
zs.name // get: 王

Person.js

class Person {
  constructor(name) {
    this.name = name
  }
  get name() {
    return 'Jack'
  }
  set name(value) {
    console.log('setter: ' + value)
  }
}
​
let a = new Animal('Kitty') // setter: Kitty
a.name = 'Tom' // setter: Tom
console.log(a.name) // Jack

  • 14
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值