TypeScript学习笔记(六)

TypeScript的泛型

泛型的概念

如果希望实现函数的返回值类型与参数类型一致,可以这样写:

  function fun(arg: string): string {
    return arg
  }

或者这样:

  function fun(arg: number): number {
    return arg
  }

但是这些写法都太局限,不能满足所有情况,这时就可以使用泛型,
泛型就是泛指的类型,用<>进行定义,一般习惯用单个大写字母例如<T>

  function fun<T>(arg: T): T { 
    return arg // 即可实现返回值类型与参数类型一致
  }

泛型数组

泛型数组的两种写法

  // T[] 形式
  function fun<T>(arg: T[]): T[] {
    return arg.reverse()
  }
  fun<string>(['a', 'b', 'c', 'd'])
  // fun(['a', 'b', 'c', 'd'])  泛型也支持类型推断

  // Array<T>
  function func<T>(arg: Array<T>): Array<T> {
    return arg.reverse()
  }
  func<number>([1, 2, 3, 4, 5])

多个泛型定义

泛型可以定义多个,用法和一个类似

  function fun<T, P>(first: T, second: P): [T, P] {
    return [first, second]
  }
  fun<string, number>('string', 123)

泛型中的继承

泛型可以通过继承来进行约束

  interface Key {
    key: number
  }

  function fun<T extends Key>(arg: T): T { // 泛型T继承接口Key,所以参数必须含有key属性
    return {
      ...arg,
      key: new Date().getTime() + arg.key
    }
  }

  fun({
    key: 123,
    name: '测试'
  })

泛型的简单应用

实现一个axios方法,接收url和payload两个参数,根据url的不同,接收参数类型及返回值类型也不同

  interface Todo {
    // Todo类型接口
    id: number;
    name: string;
    done: boolean;
  }

  let todos: Todo[] = [
    // 初始数据
    {
      id: 1,
      name: "代办1",
      done: false,
    },
    {
      id: 2,
      name: "代办2",
      done: true,
    },
  ];

  enum Urls {
    // 获取todo数据的接口,不需要参数,返回值类型为 Todo[]
    TODOS = "/api/todos",
    // 切换todo状态的接口,参数类型为 number,返回值类型 boolean
    TOGGLE = "/api/toggle",
    // 添加todo项的接口,参数类型为 Todo,返回值类型 boolean
    ADD = "/api/add",
  }

  // 定义一个工具类型
  // 根据泛型传入值来返回一个自定义的key
  type Key<U> = U extends Urls.TODOS ? "todos" : 
                U extends Urls.TOGGLE ? "toggle" : 
                U extends Urls.ADD ? "add" : "other";

  // 通过工具类型Key获取对应key,再根据key获取对应的参数类型
  type Payload<P> = {
    todos: any;
    toggle: number;
    add: Todo;
    other: any;
  }[Key<P>];

  // 通过工具类型Key获取对应key,根据key获取对应的结果类型
  type Result<R> = {
    todos: Todo[];
    toggle: boolean;
    add: boolean;
    other: any;
  }[Key<R>];

  function axios<T extends Urls>(
    url: T,
    payload?: Payload<T>
  ): Promise<Result<T>> | never {
    let res;
    switch (url) {
      case Urls.TODOS: // 获取todo数据,不需要参数,返回值为Todo[]类型
        res = todos.slice();
        break;
      case Urls.TOGGLE: // 改变todo状态,参数为number类型,返回值为boolean类型
        const todo = todos.find(({ id }) => id === payload);
        if (todo) {
          todo.done = !todo.done;
        }
        res = true;
        break;
      case Urls.ADD: // 添加todo,参数为Todo类型,返回值为boolean类型
        // 变量后面加!  为了让undefined和null通过编译
        todos.push(payload!);
        res = true;
        break;
      default:
        throw new Error("Unknow api!");
    }
    return Promise.resolve(res as any);
  }

  axios(Urls.ADD, {
    id: 3,
    name: '做笔记',
    done: false
  }) // 添加一条数据
  console.log(axios(Urls.TODOS)) // 获取全部数据
  axios(Urls.TOGGLE, 2) // 切换 id为2 的数据的done状态
posted @ 2021-08-09 11:13  流云如水  阅读(59)  评论(0)    收藏  举报