TypeScript入门|青训营笔记

52 阅读3分钟

这是我参与「第四届青训营 」笔记创作活动的第3天。

What & Why is TypeScript

TypeScript 的特点

  • 可读性增强:基于语法解析 TSDoc(自动编写文档),ide 增强。
  • 可维护性增强:可以在编译阶段暴露非常多错误。
  • 兼容所有 JS 特性。
  • 支持渐进式引入与升级,可以让2020年前的 Javascript 库与 TypeScript 项目共存。

TypeScript 的基本语法

基础数据类型

  • string
  • number
  • boolean
  • null
  • undefined

对象类型

 interface IBytedancer {
   readonly jobid: number;
   name: string;
   sex: 'man' | 'woman' | 'other';
   age: number;
   hobby?: string; // 可选属性
   [key: string]: any; // 任意属性
 }
 ​
 ​
 const bytedancer: IBytedancer = {
   jobId: 12345,
   name: 'hello world'
 }

函数类型

 function add(x: number, y: number): number: {
   return x + y;
 }
 ​
 const mult: (x: number, y: number) => number = (x, y) => x * y;
 ​
 interface IMult {
   (x: number, y: number): number;
 }
 ​
 const mult: IMult = (x, y) => x * y;

函数重载

 function getDate(type: 'string', timestamp?: string): string;
 function getDate(type: 'date', timestamp?: string): Date;
 function getDate(type: 'string' | 'date', timestamp?: string): Date | string {
   const date = new Date(timestamp);
   return type === 'string' ? date.toLocaleString() : date;
 }
 ​
 const x = getDate('date'); // x: Date
 const y = getDate('string', '2018-01-10'); // y: string

可以通过 Interface 简化 / 复用函数类型声明

 interface IGetDate {
   (type: 'string', timestamp?: string): string;
   (type: 'date', timestamp?: string): Date;
   (type: 'string' | 'date', timestamp?: string): Date | string;
 }
 ​
 const getDate2: IGetDate = (type, timestamp) => {
   const date = new Date(timestamp);
   return type === 'string' ? date.toLocaleString() : date;
 }

上方的类型推断会报错,需要将 IGetDate 返回值的类型范围扩大。

数组类型

 type IArr1 = number[];
 type IArr2 = Array<string | number | Record<string, number>>;
 type IArr3 = [number, number, string, string];
 interface IArr4 {
   [key: number]: any;
 }
 ​
 const arr1: IArr1 = [1,2,3,4];
 const arr2: IArr2 = [1,2,'12',{a:1}];
 const arr3: IArr3 = [1,2,'3','4'];
 const arr4: IArr4 = [1,2,3,4];

交叉/联合类型

利用|&将类型进行联合、交叉。

  • |:可以任选一个类型
  • &:同时需要满足两个类型

联合类型时,只有交集部分可以访问。

解决方式:定义一个守卫类型。

类型守卫

 interface IA { a: 1, a1: 2 }
 interface IB { b: 1, b1: 2 }
 ​
 function getIsIA(arg: IA | IB): arg is IA {
   return !!(arg as IA).a;
 }
 ​
 function log2(arg: IA | IB) {
   // 此时联合类型没有任何交集,无法访问任何元素
   if (getIsIA(arg)) { // 此处直接使用 arg.a 来判断会报错
     // 此时 typescript 推断 arg 为 IA 类型
     console.log(arg.a1);
   } else {
     // 此时 typescript 推断 arg 为 IB 类型
     console.log(arg.b1);
   }
 }

简化写法

 function reverse(target: string | Array<any>) {
   if (typeof target === 'string') {
     return target.split('').reverse().join('')
   }
   if (target instanceof Object) { // 剔除了 string 类型
     // typescript 可以自动识别出符合的类型
     return target.reverse();
   }
 }

类型保护

 type IBookItem = {
   author: string;
 } & ({
   type: 'history';
   range: string;
 } | {
   type: 'story';
   theme: string;
 })
 ​
 // or
 interface IHistoryBook {
   author: string;
   type: 'history';
   range: string;
 }
 ​
 interface IStoryBook {
   author: string;
   type: 'story';
   theme: string;
 }
 ​
 type IBookItem = IHistoryBook | IStoryBook;
 ​
 function logBook(book: IBookItem) {
   if (book.type === 'history') {
     console.log(book.range);
   } else {
     console.log(book.theme);
   }
 }

高级类型

function merge1(sourceObj, targetObj) {
  const result = { ...sourceObj };
  for (let key in targetObj) {
    const itemVal = sourceObj[key];
    itemVal && ( result[key] = itemVal );
  }
  return result;
}

function merge2(sourceObj, targetObj) {
  return { ...sourceObj, ...targetObj };
}


// 缺点
// 1. 实现比较繁琐:所有类型需要写两遍
// 2. 修改比较繁琐:所有类型需要改两遍
// 3. 不完善:无法处理 any 类型。
interface ISourceObj {
  x?: string;
  y?: string;
}

interface ITargetObj {
  x: string;
  y: string;
}

type IMerge = 
	(sourceObj: ISourceObj, targetObj: ITargetObj) => ITargetObj;


interface IMerge {
  <T extends Record<string, any>>
    (sourceObj: Partial<T>, targetObj: T): T;
}
// `T extends Record<string, any>` 
// 限定 T 类型需要是一个 string 为 key 的 object。

type IPartial<T extends Record<string, any>> = {
  // T 是一个 key 永远为 string,value 任意的 object
  [P in keyof T]?: T[P];
  // `keyof T` 指的是 T 中的所有 key 联合起来的字面量
  // 如 `keyof { a: string; b: number }` 即为 `'a' | 'b'`
  // [ P in keyof T ] 指的是 P 是 T 的任意一个 key ('a' 或 'b')
  // [ P in keyof T ]: T[P] 指 P 作为 key,对应 T 里面的 value 类型。
  // [ P in keyof T ]?: T[P] 指不需要所有 key,任意选择一些即可
}

// Partial 已经是内置类型了

函数的返回值

function delayCall(func) {
  return new Promise(resolve => {
    setTimeout(() => {
      const result = func();
      resolve(result);
    }, 1000);
  });
} // 1000ms 之后调用 func 函数

// 定义类型
type IDelayCall = <T extends () => any>(func: T) => IReturnType<T>;

type IReturnType<T extends (...args: any) => any> = T extends (...args:any) => infer R ? R : any;

工程应用

浏览器 Web 应用

主要使用 webpack:

  1. 配置webpack loader,将 typescript 转化为 javascript 文件。
  2. tsconfig.js 加强对代码的约束。
  3. 运行 webpack 进行打包。

Node 应用

使用 tsc 编译运行:

  1. node 环境。
  2. tsconfig.js 文件配置。
  3. tsc 运行编译得到 .js 文件。