Skip to content

条件类型 ​

条件类型指根据extends属性和三目运算符得到不同的类型, 如下所示

typescript
interface DataResult {
  length: number
  result: {
    name: string
    age: number
  }[]
}

function getData(count: number): Promise<DataResult> {
  return Promise.resolve({
    length: count,
    result: [
      {
        name: 't1',
        age: 1,
      },
      {
        name: 't2',
        age: 2,
      }
    ]
  })
}

type FinalResult<T> = T extends undefined ? Promise<DataResult> : void

function handleData<T extends undefined | ((data: DataResult) => void) >(count: number, cb?: T): FinalResult<T> {
  if (cb) {
    getData(count).then(cb)
    return undefined as FinalResult<T>
  } else {
    return getData(count).then(res => res) as FinalResult<T>
  }
}

(async function () {
  const res = await handleData(2)
  console.log(res, 'fully log')
})()
handleData(2, (data: DataResult) => {
  data.result.forEach(item => {
    console.log(item)
  })
})

console.log('no conditional type solution')
// not use conditional type
function handleDataRedo(count: number): Promise<DataResult>
function handleDataRedo(count: number, cb: (data: DataResult) => void): void
function handleDataRedo(count: number, cb?: (data: DataResult) => void): unknown {
  if (cb) {
    getData(count).then(cb)
    return undefined
  } else {
    return getData(count).then(res => res)
  }
}

在上面代码中, FinalResult 就是一个条件类型, 根据泛型的类型不同可能为Promise 类型也可能是void, 其主要的语法就是extends 和 三目运算符.

参考上面代码不同的函数handleData 和 handleDataRedo, 可以看到相比于重载函数handleDataRedo, handleData 使用了条件类型后简单了不少, 但是心理负担可能就大一些了.

扩展

https://juejin.cn/post/7002494139153530917#comment

这是我在网上搜条件类型看到的一个文章, 说的是在条件类型中使用不同的顺序来判断泛型和具体类型之间的继承关系会有不同的结果, 如下所示

typescript
type case1<T = never> = never extends T ? true : false
type case2<T = never> = T extends never ? true : false
type b = case1  // true
type c = case2  // never

这是因为泛型在前会触发Distributive Conditional Types 导致有不同的结果.

其触发条件为

  • 泛型参数必须在extends的左侧,不能在右侧type Distributive<T> = unknown extends T ? [T] : never
  • 必须是naked type。可以理解为裸露的类型,通俗来讲,就是说不能对泛型参数做任何的修饰,必须是单纯的T。比如type Distributive<T> = T | 1 extends unknown ? [T] : never 中的T | 1已经不单纯了。
  • 必须是Union type

具体例子为:

typescript
type A = string | number extends unknown ? [string | number] : never;

上面代码运行后编译成了下面的样子, 更多内容可以看上面链接

typescript
type A = (string extends unknown ? [string] : never) | (number extends unknown ? [number] : never)

extends 的不同定义

在一些语言中其可以解释为名义子类型, 需要显示的声明两者的继承关系, 如下所示

interface TypeB extends TypeA {}

但是在ts中, 其可以解释为结构子类型, 也就是说不需要显式的声明,只需要结构上相似既可, 如下所示

typescript
interface TypeA {
    a: string;
}

interface TypeB {
    a: string;
}

interface TypeC {
  a: string;
  b: string;
}

TypeA 和 TypeB 互为子类型, TypeA 和 TypeB 却不能是TypeC 的子类型, 因为TypeC 的属性多, 子类型至少需要和父类型有相同的属性, 可以多, 不能少.