类型实例化与延迟求值
类型实例化与延迟求值
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