阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 类型层级与底部类型

类型层级与底部类型

作者:陈川 阅读数:48337人阅读 分类: TypeScript

类型层级与底部类型是TypeScript类型系统中两个关键概念。理解它们有助于构建更健壮的类型约束,特别是在处理复杂类型关系和边界情况时。类型层级决定了类型之间的兼容性规则,而底部类型则代表了类型系统中最底层的类型。

类型层级的基本概念

TypeScript中的类型层级类似于集合论中的包含关系。当类型A可以赋值给类型B时,我们说A是B的子类型,或者说B是A的超类型。这种关系构成了类型系统的骨架。

let a: string = "hello";
let b: String = a; // 有效,因为string是String的子类型

基本类型的层级关系如下:

  • never <: 所有类型
  • 字面量类型 <: 对应的原始类型
  • 原始类型 <: 对应的包装类型
  • 所有类型 <: unknown

顶部类型与底部类型

在类型层级中,unknown是顶部类型(top type),它可以接受任何类型的值。而never是底部类型(bottom type),它不能接受任何值。

function acceptAny(value: unknown) {
  // 可以接受任何参数
}

function rejectAll(value: never) {
  // 不能接受任何实际参数
}

底部类型never的特殊性在于:

  1. 它是所有类型的子类型
  2. 没有值可以赋值给never类型
  3. 函数返回never表示永远不会正常返回

类型兼容性规则

TypeScript使用结构化类型系统,类型兼容性基于类型的结构而非声明。这导致了一些有趣的层级关系:

interface Point {
  x: number;
  y: number;
}

interface Point3D extends Point {
  z: number;
}

let p1: Point = { x: 0, y: 10 };
let p2: Point3D = { x: 0, y: 10, z: 20 };
p1 = p2; // 有效,Point3D是Point的子类型

联合类型与交叉类型的层级

联合类型和交叉类型在类型层级中表现出不同的行为:

type A = { a: number };
type B = { b: string };

// 交叉类型产生子类型
type AB = A & B; // AB <: A, AB <: B

// 联合类型产生超类型
type AOrB = A | B; // A <: AOrB, B <: AOrB

never的实际应用场景

底部类型never在实际开发中有几个重要用途:

  1. 表示不可能出现的分支:
function assertNever(x: never): never {
  throw new Error("Unexpected object: " + x);
}

function handleShape(shape: Circle | Square) {
  switch (shape.kind) {
    case "circle":
      return shape.radius * Math.PI;
    case "square":
      return shape.sideLength ** 2;
    default:
      return assertNever(shape); // 确保所有情况都被处理
  }
}
  1. 过滤联合类型:
type OnlyStrings<T> = T extends string ? T : never;
type Result = OnlyStrings<"a" | "b" | 1 | 2>; // "a" | "b"
  1. 表示永不返回的函数:
function infiniteLoop(): never {
  while (true) {}
}

类型推断中的never

在条件类型推断中,never会表现出特殊行为:

type TryInfer<T> = T extends { a: infer A } ? A : never;

type Test1 = TryInfer<{ a: string }>; // string
type Test2 = TryInfer<{}>; // never

类型层级与条件类型

条件类型利用类型层级来实现复杂的类型操作:

type IsSubtype<T, U> = T extends U ? true : false;

type Test1 = IsSubtype<string, String>; // true
type Test2 = IsSubtype<"literal", string>; // true
type Test3 = IsSubtype<never, unknown>; // true

分布式条件类型中的never

never出现在分布式条件类型中时,它会被自动过滤:

type ToArray<T> = T extends any ? T[] : never;

type Test = ToArray<string | number>; // string[] | number[]
type TestNever = ToArray<never>; // never

类型层级与映射类型

映射类型处理never时也有特殊行为:

type FilterProperties<T, U> = {
  [K in keyof T as T[K] extends U ? K : never]: T[K]
};

interface Example {
  name: string;
  age: number;
  flag: boolean;
}

type StringProps = FilterProperties<Example, string>; // { name: string }

类型谓词中的never

在用户定义的类型保护中,never可以用来表示不可能的情况:

function isStringArray(value: unknown): value is string[] {
  return Array.isArray(value) && value.every(item => typeof item === "string");
}

function processValue(value: string[] | number[]) {
  if (isStringArray(value)) {
    // value是string[]
  } else {
    // value是number[]
    // 如果isStringArray类型谓词写得不正确,这里可能残留never类型
  }
}

类型参数约束中的never

never可以用作类型参数约束来表示"不允许任何类型":

function forbidden<T extends never>() {}

forbidden(); // 错误:需要类型参数
forbidden<string>(); // 错误:string不满足never约束

递归类型中的never

在处理递归类型时,never常作为终止条件:

type DeepReadonly<T> = T extends object
  ? { readonly [K in keyof T]: DeepReadonly<T[K]> }
  : T;

type Test = DeepReadonly<{ a: { b: number } }>;
// { readonly a: { readonly b: number } }

类型层级与函数类型

函数参数类型是逆变的,而返回类型是协变的:

type Func = (arg: string) => void;

let func1: Func = (arg: string) => {};
let func2: Func = (arg: "literal") => {}; // 错误
let func3: Func = (arg: unknown) => {}; // 有效

类型层级与类继承

类继承关系也遵循类型层级规则:

class Animal {
  move() {}
}

class Dog extends Animal {
  bark() {}
}

let animal: Animal = new Dog(); // 有效
// animal.bark(); // 错误:Animal类型上没有bark方法

类型断言与层级

类型断言可以绕过类型检查,但需要满足一定的层级关系:

let value = "hello" as unknown as number; // 不推荐,但技术上可行

类型层级与模板字面量类型

模板字面量类型也参与类型层级:

type Color = "red" | "green" | "blue";
type UpperColor = Uppercase<Color>; // "RED" | "GREEN" | "BLUE"

let color: Color = "red";
let upperColor: UpperColor = "RED";
// color = upperColor; // 错误
// upperColor = color; // 错误

类型层级与索引访问

通过索引访问类型时,never会出现在不存在的属性上:

type Example = { a: string; b: number };
type A = Example["a"]; // string
type C = Example["c"]; // 错误:属性"c"不存在
type All = Example[keyof Example]; // string | number

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌