前端开发

TypeScript基础之--日常类型

介绍 TypeScript 代码中最常见的几种值类型,并解释如何在 TypeScript 中描述这些类型

• 9分钟

Contents

Primitives Types

// The inferred return type is void
function noop() {
  return
}

function noReturn() {}

Arrays

type NumberArrayT = number[]
type StrArrayT = string[]

请注意 [number] 是不同的事物;参考元组部分。

Any

any 类型在你不想为了说服 TypeScript 某行代码是正确的而写出冗长类型时很有用。一般在项目中少用

Type Annotations on Variables

let str: string = '1'
let num: number = 1

不过,在大多数情况下,这并非必需的。TypeScript 尽可能自动推断代码中的类型。例如,变量的类型是根据其初始值的类型推断的:

const username = ref('Yeeee')
const nickname = ref<string>()

Functions

函数是 JavaScript 中传递数据的主要方式。TypeScript 允许你指定函数输入和输出值的类型。

Parameter Type Annotations

function greet(name: string) {
  console.log('Hello, ' + name.toUpperCase() + '!!')
}

Return Type Annotations

function getFavoriteNumber(): number {
  return 26
}

Functions Which Return Promises

async function getFavoriteNumber(): Promise<number> {
  return 26
}

Anonymous Functions

匿名函数与函数声明略有不同。当函数出现在 TypeScript 可以确定其调用方式的场合时,该函数的参数会自动被赋予类型。

let strList = ['1', '2', '3']
strList.forEach((item) => console.log(item.toUpperCase()))

在ts文件中,item将自动被推断为string类型

Object Types

在开发中,除了原始类型外常见的便是对象类型,要定义一个对象类型,我们只需列出其属性及其类型。

interface Person {
  name: string
  age: number
}

Optional Properties

对象类型也可以指定其某些或所有属性是可选的。要这样做,在属性名后添加 ? :

interface Person {
  name: string
  age: number
  address?: string
}

Union Types

提供匹配联合类型的值很简单——只需提供一个与联合类型中的任何成员匹配的类型。

Defining a Union Type

type TMixin =
  | {
      name: string
      age: number
    }
  | string
  | number

const a: TMixin = '1'
const b: TMixin = 1
const c: TMixin = {
  name: '1',
  age: 1
}

Working with Union Types 处理联合类型

function printMixinType(payload: TMixin) {
  console.log('payload', payload)
  if (typeof payload === 'object') {
    console.log('payload is object', payload)
  } else if (typeof payload === 'string') {
    console.log('payload is string', payload)
  } else if (typeof payload === 'number') {
    console.log('payload is number', payload)
  }
}

Type Aliases 类型别名

通常我们希望多次使用相同的类型,并用一个名称来引用它,从而实现重复使用。可以使用类型别名为任何类型命名,而不仅仅是对象类型。 可以是基础类型,也可以是联合类型,还可以是对象类型。

type TbooleanStrNum = boolean | string | number

function inputTypeAliases(t: TbooleanStrNum) {
  console.log('t', t)
}

type TComplex =
  | TbooleanStrNum
  | {
      name: string
      age: number
    }

type TComplex =
  | TbooleanStrNum
  | {
      name: string
      age: number
    }

function inputComplexType(t: TComplex) {
  console.log('t', t)
}

inputTypeAliases(true)
inputTypeAliases('1')
inputTypeAliases(1)

inputComplexType({
  name: '1',
  age: 1
})

Interfaces

接口声明是另一种命名对象类型的方式

interface Point {
  x: number
  y: number
}

function printCoord(pt: Point) {
  console.log("The coordinate's x value is " + pt.x)
  console.log("The coordinate's y value is " + pt.y)
}

printCoord({ x: 100, y: 100 })

就像我们上面使用类型别名一样,这个例子也像我们使用了匿名对象类型一样工作。TypeScript 只关心我们传递给 printCoord 的值的结构——它只关心它具有预期的属性。TypeScript 只关心类型的结构和能力,这就是为什么我们将它称为结构化类型系统。

Differences Between Type Aliases and Interfaces

类型别名和接口非常相似,在许多情况下你可以自由选择它们。 interface 的几乎所有特性在 type 中都可用,关键的区别在于类型不能重新打开以添加新属性,而接口总是可以扩展的。

Differences Between Type Aliases and Interfaces

Type Assertions

有时你会对某个值的类型有信息,而 TypeScript 无法知道。这个时候可以使用断言来明确告诉typescript更具体的类型。但是在实际使用过程中,我们尽量使用 Narrowing类型缩小来完成具体类型的处理。

const myCanvas = document.getElementById('main_canvas') as HTMLCanvasElement

// const myCanvas = <HTMLCanvasElement>document.getElementById("main_canvas"); // valid

// const myCanvas: HTMLCanvasElement = document.getElementById("main_canvas"); // invalid

TypeScript 只允许将类型转换为更具体或更不具体的版本。这条规则防止了“不可能”的类型转换,例如:

const x = 'hello' as number

// Conversion of type 'string' to type 'number' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.

Literal Types

除了通用的类型 string 和 number 之外,我们还可以在类型位置引用特定的字符串和数字。比如说后端定义的枚举值,类型,状态等

type TStatus = 'pending' | 'runing' | 'completed' | 'archived'
type TNums = 1 | 2 | 4

用处

Literal Inference

declare function handleRequest(url: string, method: 'GET' | 'POST'): void

const req = { url: 'https://example.com', method: 'GET' }
handleRequest(req.url, req.method)
// Argument of type 'string' is not assignable to parameter of type '"GET" | "POST"'.

在上面的例子中 req.method 被推断为 string ,而不是 “GET” 。因为代码可以在 req 创建和 handleRequest 调用之间被评估,这可能会将新的字符串 “GUESS” 赋值给 req.method ,TypeScript 认为这段代码存在错误。

解决错误的方法

  1. 在变量定义处增加类型注释
const req: { url: string; method: 'GET' | 'POST' } = { url: 'https://example.com', method: 'GET' }
  1. 属性断言
// Change 1:
const req = { url: 'https://example.com', method: 'GET' as 'GET' }
  1. 你可以使用 as const 将整个对象转换为类型字面量:
const req = { url: 'https://example.com', method: 'GET' } as const
handleRequest(req.url, req.method)

Enums

枚举是 TypeScript 为 JavaScript 添加的一项功能,它允许描述一个可能是一组命名常量之一的值。与大多数 TypeScript 功能不同,这不是对 JavaScript 的类型级别补充,而是对语言和运行时的补充

Less Common Primitives

bigint

从 ES2020 开始,JavaScript 中有一个用于非常大的整数的原始类型

symbol

JavaScript 中有一个原始类型,可以通过函数 Symbol() 创建一个全局唯一的引用:

const firstName = Symbol("name");
const secondName = Symbol("name");

if (firstName === secondName) {
// This comparison appears to be unintentional because the types 'typeof firstName' and 'typeof secondName' have no overlap.
//   // Can't ever happen
// }

参考文档