TS06 TypeScript高级用法

使用泛型+type定义类型函数

使用type+泛型,可以定义类型函数:

type foo<T> = T;
type bar = foo<string>

可以对泛型入参进行约束、设置默认值

// 对入参进行约束
type foo<T extends string> = T;
type bar = foo<string>

// 设定默认值
type foo<T extends string = '123'> = T;
type bar = foo<string>

条件判断

TypeScript中使用extends进行类型判断,与三元运算符很相似:

T extends U ? X : Y;

如果T的类型能够extends U,结果返回X,否则返回Y

结合上面的类型函数,可以进行扩展:

type num = 1;
type str = 'hello world';

type IsNumber<N> = N extends number ? 'yes is a number' : 'no not a number';

type result1 = IsNumber<num>; // 类型为 'yes is a number'
type result2 = IsNumber<str> // 类型为  'no not a number'

遍历联合类型

使用in关键在来遍历type的联合类型

联合类型就是使用|来定义的类型的合集

type Key = 'vue' | 'react';

type Mapped = {[k in Key]: string}; // {vue: string; react: string}

const bar: Mapped = {
  vue: 's',
  react: 's'
}

如果联合类型不是我们显式的定义出来的,那么想要动态的推导出联合类型的类型,需要使用keyof方法

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

type Foo = keyof Person; // 'name' | 'age'

对联合类型进行map操作

可以使用extends+泛型,实现将一组联合类型批量映射为另一种类型:

type Foo = 'a' | 'b';
type UnionTypesMap<T, U> = U;

type Bar = UnionTypesMap<Foo, 5>

全局作用域

使用declare关键字来声明全局作用域:

declare module '*.png';
declare module '*.svg';
declare module '*.jpg';

declare type str = string;
declare interface Foo {
  propA: string;
  propB: number;
}

要注意,如果模块使用了export关键字导出了内容,上述方式可能会失效,需要显式的声明到全局:

declare global {
  const ModuleGlobalFoo: string;
}

注意,上述方式之恩能够用在模块声明内,也就是说代码中必须包含export,否则就会报错:

TS2669: Augmentations for the global scope can only be directly nested in external modules or ambient module declarations.

模块作用域

模块作用域的触发条件之一就是使用export关键字导出内容,在其他模块获取时需要import导入

never类型的作用

never类型代表空集,常用语校验“类型收窄”是否符合预期,常被用来做“类型收底”。

例如,有一个联合类型时:

interface Foo {
  type: 'foo'
}

interface Bar {
  type: 'bar'
}

type All = Foo | Bar

switch判断type,TS是可以收窄类型的(discriminated union):

funcfunction handleValue(val: All) {
  switch (val.type) {
    case 'foo':
      // 这里 val 被收窄为 Foo
      break
    case 'bar':
      // val 在这里是 Bar
      break
    default:
      // val 在这里是 never
      const exhaustiveCheck: never = val
      break
  }
}

default里面把收窄的neverval赋给了显示声明为never的变量。如果有一天type类型被修改了:

type All = Foo | Bar | Baz

如果没有在handleValue添加针对Baz的处理逻辑,这时候在default的分支中就会编译错误。所以通过这个办法可以确保switch总是穷尽了所有All的可能性

never进行类型过滤联合类型

never参与运算时T | never的结果是T,根据这个规则就可以过滤联合类型中不符合期望的类型,TS内置的Exclude泛型操作符就是根据这个原理实现的:

/**
 * Exclude from T those types that are assignable to U
 */
type Exclude<T, U> = T extends U ? never : T;

type A = 1 | 2;
type B = Exclude<A, 1>; // 2

自定义类型守卫

类型守卫(Type Guard)的目的就是为了对类型进行分流,将联合类型分发到不同管道。可以出发类型守卫的常见方式有:typeofinstancofin=====!=!==

function foo(x: A | B) {
  if (x instanceof A) {
    // x is A
  } else {
    // x is B
  }
}

当以上的方式不满足需求时,可以通过is关键字自定义类型守卫:

function isA(x): x is number {
  return true
}

function foo(x: unknown) {
  if(isA(x)) {
    return x
  }
  return null;
}

参考

相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页