# tTypeScript

# 基础类型

// 常见原始类型: string,number,boolean,undefined,null,symbol
let var1: string; // 类型注解

// 编译器类型推断可省略这个语法
let var2 = true;

// 类型数组
let arr: string[];

// 函数中的类型约束
// 函数中的类型约束
function greet(person: string): string {
    return 'hello, ' + person;
}

// void类型,常用于没有返回值的函数
function warn(): void {}

# 类型注释

interface IProps {
    /**
     * logo的地址
     */
    logo?: string
    className?: string
    alt?: string
}
// 在使用这样的注释以后,同事在查询这个参数时就能看到这段注释

# 类型断言

let strLength: number = (<string>someValue).length;
let strLength: number = (someValue as string).length;
//上面两种都是类型断言写法

# 非空断言

function addFeatur(e: KeyboardEvent) {
  const inp = e.taget as HTMLInputElement;
  const inp = e.taget!;
}

# 类型别名

// 可以用下面这样方式定义对象类型
const objType: { foo: string; bar: string };

// 使用type定义类型别名,使用更便捷,还能复用
type Foobar = { foo: string; bar: string };

# 联合类型

let features: Features[] | null;

# 交叉类型

type First = { first: number };
type Secord = { secord: string };
type third = First & Secord;

为了分离类型,比如前端类型和后端返回的类型应该分开定义后合并

# 合起来,如果有同 key,类型不一样,怎么处理的?

# 重载

以函数参数数量或者类型,或者返回值的类型区分多个同名函数

// 重载:以函数参数数量或者类型,或者返回值的类型区分多个同名函数
// 先声明,再实现
// 重载1
function watch(cb1: () => void): void;
// 重载2
function watch(cb1: () => void, cb2: (v1: any, v2: any) => void): void;
// 实现
function watch(cb1: () => void, cb2?: (v1: any, v2: any) => void) {
  if (cb2) {
    console.log("执行重载2");
  } else {
    console.log("执行重载1");
  }
}

watch();

Reduxcompose (opens new window)就是运用大量函数重载的典型案例

image-20210428110817173

# 声明文件

// shims-vue.d.ts
import Vue from "vue";
import { AxiosInstance } from "axios";
declare module "vue/types/vue" {
    interface Vue {
        $axios: AxiosInstance;
    }
}

# Class

// 03-class.ts
class Parent {
  private _foo = "foo"; // 私有属性,不能在类的外部访问
  protected bar = "bar"; // 保护属性,可以在子类中访问
  // 参数属性:构造函数参数加修饰符,能够定义为成员属性
  constructor(public tua = "tua") {}
  // 方法也有修饰符
  private someMethod() {}
  // 存取器:属性方式访问,可添加额外逻辑,控制读写性
  get foo() {
    return this._foo;
  }
  set foo(val) {
    this._foo = val;
  }
}

# 枚举类型

# 数字枚举

当我们声明一个枚举类型是,虽然没有给它们赋值,但是它们的值其实是默认的数字类型,而且默认从0开始依次累加:

enum Direction {
    Up = 10,
    Down,
    Left,
    Right
}

console.log(Direction.Up, Direction.Down, Direction.Left, Direction.Right); // 10 11 12 13

# 异构 | 字符串枚举

enum BooleanLikeHeterogeneousEnum {
    No = 0,
    Yes = "YES",
}

# 接口(interface)

interface User {
    name?: string // 可选属性
    age: number  // 必填属性
    readonly isMale: boolean  // 只读属性
    say: (words: string) => string  // 函数类型
    [propName: string]: ;  // 字符串索引
}

# 继承

// 可同时继承多个接口
interface VIPUser extends User, SupperUser {
    broadcast: () => void
}

# 泛型

# 使用时机:

当你的函数,接口或者类:

  • 需要作用到很多类型的时候,
  • 需要被用到很多地方的时候,

# 泛型使用

可以作为一个动态参数传入,使类型使用更加灵活

interface Result<T> {
  ok: 0 | 1;
  data: T[];
}

function getResult<T>(data: T): Result<T> {
  return { ok: 1, data: [data] };
}

getResult<string>("1");

# keyof

keyof 操作符可以用来一个对象中的所有 key 值:

interface Person {
  name: string;
  age: number;
}

type K1 = keyof Person; // "name" | "age"
type K2 = keyof Person[]; // "length" | "toString" | "pop" | "push" | "concat" | "join"
type K3 = keyof { [x: string]: Person }; // string | number

# in

in 用来遍历枚举类型:

type Keys = "a" | "b" | "c"

type Obj =  {
  [p in Keys]: any
} // -> { a: any, b: any, c: any }

# TS 泛型工具 (目前 16 种)

点击查看官方实现 (opens new window)

功能是将类型的属性**「变成可选」**。

type Partial<T> = { [P in keyof T]?: T[P] };

功能是将类型的属性**「变成只读」**, 在属性前面增加 readonly 意思会将其变成只读。

type Readonly<T> = { readonly [P in keyof T]: T[P] };

功能是用来得到一个函数的返回值类型。

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

功能和Partial 相反,是将类型的属性**「变成必填」**, 这里的 -指的是去除。 -?意思就是去除可选,也就是必填啦。

type Required<T> = { [P in keyof T]-?: T[P] };

# 设计工具类型

# diff

Diff<T, U>,我们要找出T类型中U不包含的部分:

type R = Diff<"a" | "b" | "c" | "d", "a" | "c" | "f">;  // "b" | "d"
type Diff<T, U> = T extends U ? never : T;

# Part

现在需要编写一个工具类型将interface中函数类型名称取出来,

interface Part {
    id: number;
    name: string;
    subparts: Part[];
    updatePart(newName: string): void;
}

type R = FunctionPropertyNames<Part>; // "updatePart"

# TypeScript 装饰器

# 装饰器是什么

  • 它是一个表达式
  • 该表达式被执行后,返回一个函数
  • 函数的入参分别为 target、name 和 descriptor
  • 执行该函数后,可能返回 descriptor 对象,用于配置 target 对象

# 装饰器的分类

  • 类装饰器(Class decorators)
  • 属性装饰器(Property decorators)
  • 方法装饰器(Method decorators)
  • 参数装饰器(Parameter decorators)

tsconfig.json:

{
  "compilerOptions": {
     "target": "ES5",
     "experimentalDecorators": true
   }
}

# 装饰器使用

# 类装饰器

function Greeter(greeting: string) {
  return function(target: Function) {
    target.prototype.greet = function(): void {
      console.log(greeting);
    };
  };
}

@Greeter("Hello TS!")
class Greeting {
  constructor() {
    // 内部实现
  }
}

let myGreeting = new Greeting();
myGreeting.greet(); // console output: 'Hello TS!';

# 方法装饰器

它接收三个参数:

  • target: Object - 被装饰的类
  • propertyKey: string | symbol - 方法名
  • descriptor: TypePropertyDescript - 属性描述符

# React中的运用

对于 input 组件 onChange 中的事件,我们一般是这样声明的:

private updateValue(e: React.ChangeEvent<HTMLInputElement>) {
    this.setState({ itemText: e.target.value })
}
// form表单 React.FormEvent<HTMLFormElement>

# compilerOptions 选项

{
  "compilerOptions": {

    /* 基本选项 */
    "target": "es5",                       // 指定 ECMAScript 目标版本: 'ES3' (default), 'ES5', 'ES6'/'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'
    "module": "commonjs",                  // 指定使用模块: 'commonjs', 'amd', 'system', 'umd' or 'es2015'
    "lib": [],                             // 指定要包含在编译中的库文件
    "allowJs": true,                       // 允许编译 javascript 文件
    "checkJs": true,                       // 报告 javascript 文件中的错误
    "jsx": "preserve",                     // 指定 jsx 代码的生成: 'preserve', 'react-native', or 'react'
    "declaration": true,                   // 生成相应的 '.d.ts' 文件
    "sourceMap": true,                     // 生成相应的 '.map' 文件
    "outFile": "./",                       // 将输出文件合并为一个文件
    "outDir": "./",                        // 指定输出目录
    "rootDir": "./",                       // 用来控制输出目录结构 --outDir.
    "removeComments": true,                // 删除编译后的所有的注释
    "noEmit": true,                        // 不生成输出文件
    "importHelpers": true,                 // 从 tslib 导入辅助工具函数
    "isolatedModules": true,               // 将每个文件做为单独的模块 (与 'ts.transpileModule' 类似).

    /* 严格的类型检查选项 */
    "strict": true,                        // 启用所有严格类型检查选项
    "noImplicitAny": true,                 // 在表达式和声明上有隐含的 any类型时报错
    "strictNullChecks": true,              // 启用严格的 null 检查
    "noImplicitThis": true,                // 当 this 表达式值为 any 类型的时候,生成一个错误
    "alwaysStrict": true,                  // 以严格模式检查每个模块,并在每个文件里加入 'use strict'

    /* 额外的检查 */
    "noUnusedLocals": true,                // 有未使用的变量时,抛出错误
    "noUnusedParameters": true,            // 有未使用的参数时,抛出错误
    "noImplicitReturns": true,             // 并不是所有函数里的代码都有返回值时,抛出错误
    "noFallthroughCasesInSwitch": true,    // 报告 switch 语句的 fallthrough 错误。(即,不允许 switch 的 case 语句贯穿)

    /* 模块解析选项 */
    "moduleResolution": "node",            // 选择模块解析策略: 'node' (Node.js) or 'classic' (TypeScript pre-1.6)
    "baseUrl": "./",                       // 用于解析非相对模块名称的基目录
    "paths": {},                           // 模块名到基于 baseUrl 的路径映射的列表
    "rootDirs": [],                        // 根文件夹列表,其组合内容表示项目运行时的结构内容
    "typeRoots": [],                       // 包含类型声明的文件列表
    "types": [],                           // 需要包含的类型声明文件名列表
    "allowSyntheticDefaultImports": true,  // 允许从没有设置默认导出的模块中默认导入。

    /* Source Map Options */
    "sourceRoot": "./",                    // 指定调试器应该找到 TypeScript 文件而不是源文件的位置
    "mapRoot": "./",                       // 指定调试器应该找到映射文件而不是生成文件的位置
    "inlineSourceMap": true,               // 生成单个 soucemaps 文件,而不是将 sourcemaps 生成不同的文件
    "inlineSources": true,                 // 将代码与 sourcemaps 生成到一个文件中,要求同时设置了 --inlineSourceMap 或 --sourceMap 属性

    /* 其他选项 */
    "experimentalDecorators": true,        // 启用装饰器
    "emitDecoratorMetadata": true          // 为装饰器提供元数据的支持
  }
}
Last Updated: 12/22/2022, 9:53:26 AM