Blogs AI
蓝绿渐变 · 深色科技风

TypeScript入门

前端

TypeScript

1. 初识TypeScript

1.1 优化编译

  • npm i typescript -g:全局安装tsc指令用于执行ts文件。

  • tsc --init:初始化TS配置文件。

  • tsc --watch:启动热更新。

1.2 基本类型

  1. 基元类型String,NumberBoolean

    tsx
    let str: string = 'hello World'
    let num: number = 123
    let bool: boolean = true
    

1.3 数组类型

  1. 数组类型通过Array<type>type[]两种方式定义。

    tsx
    let arr1: number[] = [1, 2, 3]
    let arr2: Array<number> = [1, 2, 3]
    

1.4 any类型

  1. any可以是任意类型的数据。
  2. any可以重新赋值为任意类型的数据。

1.5 函数类型

  1. 函数参数可以设置类型。

  2. 函数的返回值可以设置类型。

    tsx
    function greet(name: string): number {
      console.log(`Hello, ${name}`.toLocaleUpperCase());
      return 0
    }
    
    greet('123')
    
  • 可以限制函数的参数和返回值。

1.6 对象类型

  1. 对象类型可以为单个属性设置类型。

  2. 对象类型的属性可以设置为选填类型。

    typescript
    function printCoord(point: { x: number, y: number }) {
      console.log(`坐标点为(${point.x},${point.y})`);
    }
    
    printCoord({
      x: 3,
      y: 7
    })
    
    function printName(obj: { first: string, last?: string }) {
      console.log(`我的名字是${obj.first}${obj.last?.toLocaleUpperCase()}`);
    }
    
    printName({
      first: 'zhou',
      last: 'kai'
    })
    

1.7 联合类型

  1. 通过|符号可以设置联合类型。

    tsx
    function printId(id: number | string) {
      //console.log(`Your ID is ${id}`);
      if (typeof id === 'number') {
        console.log(`Your ID is ${id}`);
      } else {
        console.log(`Your ID is ${id.toLocaleUpperCase()}`);
      }
    }
    
    printId(123)
    printId('zhou')
    
  • 注意:使用联合类型需要判断类型后再使用对应的方法。

1.8 类型别名

  1. 可以通过type关键字设置类型别名方便复用。

    tsx
    type Point = {
      x: number,
      y: number
    }
    
    function printCoord(point: Point) {
      console.log(`坐标点为(${point.x},${point.y})`);
    }
    
    printCoord({
      x: 3,
      y: 7
    })
    
    type ID = number | string
    
    function printId(id: ID) {
      //console.log(`Your ID is ${id}`);
      if (typeof id === 'number') {
        console.log(`Your ID is ${id}`);
      } else {
        console.log(`Your ID is ${id.toLocaleUpperCase()}`);
      }
    }
    
    printId(123)
    printId('zhou')
    

1.9 类型扩展

  1. 可以使用interfaceextends对接口类型进行扩展。

    tsx
    /// 扩展接口
    interface Animal {
      name: string
    }
    
    interface Bear extends Animal {
      say: boolean
    }
    
    const bear: Bear = {
      name: 'anmi',
      say: false
    }
    
  2. 可以通过&符号对type类型别名进行拓展。

    tsx
    type Animal = {
      name: string
    }
    
    type Bear = Animal & {
      say: boolean
    }
    
    const bear : Bear = {
      name: 'anmi',
      say: true
    }
    

1.10 类型断言

  1. 通过as关键字或者在值前面通过<type>来断言变量的类型。

    tsx
    const myCanvas = document.getElementById('main_canvas') as HTMLCanvasElement
    
    const myCanvas2 = <HTMLCanvasElement>document.getElementById('main_canvas')
    
    let db = 123 as unknown
    
    db = 'zhou'
    

1.11 字面量类型

  1. 可以直接使用字面量对数据进行类型设置。

    tsx
    let str1: 'left' | 'center' | 'right' = 'left'
    
    let num1: 1 | 0 | -1 = 0
    
    let bool1: true | 'auto' | 1 = 1
    
  2. 通过类型断言可以将对象中属性的类型断言为字面量类型进行传参。

    tsx
    function handleRequest(url: string, method: 'GET' | 'POST') {
      console.log(`请求${url},类型为${method}`);
    
    }
    
    const req = {
      url: 'https//example.com',
      method: 'GET'
    } as const
    handleRequest(req.url, req.method)
    

1.12 null和unfined类型

tsx
let un: undefined = undefined
let nu: null = null

function liveDangerously(x?: number | null) {
  console.log(x?.toFixed(2));
}

1.13 bigint和symbol类型

tsx
const oneHundred: bigint = BigInt(100)

const antherHundred: bigint = 100n

const firstName = Symbol('name')

const secondName = Symbol('name')
  • 注意:需要在es2020及以上的版本才可以使用BigInt

2. 入门TypeScript

2.1 枚举

  1. 使用enum关键字进行枚举,枚举内容会根据第一个的值依次递增。

    tsx
    enum Direction {
      Up = 1,
      Down,
      Left,
      Right
    }
    
    console.log(Direction.Up); // 1
    console.log(Direction[1]); // Up
    

    转义后的JS

    js
    var Direction;
    (function (Direction) {
        Direction[Direction["Up"] = 1] = "Up";
        Direction[Direction["Down"] = 2] = "Down";
        Direction[Direction["Left"] = 3] = "Left";
        Direction[Direction["Right"] = 4] = "Right";
    })(Direction || (Direction = {}));
    console.log(Direction.Up);
    

2.2 类型缩小

tsx
function padLeft(padding: string | number, input: string) {
  if (typeof padding === 'number') {
    return new Array(padding + 1).join(' ') + input
  }
  return padding + input
}
  1. 通过typeof对类型进行缩小范围,用于精确表示类型。

  2. 可以通过!符号进行真值缩小,用于判断是否为null或undefined。

    tsx
    function multiplyAll(
      values: Array<number> | undefined,
      factor: number
    ) {
      if (!values) {
        return values
      } else {
        return values.map((v) => {
          return v * factor
        })
      }
    }
    console.log(multiplyAll([1, 2, 3, 4], 5));
    
  3. 可以通过=符号判断类型是否相等或者不等来缩小类型范围。

  4. 可以使用in操作符缩小对象属性中类型范围。

    tsx
    type Fish = { swim: Function }
    type Bird = { fly: () => string }
    type Human = { swim?: () => void; fly?: () => void }
    
    function move(animal: Fish | Bird | Human) {
      if ('swim' in animal) {
        return (animal as Fish).swim()
      }
    
      return (animal as Bird).fly()
    }
    
    console.log(move({
      fly() {
        console.log('fly');
        return "zhoukai"
      }
    }));
    
  5. 可以使用instance操作符来检验原型链对类型进行缩小。

  6. 可以使用never类型进行穷尽性检查,用于是否排除干净所有类型。

2.3 类型谓词

  1. is 关键字一般用于函数返回值类型中,判断参数是否属于某一类型,并根据结果返回对应的布尔类型。

    tsx
    type Fish = {
      name: string,
      swim: Function
    }
    
    type Bird = {
      name: string,
      fly: Function
    }
    
    function isFish(pet: Fish | Bird): pet is Fish {
      return (pet as Fish).swim !== undefined
    }
    
    function getSmallPet(): Fish | Bird {
      let fish: Fish = {
        name: 'sharkey',
        swim() {
          console.log('my is sharkey');
        }
      }
    
      let bird: Bird = {
        name: 'sparrow',
        fly() {
          console.log('my is sparrow');
        }
      }
    
      return Math.random() < 0.5 ? fish : bird
    }
    
    let pet = getSmallPet()
    
    if (isFish(pet)) {
      pet.swim()
    } else {
      pet.fly()
    }
    
    const zoo: (Fish | Bird)[] = [getSmallPet(), getSmallPet(), getSmallPet()]
    
    const underWater1: Fish[] = zoo.filter(isFish)
    
    const underWater2: Fish[] = zoo.filter(isFish) as Fish[]
    
    console.log(zoo, underWater1);
    
  • is 关键字经常用来封装”类型保护函数”,通过和函数返回值的比较,从而缩小参数的类型范围,所以类型谓词 is 也是一种类型保护。

2.4 有歧义的联合类型

当接口有多个属性存在不确定时,可能出现歧义。如下:

js
interface Shape {
  kind: 'circle' | 'square',
  radius?: number,
  sideLength?: number
}

function getArea(shape: Shape) {
  switch (shape.kind) {
    case 'circle': return Math.PI * (shape.radius) ** 2 // “shape.radius”可能为“未定义”。
    case 'square': return (shape.sideLength) ** 2 // “shape.sideLength”可能为“未定义”。
  }
}

解决方法:

方法一:真值校验

js
interface Shape {
  kind: 'circle' | 'square',
  radius?: number,
  sideLength?: number
}

function getArea(shape: Shape) {
  switch (shape.kind) {
    case 'circle': return Math.PI * (shape.radius || 0) ** 2 // 对shape.radius进行真值校验
    case 'square': return (shape.sideLength || 0) ** 2 // 对shape.sideLength进行真值校验
  }
}

方法二:接口进行拆分处理

js
interface Circle {
  kind: 'circle',
  radius: number
}

interface Square {
  kind: 'square',
  sideLength: number
}

type Shape = Circle | Square

function getArea(shape: Shape) {
  switch (shape.kind) {
    case 'circle': return Math.PI * shape.radius ** 2
    case 'square': return shape.sideLength ** 2
  }
}

2.5 never类型的穷尽性检查

never类型表示的是那些永不存在的值的类型。

js
interface Circle {
  kind: 'circle'
  radius: number
}

interface Square {
  kind: 'square'
  sideLength: number
}

interface Triangle {
  kind: 'triangle'
  height: number
  width: number
}

type Shape = Circle | Square | Triangle
function assertNever(x: never): never {
  throw new Error("Unexpected object: " + x);
}

function getArea(shape: Shape) {
  switch (shape.kind) {
    case 'circle': return Math.PI * shape.radius ** 2
    case 'square': return shape.sideLength ** 2
    //case 'triangle': return shape.width * shape.height / 2
    default: return assertNever(shape) // 类型“Triangle”的参数不能赋给类型“never”的参数。
  }
}

2.6 函数

2.6.1 基本写法

函数可以声明类型,可以给每个参数添加类型之后再为函数本身添加返回值类型。

js
type GreetFunction = (a: string) => void

function greeter(fn: GreetFunction) {
  fn('Hello World!')
}

function printToConsole(s: string) {
  console.log(s);
}

greeter(printToConsole)

2.6.2 函数签名

使用type声明函数类型。

tsx
type DescribableFunction = {
  description: string
  (someArg: number): boolean
}

function doSomething(fn: DescribableFunction) {
  console.log(fn.description + ' returned ' + fn(5));
}

function fn1(n: number) {
  console.log(n)
  return true
}

fn1.description = 'Hello'

doSomething(fn1)

编译后:

js
function doSomething(fn) {
    console.log(fn.description + ' returned ' + fn(5));
}
function fn1(n) {
    console.log(n);
    return true;
}
fn1.description = 'Hello';
doSomething(fn1);

2.6.3 构造签名

通过new关键字进行构造签名。

tsx
class Ctor {
  s: string
  constructor(s: string) {
    this.s = s
  }
}

interface SomeConstructor {
  new(s: string): Ctor
}

function fn(ctor: SomeConstructor) {
  return new ctor('hello')
}

console.log(fn(Ctor).s); // hello