封面作者:NOEYEBROW
请先阅读JavaScript学习笔记
简介
TypeScript 是 JavaScript 的一个超集, 支持静态类型检查, 可以在编译时发现并纠正错误
tsc 是 TypeScript 的编译器, 可以将 TypeScript 代码编译成 JavaScript 代码
tsconfig.json 是 TypeScript 的配置文件, 可以使用 tsc --init 命令生成默认配置文件
在后端, Deno、Bun、最新的 Node.js 等环境已经内置了 TypeScript 编译器和配置, 可以直接运行 TypeScript 代码
在前端, Vite 等打包工具会自动执行 TypeScript 编译, 一般不需要手动配置
TypeScript 有类型推断机制, 因此现有的 JavaScript 代码只需少量修改即可迁移到 TypeScript 中
1 2 3 4 5 6 7 8 deno run index.ts bun run index.ts npm i -g typescript tsc index.ts node index.js
类型 TypeScript 可以使用 : 来定义变量的类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 const any : any = 1 const num : number = 1 const str : string = 'hello' const bool : boolean = true const nul : null = null const und : undefined = undefined const void : void = undefined const sym : symbol = Symbol ('symbol' )const big : bigint = 100n const arr : number [] = [1 , 2 , 3 ] const tuple : [string , number ] = ['leaf' , 18 ] const obj : { name : string , age : number } = { name : 'leaf' , age : 18 }const funExp : (a: number , b: number ) => number = (a, b ) => a + bconst voidFun : () => void = () => console .log ('void' )function fun (a: number , b: number ): number { return a + b }const result : 'success' | 'error' = 'success' enum Color { R = 'red' , G = 'green' , B = 'blue' , Y = 'yellow' }const favColor : Color = Color .Y const enum Direction { Up , Down , Left , Right }const dir : Direction = Direction .Up const promise : Promise <number > = new Promise ((resolve, reject ) => { resolve (1 ) })
unknown unknown 类型是 any 类型的安全版本, 可以接受任意类型的值, 但不能直接操作, 需要先进行类型检查
1 2 3 4 5 6 let value : unknown value = 1 if (typeof value === 'number' ) { value += 1 }
never never 类型表示永远不会返回结果的类型, 通常用于抛出异常或无限循环
1 2 3 4 5 6 function throwError (message: string ): never { throw new Error (message) } function infiniteLoop ( ): never { while (true ) {} }
类型断言 TypeScript 可以使用 as 关键字来断言变量的类型; 断言只在编译阶段起作用, 不会影响运行时的类型
1 2 3 4 5 6 7 8 9 const arr : number [] = [0 , 1 , 2 , 3 ]const num = arr.find (n => n > 1 ) as number const absoulteNum : number = arr.find (n => n > 1 ) as number const num = <number >arr.find (n => n > 1 )
非空断言 TypeScript 还提供了非空断言操作符 !, 可以用来断言一个值不为 null 或 undefined
1 2 3 4 const element = document .getElementById ('app' )!element.innerHTML = 'Hello, TypeScript!'
接口 TypeScript 可以使用 interface 或 type 来定义对象的成员及其类型, 二者区别见示例 ; 我一般在定义某个多次使用的对象时使用 type, 定义类、函数 props 时使用 interface
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 interface Person { name : string age : number gender?: 'male' | 'female' | 'other' phone?: string readonly id : number [hobby : string ]: string } const leaf : Person = { name : 'leaf' , age : 18 , id : 1 }function sayHi (person: Person ): void { console .log (`Hi, I'm ${person.name} , I'm ${person.age} years old` ) } sayHi (leaf)
类 TypeScript 中的类和 ES6 中的类基本一致, 但有一些额外的特性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Person { name : string age : number readonly id : number = 1 constructor (name: string , age: number ) { this .name = name this .age = age } sayHi (): void { console .log (`Hi, I'm ${this .name} , I'm ${this .age} years old` ) } }
TypeScript 中的类可以用 public、private、protected 来修饰成员: public: 默认, 所有地方都可以访问, 相当于 ES6 中的类成员private: 只能在类的内部访问, 相当于 ES6 中的 #protected: 只能在类的内部和子类中访问
class 中也可以使用 readonly 修饰成员, 表示只读
类的接口 多个类可能具有一些共同的特性, 但这些特性之间的实现又有一些小差异, 此时用父类可能不太合适, 可以使用接口来抽象这些共同的特性; TypeScript 中, 可以使用 implements 来在类中实现接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 interface Eat { eat (food : string ): void } interface Say { sayHi (): void } class Cat implements Eat { name : string constructor (name: string ) { this .name = name } eat (food : string ): void { console .log (`喵喵喵, ${this .food} , 喵喵喵喵喵喵!` ) } } class Person implements Eat , Say { name : string age : number constructor (name: string , age: number ) { this .name = name this .age = age } eat (food : string ): void { console .log (`${this .name} is eating ${food} ` ) } sayHi (): void { console .log (`Hi, I'm ${this .name} , I'm ${this .age} years old` ) } }
抽象类 抽象类类似于上面的类的接口, 是多个类具有的一些公共方法, 但抽象类可以指定一些方法的实现, 而接口只能定义方法的签名; 抽象类使用 abstract 关键字来定义, 使用 extends 来继承
注意: 抽象类不能被实例化, 只能被继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 abstract class Animal { abstract eat (food : string ): void abstract sayHi (): void sleep (): void { console .log ('sleeping' ) } } class Cat extends Animal { name : string constructor (name: string ) { super () this .name = name } eat (food : string ): void { console .log (`喵喵喵, ${food} , 喵喵喵喵喵喵!` ) } sayHi (): void { console .log (`喵喵喵喵喵!` ) } } const cat = new Cat ('Tom' )cat.eat ('fish' ) cat.sayHi () cat.sleep ()
泛型 泛型指的是在定义函数、接口或类的时候, 不预先指定具体的类型, 而在使用的时候再指定类型的一种特性; 定义函数或类时, 可以使用 <T> 来定义泛型类型, T 可以是任意标识符, 相当于一个额外的参数, 用来接受传入的类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 function createArray<T>(length : number , value : T): T[] { return Array .from ({ length }, () => value) } const arr1 = createArray<string >(3 , 'x' ) const arr2 = createArray<number >(3 , 1 ) const arr3 = createArray (3 , 1 ) function generateArray<T = number >(length : number , value : T): T[] { return Array .from ({ length }, () => value) } class Stack <T = number > { #items : T[] = [] push (item : T): void { this .#items.push (item) } pop (): T | undefined { return this .#items.pop () } }
typeof typeof 关键字可以用来获取变量的类型, 用于约束泛型类型; 不同于 JavaScript 中的 typeof 运算符 (返回字符串), TypeScript 中的 typeof 返回变量的类型
1 2 3 4 function add (a: number , b: number ): number { return a + b } type Add = typeof add
extends extends 关键字可以用来约束泛型的类型, 使泛型必须继承某个类或实现某个接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 type TypeName <T> = T extends string ? 'string' : 'other' const str : TypeName <string > = 'string' const num : TypeName <number > = 'other' type TypeName <T> = T extends string ? 'string' : T extends number ? 'number' : 'other' const str : TypeName <string > = 'string' const num : TypeName <number > = 'number' const other : TypeName <boolean > = 'other' function getLength<T extends { length : number }>(arg : T): number { return arg.length } getLength ('hello' ) getLength ([1 , 2 , 3 ]) getLength ({ length : 5 })
keyof keyof 关键字可以用来获取对象的所有键, 用于约束泛型类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 type Person = { name : string age : number } type PersonKey = keyof Person function getValue<T, K extends keyof T>(obj : T, key : K): T[K] { return obj[key] } const obj = { name : 'leaf' , age : 18 }const name : string = getValue (obj, 'name' )const age : number = getValue (obj, 'age' )
infer infer 关键字可以用来推断泛型类型, 通常用于条件判断
1 2 3 4 5 6 7 8 9 10 11 type ReturnType <T> = T extends (...args : any []) => infer R ? R : any function add (a: number , b: number ): number { return a + b } type AddReturnType = ReturnType <typeof add> type UnwrapPromise <T> = T extends Promise <infer R> ? R : Ttype PromiseReturnType = UnwrapPromise <Promise <number >> type NormalReturnType = UnwrapPromise <number >
类型声明 一些第三方库的函数可能没有提供类型声明文件, 可以使用 declare 关键字来定义类型声明文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { camelCase } from 'lodash' declare function camelCase (input: string ): string import * as _ from 'lodash' declare module 'lodash' { export function camelCase (input: string ): string export function kebabCase (input: string ): string export function snakeCase (input: string ): string export function upperFirst (input: string ): string }
一些常见第三方库已经有了类型声明文件, 可以直接安装即可, 如 npm i @types/node -D(还有些库的声明文件是内置的, 不需要安装, 如下所示)
.d.ts 文件 TypeScript 的类型声明文件通常以 .d.ts 为后缀, 用来声明全局变量、函数、类等
其中 /// <reference types="xxx" /> 用来引入其他类型声明文件, /// <reference path="xxx" /> 用来引入其他文件
函数重载 函数重载指的是为同一个函数提供多个函数类型定义, 以便在不同的情况下使用不同的类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function reverse (x: number ): number function reverse (x: string ): string function reverse (x: number | string ): number | string { if (typeof x === 'number' ) { return Number (x.toString ().split ('' ).reverse ().join ('' )) } else if (typeof x === 'string' ) { return x.split ('' ).reverse ().join ('' ) } } reverse (123 ) reverse ('hello' )
工具类型 TypeScript 内置了一些工具类型, 可以用来操作类型
工具类型
描述
Partial<T>
将类型 T (对象) 中的所有属性设置为可选
Required<T>
将类型 T (对象) 中的所有属性设置为必选
Readonly<T>
将类型 T (对象) 中的所有属性设置为只读
Record<K, T>
可以简化对象的类型定义
Pick<T, K>
从类型 T (对象) 中选取部分属性
Omit<T, K>
从类型 T (对象) 中排除部分属性
Exclude<T, U>
从类型 T (联合类型) 中排除可以赋值给 U 的类型
Extract<T, U>
从类型 T (联合类型) 中提取可以赋值给 U 的类型
NonNullable<T>
从类型 T (联合类型) 中排除 null 和 undefined
ReturnType<T>
获取类型 T (函数) 的返回值类型
Parameters<T>
获取类型 T (函数) 的参数类型 [...]
Awaited<T>
获取类型 T (Promise) 的返回值类型
ConstructorParameters<T>
获取类型 T (类) 的构造函数参数类型 [...]
InstanceType<T>
获取类型 T (类) 的实例类型
还有几个和 this 相关的工具类型, 这里不做介绍
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 type Gender = 'male' | 'female' | 'other' type Person = { name : string , age : number , gender?: Gender }type PartialPerson = Partial <Person >const personA : PartialPerson = { name : 'leaf' }const personB : PartialPerson = {}type RequiredPerson = Required <PartialPerson >const personC : RequiredPerson = { name : 'leaf' , age : 18 , gender : 'male' }type ReadonlyPerson = Readonly <Person >const personD : ReadonlyPerson = { name : 'leaf' , age : 18 }type RecordPerson = Record <'name' | 'age' , string > type PickPerson = Pick <Person , 'name' > type OmitPerson = Omit <Person , 'name' > type TwoGender = Exclude <Gender , 'other' > type ExtractGender = Extract <Gender , 'male' | 'female' > type NonNullablePerson = NonNullable <Person | null | undefined > type AddReturnType = ReturnType <(a: number , b: number ) => number > type AddParameters = Parameters <(a: number , b: number ) => number > type PromiseReturnType = Awaited <Promise <number >> class PersonClass { name : string constructor (name: string ) { this .name = name } } type PersonInstance = InstanceType <typeof PersonClass > const person : PersonInstance = new PersonClass ('leaf' )const person : PersonClass = new PersonClass ('leaf' )type PersonConstructorParameters = ConstructorParameters <typeof PersonClass >
模版字符串 TypeScript 5+ 中可以使用类似于模版字符串的语法来定义字符串类型
工具类型
描述
Uppercase<StringType>
将字符串转换为大写
Lowercase<StringType>
将字符串转换为小写
Capitalize<StringType>
将字符串的首字母转换为大写
Uncapitalize<StringType>
将字符串的首字母转换为小写
1 2 3 4 5 6 7 8 9 10 type HexColor = `#${string } ` type RGBColor = `rgb(${number } , ${number } , ${number } )` type Color = Uppercase <HexColor > | Lowercase <RGBColor >type Colors = { [key : `color-${string } ` ]: Color }const colors : Colors = { 'color-red' : '#FF0000' , 'color-green' : 'rgb(0, 255, 0)' , 'color-blue' : '#0000FF' , }