阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 类型实例化与延迟求值

类型实例化与延迟求值

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

类型实例化与延迟求值

TypeScript的类型系统支持类型实例化与延迟求值,这些特性在处理复杂类型时尤为重要。类型实例化允许基于泛型参数创建具体类型,而延迟求值则确保类型表达式只在需要时计算,避免不必要的性能开销。

类型实例化

类型实例化是TypeScript中泛型的核心机制。当使用泛型时,TypeScript会根据提供的类型参数生成具体的类型实例。这个过程类似于函数调用,但发生在类型层面。

type Box<T> = { value: T };

// 类型实例化
type StringBox = Box<string>;  // { value: string }
type NumberBox = Box<number>;  // { value: number }

类型实例化可以嵌套,形成更复杂的类型结构:

type Pair<T, U> = [T, U];
type BoxedPair<T, U> = Box<Pair<T, U>>;

// 多层实例化
type StringNumberBox = BoxedPair<string, number>;  // { value: [string, number] }

条件类型中的延迟求值

TypeScript的条件类型支持延迟求值,只有在实际使用时才会计算类型表达式。这种特性在处理递归或复杂类型时特别有用。

type IsString<T> = T extends string ? true : false;

// 不会立即计算
type MaybeString = IsString<unknown>;  // 保持为条件类型

// 使用时才计算
type A = MaybeString extends true ? "yes" : "no";  // 计算为"no"

类型别名中的延迟行为

类型别名在定义时不会立即展开,这种延迟行为可以防止无限递归并提高性能。

type Json =
  | string
  | number
  | boolean
  | null
  | Json[]
  | { [key: string]: Json };

// 使用时不立即展开整个结构
type MyData = Json;

映射类型与延迟求值

映射类型结合延迟求值可以实现强大的类型变换功能:

type PartialNullable<T> = {
  [K in keyof T]?: T[K] | null;
};

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

// 使用时才计算映射
type PartialUser = PartialNullable<User>;  // { name?: string | null; age?: number | null }

分布式条件类型

当条件类型作用于联合类型时,TypeScript会分布式地应用条件,这也是延迟求值的一种表现:

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

// 分布式应用
type StringOrNumberArray = ToArray<string | number>;  // string[] | number[]

递归类型中的延迟求值

递归类型定义依赖延迟求值来避免立即展开导致的无限循环:

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

interface Nested {
  a: {
    b: {
      c: number;
    };
  };
}

// 递归应用但不会立即展开
type ReadonlyNested = DeepReadonly<Nested>;

类型推断与延迟

TypeScript的类型推断系统也利用延迟求值来优化性能:

type Unpacked<T> =
  T extends (infer U)[] ? U :
  T extends (...args: any[]) => infer U ? U :
  T extends Promise<infer U> ? U :
  T;

// 保持惰性直到使用
type UnpackedArray = Unpacked<string[]>;  // string
type UnpackedPromise = Unpacked<Promise<number>>;  // number

实用工具类型实现

许多内置工具类型都利用了延迟求值特性:

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

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

function foo(a: string, b: number): boolean {
  return true;
}

// 延迟推断参数和返回类型
type FooParams = MyParameters<typeof foo>;  // [string, number]
type FooReturn = MyReturnType<typeof foo>;  // boolean

类型运算的性能考虑

延迟求值机制使得TypeScript可以优化复杂类型的性能:

type ComplexType<T> = 
  T extends string ? "string" :
  T extends number ? "number" :
  T extends boolean ? "boolean" :
  "other";

// 不会预先计算所有分支
type Result = ComplexType<unknown>;  // 保持为条件类型

// 只有明确类型时才计算
type StringResult = ComplexType<"hello">;  // "string"

类型实例化的边界情况

在某些边界情况下,类型实例化会表现出特殊行为:

type Foo<T> = { x: T };
type Bar<U> = Foo<U>;  // 别名不会立即实例化

// 实际使用时才实例化
type StringBar = Bar<string>;  // { x: string }

与函数泛型的交互

函数泛型参数在调用时才会实例化,这也是延迟求值的一种形式:

function identity<T>(x: T): T {
  return x;
}

// 调用时才实例化类型
const result = identity("hello");  // T实例化为string

类型参数的约束检查

类型参数的约束检查也采用延迟策略:

interface Lengthwise {
  length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
  console.log(arg.length);
  return arg;
}

// 调用时才检查约束
loggingIdentity("text");  // OK,string有length属性

高级类型模式

利用延迟求值可以实现高级类型模式:

type Chainable<T = {}> = {
  option<K extends string, V>(key: K, value: V): Chainable<T & { [P in K]: V }>;
  get(): T;
};

// 逐步构建类型
const config = {} as Chainable;
const result = config
  .option("name", "TypeScript")
  .option("version", 4.9)
  .get();  // { name: string; version: number }

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

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

上一篇:递归类型定义

下一篇:类型参数约束

前端川

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