Skip to content

关于 TypeScript 的一些知识点和使用技巧

后缀非空断言符

! 表示。

断定前面的那个值类型不为 null 或 undefined。

ts
const a = {}
a!.b = 123

后缀确定赋值断言符

这个符号也是 !,放在变量和函数后面。

ts
let x: number
fn()
// 会报错:varialbe 'x' is used before being assigned
console.log(x)
function fn() {
  x = 10
}

在 x 后面加个 ! 就不会报错了。

交叉类型与联合类型

&: 将多个类型叠加在一起。

|: 表示多种类型的一种。

类型收窄

  1. in
ts
if("abc" in emp) {}
  1. typeof
ts
if(typeof a === 'string') {}
  1. instanceof
ts
abc instanceof Promise
  1. is
ts
function x(x): x is number {
  return typeof x === 'number'
}

枚举

枚举其实就是对象。只不过用枚举来表示一些常量更有逼格(我瞎说的)。

枚举不是类型。

数字枚举比字符枚举多了反向映射。在 enum 关键字前面加上 const 关键字可以将普通枚举变为常量枚举,这样编译后的代码就不会有反向映射,而是像常量一样。

ts
enum OrderStatus {
  A,
  B = "123"
}

// =>

OrderStatus[OrderStatus["A"] = 0] = "A"
OrderStatus["B"] = "123"

any 与 unknown

any 类型不做类型检查。任何类型都可以 any 类型赋值。

unknown 类型可以任何类型赋值,且被复制后还可以被不同类型赋值。只有 any,unknown 类型能 unknown 类型赋值。

ts
let a: unknown

a = 1
a = "abc"

never 类型

never 类型可以用来实现全面类型检查。平常业务可能用不到 never 类型,但对库开发者可能有用。因为配置参数可能有多种类型。

ts
type Foo = string | number
function fn(foo: Foo) {
  if (typeof foo === string) {
  	 //
  } else if (typeof foo === number) {
  	 //
  } else if (typeof foo === never) {
  	 console.log('配置参数错误!')
  }
}

使用 TS 注释

ts
// 单行 ts 注释
/** ts 注释 */

// 多行 ts 注释
/**
 */

这样鼠标悬浮在类型上会有类型提示。

声明文件

  1. 对于第三方库,TS 我猜测会去它的 package.json 中找到 type 定义的声明文件位置。如果没有,可以在 .d.ts 里自定义模块。
ts
declare module 'antd-dayjs-webpack-plugin' {
  // ...
}
  1. declare 顾名思义,是声明的意思。声明的值全局可获取到。与 export、import 不同它们只能导入后使用。

双重断言

下面的例子是会报错的。

ts
let foo:number = 18
let abc:string = foo as string

可以改为

ts
let foo: number = 19
let abc: string = foo as unknown as string

使用断言时,我们的类型断言必须要与编辑器的有重叠,否则只能用双重类型断言。

比如下面这个例子:

ts
type A = '1' | '2' | '3'
type B = '3' | '4' | '5'

let a!: A
let b!: B

a = b as A
b = a as B

number、Number、string、String 类型区别

前者是基本数据类型,后者是基本数据类型的包装对象。

模块

任何顶级有 export、import 的文件都会被当作模块处理。否则内容被认为全局可见。

关于 declare 的说明

declare 用于声明一个全局变量或全局函数的类型。从而让 TypeScript 编译器知道它的存在。比如我们通过 CDN 方式使用了一个库,这个库提供了一个全局变量 BestHTML5,这时候我们就可以使用 decalare 了。

注意 declare 关键字并不会在编译后的 js 代码中生成相应的代码。

ts
// 声明一个全局变量
declare const BestHTML5: User

// 声明一个全局函数
declare function parseInt(s: string): number

// 声明一个全局命名空间
declare namespace MyNamespace {
  function myFunction(): void
  const a = 1
}

关于 declare module 的说明

declare module 里面使用 export default 代表什么意思?

在 TypeScript 的 declare module 块中使用 export default 表示该模块默认导出一个特定的值或对象。

例如,假设有一个名为 myModule 的模块,其中包含一个默认导出对象,可以在 declare module 中这样声明:

ts
declare module 'myModule' {
  export default {
  	foo: string
  	bar: number
  }
}

这样,当其它 TypeScript 模块导入 myModule 时,可以使用默认导出对象的属性,如下所示:

ts
import myModule from 'myModule'

console.log(myModule.foo)
console.log(myModule.bar)

请注意,declare module 块仅用于声明模块的类型信息,而不是实现模块。因此,它们不会影响生成的 JavaScript 代码。

关于 namespace 的说明

相当于一个作用域,可以避免命名冲突。现在推荐使用 export、import,而不是 namespace。

在 namespace 里使用 export,其它地方就可以使用 export 后的变量。 但我测试发现,不在变量前加 export,其它地方也可以使用 export 后的变量。奇怪。

ts
namespace MyNamespace {
  export function sayHello(name: string) {
  	//
  }
}

其它文件怎么使用这个命名空间呢,除了在 namespace 前加 export 导出然后引入,还可以使用三斜线指令。

在其它地方引入定义了 namespace 的文件:

ts
/// <reference path="./xx.ts" />

注意不要和 import 语句混用。

ts-node 的简单使用

[ts-node](useful/npm, yarn, pnpm/ts-node.md)

babel 与 typescript

擦除类型

粗略地说,一旦 TypeScript 的编译器完成了检查代码的工作,它就会 擦除 类型以生成最终的“已编译”代码。这意味着一旦您的代码被编译,生成的普通 JS 代码便没有类型信息。

typescript compiler 可以将 ts 代码转为对应目标的(es5、es6)js 代码,还可以生成对应的声明文件(测试一下?)。

babel 和 ts 编译器都可以将 ts 文件转为 js 文件。但是有一些不同:

对于 ts 编译器:

  • 在编译到目标 js 代码时,ts 编译器只能转换语法,不会作 polyfill 兼容。
  • ts 编译器转换时会作类型检查。ts 编译器能生成类型声明文件(ts 编译器会扫描全部的文件并分析其类型)。

对于 babel:

  • 不支持常量枚举。即在 enum 关键字前使用 const 修饰,在编译时会被忽略去掉。
  • 不支持 namespace 合并,不支持 namespace 里导出非 const 的值。

总结

  • 如果只想让 ts 编译器做类型检查,但不转换 ts 为 js 文件,可以使用 tsc --noEmit
  • 用 babel 转换 ts 文件为 js 文件也许好一点,因为有 polyfill 兼容支持等等。(关键包:@babel/preset-typescript

eslint 与 typescript

为了让 eslint 解析 typescript 代码,我们需要安装解析器和插件。

@typescript-eslint/parser

@typescript-eslint/eslint-plugin

它们两个的版本必须一致。

然后配置 .eslintrc.json:

json
{
  "parser": "@typescript-eslint/parser",
  "plugins": [
    "@typescript-eslint/eslint-plugin"
  ]
}

tsc --init

会创建 tsconfig.json 文件。

常量枚举

在 enum 前加一个 const。

优点:编译后不会有反向映射的代码,被枚举赋值的变量不会进行查找,而是直接被赋值为一个常量。

三斜线指令

更推荐使用 imoprt 来导入模块。

ts
<reference path="" />
<reference type="" />
<reference lib="" />

接口自动生成 ts 类型

安装插件 Paste JSON as Code。

使用方法一

先将 json 数据复制到剪切板,然后 ctr + shift + p 找到命令:Paste JSON to Types,然后输入接口名称。

使用方法二

打开一个 json 文件,找到命令 open quick type for json。

Released under the MIT License.