Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

never 是什么类型,详细讲一下【热度: 667】 #506

Open
yanlele opened this issue Aug 2, 2023 · 0 comments
Open

never 是什么类型,详细讲一下【热度: 667】 #506

yanlele opened this issue Aug 2, 2023 · 0 comments
Labels
TypeScript TypeScript 语法部分
Milestone

Comments

@yanlele
Copy link
Member

yanlele commented Aug 2, 2023

关键词:never类型、never类型应用

never 是其他任意类型的子类型的类型被称为底部类型(bottom type)。

在 TypeScript 中,never 类型便为空类型和底部类型。never 类型的变量无法被赋值,与其他类型求交集为自身,求并集不参与运算。

应用一: 联合类型中的过滤

never在联合类型中会被过滤掉:

type Exclude<T, U> = T extends U ? never : T;

// 相当于: type A = 'a'
type A = Exclude<'x' | 'a', 'x' | 'y' | 'z'>

T | never // 结果为T
T & never // 结果为never

取一个映射类型中所有value为指定类型的key。例如,已知某个React组件的props类型,我需要“知道”(编程意义上)哪些参数是function类型。

interface SomeProps {
    a: string
    b: number
    c: (e: MouseEvent) => void
    d: (e: TouchEvent) => void
}
// 如何得到 'c' | 'd' ? 

type GetKeyByValueType<T, Condition> = {
    [K in keyof T]: T[K] extends Condition ? K : never
} [keyof T];

type FunctionPropNames =  GetKeyByValueType<SomeProps, Function>;    // 'c' | 'd'

运算过程如下:

// 开始
{
    a: string
    b: number
    c: (e: MouseEvent) => void
    d: (e: TouchEvent) => void
}
// 第一步,条件映射
{
    a: never
    b: never
    c: 'c'
    d: 'd'
}
// 第二步,索引取值
never | never | 'c' | 'd'
// never的性质
'c' | 'd'

应用二:防御性编程

举个具体点的例子,当你有一个 union type:

interface Foo {   type: 'foo' } 
interface Bar {   type: 'bar' } 
type All = Foo | Bar

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

function 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 里面我们把被收窄为 never 的 val 赋值给一个显式声明为 never 的变量。如果一切逻辑正确,那么这里应该能够编译通过。但是假如后来有一天你的同事改了 All 的类型:

type All = Foo | Bar | Baz

然而他忘记了在 handleValue 里面加上针对 Baz 的处理逻辑,这个时候在 default branch 里面 val 会被收窄为 Baz,导致无法赋值给 never,产生一个编译错误。所以通过这个办法,你可以确保 handleValue 总是穷尽 (exhaust) 了所有 All 的可能类型。

@yanlele yanlele added the TypeScript TypeScript 语法部分 label Aug 2, 2023
@yanlele yanlele added this to the milestone Aug 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
TypeScript TypeScript 语法部分
Projects
None yet
Development

No branches or pull requests

1 participant