TypeScript 基本语法

栏目说明

本栏目收集了一些 TypeScript 相关学习知识

1. 体验 TS

栏目说明

  • 可以全局安装 typescript 这个包,然后终端用 tsc test.ts 命令来将 test.ts 这个文件 转化为 test.js 文件
  • 可以全局安装 ts-node 这个包,然后终端用 ts-node test.ts 命令来直接运行 test.ts 文件(内部做了转化)

2. TS 中的类型定义

1. 简单数据类型定义

点击查看 6 种基础类型定义
// 6 种基础类型定义: number, string, boolean, null, undefined, symbol
let age: number = 18
let str: string = 'str'
let bool: boolean = false
let n: null = null
let u: undefined = undefined
let s: symbol = Symbol()

2. 复杂数据类型定义

  • 2.1 类型别名:type
    • 使用关键字 type 可以复用 所有变量 类型
      // customType 是自定义的变量名
      type customType = (number | string)[]
      
点击查看 数组 类型定义
// 1. 只包含数字的数组(2种方法都可以)
let arr: number[] = [1, 2, 3] // 推荐
let arr1: Array<number> = [1, 2, 3]

// 2. 联合类型:数组中可以包含 number 或 string 类型(2种方法都可以)
let arr3: (number | string)[] = [1, 'sdfd', 2] // 推荐
let arr4: Array<number | string> = [1, 'sdfd', 2]

// 3. 类型别名:使用 关键字 type 给复杂的类型起别名
type customType = (number | string)[]
let arr5: customType = [1, 'sdfd', 2]
点击查看 函数 类型定义
// 场景一:有返回值
// 1. 单独指定:给参数和返回值单独指定类型
// 命名函数
function add(n1: number, n2: number): number {
  return n1 + n2
}
console.log(add(1, 2)) // 3

// 匿名函数
const add2 = (n1: number, n2: number): number => {
  return n1 + n2
}
console.log(add2(1, 2)) // 3

// 2. 同时指定:直接使用表达式统一指定类型 (只适用于匿名函数)
const add3: (n1: number, n2: number) => number = (n1, n2) => n1 + n2
console.log(add3(1, 2)) // 3

// 场景二:无返回值
function greet(str: string): void {
  console.log(str)
}
greet('hello') // hello

// 场景三:可选参数 (这种形参一定要在放在最后面)
function mySlice(n1?: number, n2?: number): void {
  console.log('start:' + n1, 'end:' + n2)
}
mySlice() // start:undefined end:undefined
mySlice(1) // start:1 end:undefined
mySlice(1, 2) // start:1 end:2
  • 2.2 接口:interface

    • 使用关键字 interface 可以复用 对象变量 的类型

      // 普通定义
      interface MyPerson {
        name: string
        age: number
        sayHi(): void
      }
      
      // 继承
      interface MyPerson2 extends MyPerson {
        sex: string
      }
      
      const person: MyPerson2 = {
        name: 'zs',
        age: 18,
        sayHi() {},
        sex: '男'
      }
      
点击查看 对象 类型定义
// 场景一:必选属性
// 写在一行(加分号)
const obj: { name: string; age: number; sayHi(): void; greet(name: string): void } = {
  name: '张三',
  age: 18,
  sayHi() {},
  greet(name) {}
}

// 写在多行(不加分号)
const obj2: {
  name: string
  age: number
  sayHi: () => void // 函数也可以用箭头函数形式
  greet: (name: string) => void
} = {
  name: '张三',
  age: 18,
  sayHi() {},
  greet(name) {}
}

// 场景二:可选属性(使用 ? )
function myAxios(config: { url: string; method?: string }) {
  console.log(config.url)
}
myAxios({ url: 'url' }) // url

// 场景三:复用对象类型,要使用 关键字 interface 来定义对象类型
interface MyPerson {
  name: string
  age: number
  sayHi(): void
}
const person1: MyPerson = {
  name: 'zs',
  age: 18,
  sayHi() {}
}
const person2: MyPerson = {
  name: 'zs2',
  age: 20,
  sayHi() {}
}

3. 扩展类型

点击查看 元组 数据类型定义
// 元组:特殊的数组,限定好元素个数和类型
const position: [number, string] = [1, '2']
点击查看 字面量 类型定义
// 字面量类型:用来限定某个变量的具体范围,搭配联合类型使用
const fn = (direction: 'left' | 'right') => {}

// 使用:只能填入 left right 这两个值
fn('left')
fn('right')
fn('others') // 报错
点击查看 枚举 类型定义
// 枚举类型:用来限定某个变量的具体范围(默认从0开始,如果被赋值,则递增)
enum Color {
  Red, // 0
  Green, // 1
  Blue // 2
}
console.log(Color.Red) // 0

// 也可以手动初始化
enum Direction {
  left = 'left',
  right = 'right',
  up = 'up',
  down = 'down'
}
console.log(Direction.down) // down

3. 类型断言

说明

  • 类型断言 可以用来手动指定一个值的类型。
  • 类型断言 用来告诉编译器你比它更了解这个类型,并且它不应该再发出错误。
点击查看 类型断言语法
// 语法有 2 种,如下:
// 1. 值 as 类型 (推荐写法)
interface Foo {
  bar: number
  bus: string
}
const foo = {} as Foo
foo.bar = 123 // 如果不指定类型,而给空对象添加属性,会报错
foo.bus = 'bus'

// 2. <类型> 值
let foo: any
let bar = <string>foo // 现在 bar 的类型是 'string'






 






4. class 类

1. 类的继承

说明

类的继承 2 种方案

  • extends(继承父类)
  • implements(实现接口)
点击查看 继承案例
// 1. extends 的继承方法和 js 一样
class Father {
  com() {
    console.log('公司')
  }
}
class Son extends Father {
  son = 'son'
}
const s = new Son() // 实例s 有 son 属性和 com 方法

// 2. implements 接口继承要求这个 子类必须有 interface 中的属性
interface Singable {
  sing(): void
}
class Person implements Singable {
  // 必须有该方法(因为该方法在 接口Singable 中被定义过)
  sing() {
    console.log('sing')
  }
}
 










 









2. 类成员的保护

关键字publicprotectedprivate
可访问处类本身、子类、实例类本身、子类类本身
点击查看 类成员的 3 种保护 案例
class Father {
  // 1. public 写了相当于没写(类及其子类的内部可以使用,实例也可以访问)
  public a = 'a'
  public say(): void {
    console.log('say')
    console.log(this.a)
  }

  // 2. protected 保护成员(类及其子类的内部可以使用,但实例不可访问)
  protected money = 'my money'
  protected getMyMoney() {
    console.log('getMyMoney')
    console.log(this.money)
  }

  // 3. private 保护成员(类内部可以使用,子类和实例都不可访问)
  private mySelf = 'mySelf'
  private beatMySelf() {
    console.log(this.mySelf)
  }
}
class Son extends Father {
  study = 'study'
  run() {
    this.getMyMoney()
    console.log(this.money)
  }
}

const fat = new Father()
const son = new Son()
console.log(fat.a) // 'a'
fat.say() // 'say', 'a'
console.log(son.a) // 'a'
son.say() // 'say', 'a'

console.log(fat.money) // 报错
fat.getMyMoney() // 报错

 






 






 






















5. readonly

使用

  • 可以初始化,可以在类的 constructor 中改变
    • 能在 interface 中使用,修饰属性
    • 能在 type 中使用,修饰属性
    • 能在 class 类中使用,修饰属性
点击查看 readonly 案例
// 1. 在 interface 中使用
interface Foo {
  readonly [x: number]: number
}
// 使用
const foo: Foo = { 0: 123, 2: 345 }
console.log(foo[0]) // 123 (读取成功)
foo[0] = 456 // 报错 (属性只读)

// 2. 在 type 中使用
type Foo2 = {
  readonly bar: number
  readonly bas: number
}
// 初始化
const foo: Foo2 = { bar: 123, bas: 456 }
// 不能被改变
foo.bar = 456 // 报错 (属性只读)

// 3. 在 类 中使用
class Father {
  readonly aaa: number = 18
  constructor(age?: number) {
    if (!age) {
      console.log(this.aaa)
      return
    }
    this.aaa = age
    console.log(this.aaa)
  }
}

new Father() // 18
new Father(999) // 999

6. 类型兼容性

TS 采用 结构化类型系统,常见的注意点有 3 种

1. 类 与 接口 兼容 多赋少

  • 定义 接口时,属性多的可以赋值给属性少的
点击查看案例
// 1. 类 兼容
class Father {
  name: string
  age: number
}
class Son {
  name: string
  age: number
  job: string
}
let father = new Father()
let son = new Son()
father = son // 可行 (Son只需要具备Father的name,age属性即可兼容)
// son = father // 报错

// 2. 接口 兼容
interface Father {
  name: string
  age: number
}
interface Son {
  name: string
  age: number
  job: string
}
let father: Father
let son: Son
father = son // 可行 (Son只需要具备Father的name,age属性即可兼容)
// son = father // 报错
 














 













2. 函数的形参 兼容 少赋多

  • 定义 函数时, 形参 少的可以赋值给 形参 多的函数
  • 定义 函数时, 返回值 多的可以赋值给 返回值 少的函数
点击查看案例
// 1. 一般情况
type Father = (name: string, age: number) => void
type Son = (name: string, age: number, job: string) => void
let father: Father
let son: Son

son = father // 可行 (Father 只需要具备 Son 的 name,age 属性即可兼容)
// father = son // 报错

// 2. 混入接口的情况(依旧以函数形参为准)
interface Father {
  name: string
  age: number
}
interface Son {
  name: string
  age: number
  job: string
}
type fn = (p: Father) => void
type sn = (p: Son) => void
let father: fn
let son: sn
son = father // 可行 (认为 函数 fn 的参数比 函数 sn 的参数少)
father = son // 报错
 








 















7. 交叉类型

  • 可以实现 成员的继承
点击查看 接口中使用交叉类型 案例
// 1. 使用 extends继承 实现
interface Father {
  name: string
}
interface Son extends Father {
  age: number
}
// 必须有 Father 和 Son 所有属性
const obj: Son = {
  age: 18,
  name: 'zs'
}

// ---------------------------------------------------

// 2. 使用 交叉类型(&) 实现
interface Father {
  name: string
}
interface Son extends Father {
  age: number
}
type newInterFace = Father & Son

// 必须有 Father 和 Son 所有属性
const newObj: newInterFace = {
  age: 18,
  name: 'zs'
}
 














 













8. 泛型

  • 可以 在保证类型安全的同时,支持多种类型
点击查看 函数中使用泛型 案例
// 1. 在函数中使用泛型 (T相当于一个形参,可以在调用函数时传入一个类型)
function fn<T>(params: T): T {
  return params
}
const res = fn<number>(50)
console.log(res) // 50
const res2 = fn<string>('str')
console.log(res2) // 'str'

// 2. 也可以省略类型(ts内部会推断)
const res4 = fn(100) // 100  此时不再是 number 类型,而是 字面量100 类型

// 3. 泛型可以用 extends 进行约束
interface MyLength {
  length: number
}
function getValue<Type extends MyLength>(value: Type): Type {
  console.log(value.length)
  // console.log(value.name) // 报错 (因为MyLength中没有name属性)
  return value
}
getValue({ length: 50, name: 12 }) // 传入的参数必须有 length 属性
 









 

 









点击查看 接口中使用泛型 案例
// 1. 接口中使用 泛型 (使用时不能省略,无推断)
interface Father<Type> {
  name: Type
  com: () => Type[]
}

let father: Father<string> = {
  name: 'zs',
  com() {
    return ['1', '2']
  }
}
点击查看 类中使用泛型 案例
// 1. 类中使用 泛型
class Father<Type> {
  age: Type
  constructor(age: Type) {
    this.age = age
  }
  add(x: Type, y: Type): void {
    console.log(11)
    console.log(this.age)
  }
}

let father = new Father<number>(100)
father.add(0, 0) // 11  100

9. 泛型工具类型

说明

TS 内置了一些工具类型,用于提升开发效率

1. Partial 可选

点击查看 Partial 用法
// Partial 可以把接口成员 一次性全部改为 可选成员
// 1. 定义一个接口
// 2. 使用该接口
// 3. 怎么把该接口快速改造成 所有成员可选?

// 1. 定义
interface Father {
  name: string
  age: number
  hasSon: boolean
}

// 2. 使用时 必须传入三个属性
let father: Father = {
  name: 'zs',
  age: 100,
  hasSon: true
}

// 3. Partial 改造后,新类型 NewFather 三个属性都变为可选属性(同时不破坏Father)
type NewFather = Partial<Father>
let father2: NewFather = {
  name: 'ls'
}

2. Readonly 只读

点击查看 Readonly 用法
// Readonly 可以把接口成员 全部改为只读
// 1. 定义一个接口
// 2. 使用该接口
// 3. 怎么把该接口快速改造成 所有成员只读?

// 1. 定义
interface Father {
  name: string
  age: number
  hasSon: boolean
}

// 2. 创建变量后,成员还可以修改
let father: Father = {
  name: 'zs',
  age: 100,
  hasSon: true
}
father.age = 120

// 3. Readonly 改造后,三个属性都变为可选属性
type NewFather = Readonly<Father>
let father2: NewFather = {
  name: 'ls',
  age: 100,
  hasSon: false
}
father2.age = 120 // 报错, 三个属性都变成 只读的了

3. Pick 挑选某些成员

点击查看 Pick 用法
// Pick 可以把选取某一些接口成员
// 1. 定义一个接口
// 2. 挑选接口中某一些成员
// 3. 使用新类型

// 1. 定义
interface Father {
  name: string
  age: number
  hasSon: boolean
}

// 2. 挑选接口中某一些成员 语法: Pick<Type, keys>
type PickFather = Pick<Father, 'name' | 'age'>

// 3. 使用新类型
let pick: PickFather = {
  name: 'zs',
  age: 18
}

4. Record 构造一个键值一样的对象

点击查看 Record 用法
// Record 可以快速构造一个键值格式一样的对象

type Father = Record<'a' | 'b' | 'c', string[]>

let father: Father = {
  a: ['aa'],
  b: ['bb'],
  c: ['cc']
}

10. 索引签名类型

  • 数组是特殊的对象
  • 索引签名类型 可以定义 对象数组 类型的接口
点击查看 案例
// 1. 对象定义:要求该对象的键名 必须为字符串类型,键值为 数字或字符串
interface Father {
  [key: string]: number | string
}
// 使用
let father: Father = {
  name: 'zs',
  age: 18
}

// 2. 数组定义:要求键名是数字,键值为数字
interface Son {
  [key: number]: number
}
// 使用 (数组是特殊的对象)
let son: Son = [1, 2, 3]

11. 映射类型

  • 映射类型只能在 类型别名 type 中使用
点击查看 映射联合类型 案例
// 1. Father 和 Son2 组合写法 等同于 Son
type Father = 'x' | 'y' | 'z'
type Son = { x: number; y: number; z: number }
type Son2 = { [x in Father]: number } // 相当于遍历了 联合类型 Father (便于扩展)

let son: Son = {
  x: 0,
  y: 1,
  z: 2
}

let son2: Son2 = {
  x: 0,
  y: 1,
  z: 2
}
点击查看 映射对象类型 案例
// 1. Father 和 Son2 组合写法 等同于 Son
type Father = { x: string; y: number; z: boolean }
type Son = { x: number; y: number; z: number }
// 相当于获取并遍历了 对象类型 Father 的键名
type Son2 = { [x in keyof Father]: number }

let son: Son = {
  x: 0,
  y: 1,
  z: 2
}

let son2: Son2 = {
  x: 0,
  y: 1,
  z: 2
}
点击查看 Partial 怎么实现 案例
// 1. 利用映射类型实现 Partial 工具类型
type Props = { x: string; y: number; z: boolean }

type MyPartial<T> = {
  [key in keyof T]?: T[key]
}

type MyProps = MyPartial<Props>

let p: MyProps = {
  x: 'x',
  y: 111
  // z: false // 都是可选的值
}



 
 
 








12. 索引查询类型

// 1. 索引查询类型
type Props = { x: string; y: number; z: boolean }

type Type1 = Props['x'] // string 类型
type Type2 = Props['x' | 'y'] // string | number 类型
type Type3 = Props[keyof Props] // string | number | boolean 类型

13. TypeScript 类型声明文件

  • 当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能。

  • ts 中有两种文件类型

    • ① .ts 文件

      • 可以定义类型,也可以编写逻辑代码
      • 可以被编译成 js 文件再执行逻辑代码
      • 作用:编写功能代码
    • ② .d.ts 文件

      • 只能定义类型,不能编写逻辑代码
      • 不会被编译成 js 文件,只提供类型定义
      • 作用:为 js 提供类型信息

1. 导入某个包后,没有代码提示?

  1. 访问 类型声明地址 搜索对应的包名
  2. 复制下载命令,并在项目中下载 这个包 对应的声明文件
    • 例如:lodash 安装后,就会有代码提示了
      • npm i @types/lodash --save-dev

2. 怎么编写自己的 声明文件?

  • 在导入 .js 文件时,TS 会自动加载与 .js 同名的 .d.ts 文件,以提供类型声明
  • declare 关键字:用于类型声明, 而不是创建一个新的变量
      1. 在 utils.js 文件已存在的变量声明(let const)
      1. 创建一个 utils.d.ts 来修饰 utils.js 在所有的 let const 和 function 前加上 declare 即可

14. tsconfig.json 文件

  • 生成 tsconfig.json 文件,先要安装 typescript 这个全局包
  • tsconfig.json 文件用来指定: 项目文件和项目编译所需的配置项
<!-- 终端输入以下命令,即可在项目中生成 tsconfig.json 文件 -->
tsc --init
上次更新:
贡献者: 唐干宵