typescript 高级

1 类型推断

从右向左流动:

// let a: number 类型从右向左流动
  let a = 1;

通过return关键字推断返回值的类型 底部流出

 function add(a: number) {
    return a;
  }

从左向右:

 //从左向右
  type S = (a: number) => void;
  const s: S = (a) => {
    // parameter) a: number
    console.log(a);
  };

mixin:

 // mixin
  function mixin<T, U>(one: T, two: U) {
    const result = <T & U>{} // result需要满足T又要满足U
    for (let key in one) {
      //   不能将类型“T”分配给类型“T & U”。单个无法赋值联合,需要先转换
      //result[key] = one[key]
      (result as T)[key] = one[key]; //这里赋值T类型需要先断言result为T类型才能赋值。
    }
    for (let key in two) {
      // result[key] = two[key]
      (result as U)[key] = two[key];
    }
    return result;

    return { ...one, ...two }; // T & U
  }
const x = mixin({ name: "1" }, { age: 2 });
console.log(x.age, x.name);
2 其他内容
索引访问操作符
interface A {
    a: string;
    b: number;
  }

  // const a: string
  const a: A["a"] = "1";
映射类型
interface A {
    a: string;
    b: number;
  }
 
// 映射类型
  type ACOPY = {
    [key in keyof A]: A[key]; // 遍历A,然后值为A[key],相当于复制一个A
  };
  type ACOPY1 = {
    [key in keyof A]?: A[key]; // Partial的源码
  };
  type ACOPY2 = Partial<A>;
条件类型
type B<T> = T extends object ? string : number
let b: B<true> // number
let c: B<{}>  // string
分发:
let d: B<{}|true> // string | number // 满足string | number即可。

找到T中不包含U的部分

  type Diff<T, U> = T extends U ? never : T;
// type R = never|never|'c'
  type R = Diff<"a" | "b" | "c", "a" | "b">; // a是a|b的子类, b是a|b的子类,他们都会匹配never,而c则不会,
4 内置条件类型
Exclude<T,U>

从T中排除U

 type Exclude<T,U>  = T extends U ? never : T // Diff
Extract<T,U>

从T中取跟U一样的

 type Extract<T,U> = T extends U ? T : never
infer

推断, infer 的作用是让 TypeScript 自己推断,并将推断的结果存储到一个类型变量中,infer 只能用于 extends 语句中。

如ReturnType

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

如果T满足与函数,那么就推断函数的返回值赋值给R,然后返回R。

 function test(a: string, b: number) {
    return { a: 1 };
  }
 // type Test1 = {   a: number; }
  type Test1 = ReturnType<typeof test>;

再如参数: Paramters

 type Paramters<T extends (...args: any) => any> = T extends (
    ...args: infer P
  ) => any
    ? P
    : any;

如果T满足与函数,就推断参数的类型赋值给P,然后返回P , 返回值是一个元组,否则返回any

type Test2 = Paramters<typeof test>;
 // type Test2 = [a: string, b: number]
元组转联合
 type ElementOf<T> = T extends Array<infer P> ? P : never; // T是否是数组的子类,是的话推断出类型返回
 type Ttuple = [string | number];
 // string | number
 type Test3 = ElementOf<Ttuple>;
5 内置的工具类型
Partial变为可选 (+?)
  type Partial<T> = {
    [key in keyof T]+?: T[key]; //+? 变成可选项
  };

深度变为可选:

 interface Test1 {
    a: 1;
    b: 2;
  }
  type test1 = Partial<Test1>;

  // 深度变为可选
  interface Test2 {
    a: 1;
    b: 2;
    c: Test1;
  }
  type test22 = Partial<Test2>;
  const test2: test22 = {
    // 类型“{}”缺少类型“Test1”中的以下属性: a, b
    c: {},
  };

如上,c一旦存在,他的对象里面的属性是必选的。所以

type DeepPartial<T> = {
    [key in keyof T]+?: T[key] extends object ? DeepPartial<T[key]> : T[key]; //递归调用
  };
// 每次都需要判断是否是一个对象,是的话继续调用DeepPartial
 type test33 = DeepPartial<Test2>;
  const test3: test33 = {
    c: {},
  };

Require 可选变必选
// 可选变必选,并且只读
  type Require<T> = {
    readonly [key in keyof T]-?: T[key]; // -? 变成必选项
  };
  interface Test4 {
    name?: 1;
    a: 2;
  }
  type test4 = Require<Test4>;
PIck只要某个属性
  type Pick<T, K extends keyof T> = {
    // 第二个选项必须是 keyof T的子类
    [key in K]: T[key];
  };
Omit过滤某个属性

Omit,过滤某个属性,反其道而行,取T中不包含K的就行

 type FilterK<T, K> = Exclude<keyof T, K>; //取T中不包含K的属性
 type Omit<T, K extends keyof T> = Pick<T, FilterK<T, K>>;  // 取T中不包含K的
从T中过滤TU两个对象相同的属性
type Diff<T extends object, U extends object> = Pick<T, Exclude<keyof T, keyof U>>

排除T中跟U一样的属性,获取剩余的属性,再使用Pick获取。

从T中找出TU相同的属性
type InterSection<T extends object, U extends object> = Pick<
    T,
    Extract<keyof T, keyof U> & Extract<keyof U, keyof T>
  >;

取T中有U的,交上U中有T的属性,然后通过pick在T里面获取。

Record
type Record<K extends keyof any, T> = {
    // keyof的值只有三个  K extends string | number | symbol
    [P in K]: T;
  };

经常使用的代表对象的就是

 type A = Record<"a" | "b", any>;   //type A = { a: any;  b: any; }
 type B = Record<string, any> ;   // type B = {[x: string]: any;}
6自定义高级类型
Proxy 代理
 type Proxy<T> = {
    get(): T;
    set(v: T): void;
  };

  type Proxify<T> = {
    [key in keyof T]: Proxy<T[key]>;  //通过T【key】获取值,然后继续取Proxy<T>
  };

 // Proxy  代理
  function proxify<T>(obj: T): Proxify<T> {
    let result = <Proxify<T>>{};
    for (const key in obj) {
      type KeyType = typeof key;
      result[key] = {
        get: () => obj[key],
        set: (v: T[KeyType]) => (obj[key] = v),
      };
    }
    return result;
  }

  const props = {
    name: "a",
    a: 10,
  };
  type Props = typeof props;
  const newPorps = proxify<Props>(props);
  newPorps.a.get(); // a: Proxy<number>
  newPorps.name.set("2"); // name: Proxy<string>

反代理:从Proxify变为T

// 反代理
  function unProxify<T>(t: Proxify<T>): T {
    let result = <T>{};
    for (const key in t) {
      result[key] = t[key].get();
    }
    return result;
  }
//  unProps { name: string; a: number; }
  const unProps = unProxify<Props>(newPorps);
  unProps.a; // a: number
  unProps.name; //name: string
OverWrite重写

有个需求,两个类型A,B,B中跟A相同的需要覆盖A的内容,

  type Porps = { a: number; b: number,d: string };
  type newProps = { a: string; b: string; c: number };
  type ReplaceProps = Overwrite<Porps, newProps>
//预期结果是: {a:string, b: string, d: string}

实现思路:

借助上面实现的找两个对象相同的,和找两个对象不同的。

// 从T找出跟U相同的
  type InterSection<T extends object, U extends object> = Pick<
    T,
    Extract<keyof T, keyof U> & Extract<keyof U, keyof T>
  >;
  //从T中找出跟U不同的
  type Diff<T extends object, U extends object> = Pick<
    T,
    Exclude<keyof T, keyof U>
  >;
  type Overwrite<T extends object, U extends object, I = Diff<T,U> & InterSection<U, T> >= Pick<I, keyof I>

这里重点就是第三个泛型I,他无需传值,通过传入的一二泛型获取值。这里的TU顺序很重要。

如第一个Diff<T,U>就是从T里面找出与U不相关的,那就是d: string了。再交上

InterSection<U,T>,从U当中找出与T相关的,就是从newProps找出与Porps相关的,就是a:string, b: string,所以最后得到的结果就是:

 type ReplaceProps = Overwrite<Porps, newProps>; // type ReplaceProps = {a: string,b: string, d: string}
7 模块与命名空间

文件模块:在ts文件的根级别位置含有import 或者 export,那么就会在这个文件创建一个本地的作用域

模块是ts中外部模块的简称,侧重于代码复用

一个模块里的变量函数类等在外部不可见,除非导出。

同个模块相同名字的命名空间会合并。

export namespace zoo {
     // 同个模块内部同样的名字的命名空间会合并 !!!
    export class Dog1{}
}
export namespace zoo {
   export class Dog1{} //报错
}

8类型声明

类型声明可以让我们不需要将js重构为ts,只需要加上声明文件就可以

类型声明在编译的时候会删除

declare关键字表示声明的意思

// 声明命名空间,一个全局变量如果有很多子属性,就可以使用namesqpce。
declare namespace $ {
    function ajax(url: string):void
    let name: string
    namespace fn {
        function extend():void
    }
}
$.fn.extend()
$.ajax('')

自己实现jq的声明文件:

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

对于jq,他的声明文件导出是export = Jquery, 他是ts的语法,表示可能是es导出,也可能是commonjs导出,具体实现看代码实现

而jq是commonjs导出的,这就导致我们需要import * as Jquery from ‘jquery’,转化以下。或者是tsconfig.json中esModuleInterop设为true,

表示转化es6的语法,才可以import Jquery from ‘jquery’

8 扩展类型

class enum既可以作为类型,也可以作为值

interface type只能作为类型,不能作为值

funciton var let const 只能作为值,不能作为类型

合并声明

同一名称的两个独立声明会被合并成一个单一声明

合并后的声明拥有原先两个声明的特性

可以通过接口合并的特性给一个第三方库扩展类型声明

// 使用命名空间扩展类,需要在类定义后面
namespace D {
    class Form {
        username: Form.Item
        test: Form.C
        d: Form.d //报错  //不可以作为类型
    }

namespace Form {
    export type Item = string  // 定义类型
    export class C {}   // 类既可以作为类型,也可以作为值
    export let d : string //值不可以做为类型
}
const test1 = new Form()
const test2 = new Form.C()
Form.Item //不可以作为值
Form.d
}
    
 //  实用,扩展redux的store
namespace G {
    import {ceateStore, Store} from 'redux'
    type StoreExt = Store & {ext: string} // 只能通过type去扩展,不能通过namespace扩展
    const store: StoreExt = ceateStore(state=>state)
    store.ext
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

coderlin_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值